diff options
245 files changed, 5288 insertions, 1551 deletions
diff --git a/Android.bp b/Android.bp index 808879f8839a..27f22bde9a3a 100644 --- a/Android.bp +++ b/Android.bp @@ -603,6 +603,7 @@ filegroup { "core/java/com/android/internal/util/StateMachine.java", "core/java/com/android/internal/util/TrafficStatsConstants.java", "core/java/com/android/internal/util/WakeupMessage.java", + "core/java/com/android/internal/util/TokenBucket.java", "core/java/android/net/shared/*.java", ], } @@ -612,13 +613,13 @@ filegroup { name: "framework-tethering-shared-srcs", srcs: [ "core/java/android/util/LocalLog.java", - "core/java/com/android/internal/util/BitUtils.java", "core/java/com/android/internal/util/IndentingPrintWriter.java", "core/java/com/android/internal/util/IState.java", "core/java/com/android/internal/util/MessageUtils.java", "core/java/com/android/internal/util/Preconditions.java", "core/java/com/android/internal/util/State.java", "core/java/com/android/internal/util/StateMachine.java", + "core/java/android/net/shared/Inet4AddressUtils.java", ], } diff --git a/StubLibraries.bp b/StubLibraries.bp index 78f1b9ca26e5..d1950474da5a 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -217,21 +217,6 @@ java_library_static { defaults: ["framework-stubs-default"], } -java_system_modules { - name: "android_stubs_current_system_modules", - libs: ["android_stubs_current"], -} - -java_system_modules { - name: "android_system_stubs_current_system_modules", - libs: ["android_system_stubs_current"], -} - -java_system_modules { - name: "android_test_stubs_current_system_modules", - libs: ["android_test_stubs_current"], -} - ///////////////////////////////////////////////////////////////////// // hwbinder.stubs provides APIs required for building HIDL Java // libraries. diff --git a/api/current.txt b/api/current.txt index 6d5d228d295e..6dc6c7d42a98 100644 --- a/api/current.txt +++ b/api/current.txt @@ -28327,7 +28327,9 @@ package android.media.tv { method public int getVideoHeight(); method public float getVideoPixelAspectRatio(); method public int getVideoWidth(); + method public boolean isAudioDescription(); method public boolean isEncrypted(); + method public boolean isHardOfHearing(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.TvTrackInfo> CREATOR; field public static final int TYPE_AUDIO = 0; // 0x0 @@ -28339,10 +28341,12 @@ package android.media.tv { ctor public TvTrackInfo.Builder(int, @NonNull String); method public android.media.tv.TvTrackInfo build(); method public android.media.tv.TvTrackInfo.Builder setAudioChannelCount(int); + method @NonNull public android.media.tv.TvTrackInfo.Builder setAudioDescription(boolean); method public android.media.tv.TvTrackInfo.Builder setAudioSampleRate(int); method public android.media.tv.TvTrackInfo.Builder setDescription(CharSequence); method @NonNull public android.media.tv.TvTrackInfo.Builder setEncrypted(boolean); method public android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle); + method @NonNull public android.media.tv.TvTrackInfo.Builder setHardOfHearing(boolean); method public android.media.tv.TvTrackInfo.Builder setLanguage(String); method public android.media.tv.TvTrackInfo.Builder setVideoActiveFormatDescription(byte); method public android.media.tv.TvTrackInfo.Builder setVideoFrameRate(float); @@ -28655,6 +28659,37 @@ package android.net { field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortal> CREATOR; } + public class ConnectivityDiagnosticsManager { + method public void registerConnectivityDiagnosticsCallback(@NonNull android.net.NetworkRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback); + method public void unregisterConnectivityDiagnosticsCallback(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback); + field public static final int DETECTION_METHOD_DNS_EVENTS = 1; // 0x1 + field public static final int DETECTION_METHOD_TCP_METRICS = 2; // 0x2 + } + + public abstract static class ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback { + ctor public ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback(); + method public void onConnectivityReport(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityReport); + method public void onDataStallSuspected(@NonNull android.net.ConnectivityDiagnosticsManager.DataStallReport); + method public void onNetworkConnectivityReported(@NonNull android.net.Network, boolean); + } + + public static class ConnectivityDiagnosticsManager.ConnectivityReport { + ctor public ConnectivityDiagnosticsManager.ConnectivityReport(@NonNull android.net.Network, long, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle); + field @NonNull public final android.os.PersistableBundle additionalInfo; + field @NonNull public final android.net.LinkProperties linkProperties; + field @NonNull public final android.net.Network network; + field @NonNull public final android.net.NetworkCapabilities networkCapabilities; + field public final long reportTimestamp; + } + + public static class ConnectivityDiagnosticsManager.DataStallReport { + ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.os.PersistableBundle); + field public final int detectionMethod; + field @NonNull public final android.net.Network network; + field public final long reportTimestamp; + field @NonNull public final android.os.PersistableBundle stallDetails; + } + public class ConnectivityManager { method public void addDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener); method public boolean bindProcessToNetwork(@Nullable android.net.Network); @@ -28888,6 +28923,7 @@ package android.net { method public boolean addRoute(@NonNull android.net.RouteInfo); method public void clear(); method public int describeContents(); + method @Nullable public java.net.Inet4Address getDhcpServerAddress(); method @NonNull public java.util.List<java.net.InetAddress> getDnsServers(); method @Nullable public String getDomains(); method @Nullable public android.net.ProxyInfo getHttpProxy(); @@ -28899,6 +28935,7 @@ package android.net { method @NonNull public java.util.List<android.net.RouteInfo> getRoutes(); method public boolean isPrivateDnsActive(); method public boolean isWakeOnLanSupported(); + method public void setDhcpServerAddress(@Nullable java.net.Inet4Address); method public void setDnsServers(@NonNull java.util.Collection<java.net.InetAddress>); method public void setDomains(@Nullable String); method public void setHttpProxy(@Nullable android.net.ProxyInfo); @@ -35218,7 +35255,9 @@ package android.os { method public int describeContents(); method @Nullable public android.os.PersistableBundle getPersistableBundle(@Nullable String); method public void putPersistableBundle(@Nullable String, @Nullable android.os.PersistableBundle); + method @NonNull public static android.os.PersistableBundle readFromStream(@NonNull java.io.InputStream) throws java.io.IOException; method public void writeToParcel(android.os.Parcel, int); + method public void writeToStream(@NonNull java.io.OutputStream) throws java.io.IOException; field @NonNull public static final android.os.Parcelable.Creator<android.os.PersistableBundle> CREATOR; field public static final android.os.PersistableBundle EMPTY; } @@ -42575,6 +42614,7 @@ package android.system { method public static void execve(String, String[], String[]) throws android.system.ErrnoException; method public static void fchmod(java.io.FileDescriptor, int) throws android.system.ErrnoException; method public static void fchown(java.io.FileDescriptor, int, int) throws android.system.ErrnoException; + method public static int fcntlInt(@NonNull java.io.FileDescriptor, int, int) throws android.system.ErrnoException; method public static void fdatasync(java.io.FileDescriptor) throws android.system.ErrnoException; method public static android.system.StructStat fstat(java.io.FileDescriptor) throws android.system.ErrnoException; method public static android.system.StructStatVfs fstatvfs(java.io.FileDescriptor) throws android.system.ErrnoException; @@ -45087,6 +45127,7 @@ package android.telephony { method public int getLevel(); method @Deprecated public boolean isGsm(); method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SignalStrength> CREATOR; field public static final int INVALID = 2147483647; // 0x7fffffff } @@ -45095,7 +45136,7 @@ package android.telephony { method @Nullable public String createAppSpecificSmsTokenWithPackageInfo(@Nullable String, @NonNull android.app.PendingIntent); method public java.util.ArrayList<java.lang.String> divideMessage(String); method public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent); - method public android.os.Bundle getCarrierConfigValues(); + method @Nullable public android.os.Bundle getCarrierConfigValues(); method public static android.telephony.SmsManager getDefault(); method public static int getDefaultSmsSubscriptionId(); method public static android.telephony.SmsManager getSmsManagerForSubscriptionId(int); diff --git a/api/system-current.txt b/api/system-current.txt index 0edf9ab40821..75bdf0ce633f 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -596,6 +596,7 @@ package android.app { public class StatusBarManager { method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo(); method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean); + method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSimNetworkLock(boolean); } public static final class StatusBarManager.DisableInfo { @@ -1162,6 +1163,7 @@ package android.app.role { method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean addRoleHolderFromController(@NonNull String, @NonNull String); method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); + method @Nullable public String getDefaultSmsPackage(int); method @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHolders(@NonNull String); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle); @@ -1411,6 +1413,14 @@ package android.bluetooth { method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); } + public final class BluetoothHidHost implements android.bluetooth.BluetoothProfile { + method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice); + method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice, int); + field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED"; + } + public final class BluetoothPan implements android.bluetooth.BluetoothProfile { method protected void finalize(); method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); @@ -1428,6 +1438,7 @@ package android.bluetooth { public class BluetoothPbap implements android.bluetooth.BluetoothProfile { method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; } @@ -1543,6 +1554,7 @@ package android.content { field public static final String EUICC_CARD_SERVICE = "euicc_card"; field public static final String HDMI_CONTROL_SERVICE = "hdmi_control"; field public static final String NETD_SERVICE = "netd"; + field public static final String NETWORK_POLICY_SERVICE = "netpolicy"; field public static final String NETWORK_SCORE_SERVICE = "network_score"; field public static final String OEM_LOCK_SERVICE = "oem_lock"; field public static final String PERMISSION_SERVICE = "permission"; @@ -3972,6 +3984,15 @@ package android.media.soundtrigger { package android.media.tv { + public final class DvbDeviceInfo implements android.os.Parcelable { + ctor public DvbDeviceInfo(int, int); + method public int describeContents(); + method public int getAdapterId(); + method public int getDeviceId(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.DvbDeviceInfo> CREATOR; + } + public final class TvContentRatingSystemInfo implements android.os.Parcelable { method public static android.media.tv.TvContentRatingSystemInfo createTvContentRatingSystemInfo(int, android.content.pm.ApplicationInfo); method public int describeContents(); @@ -4086,12 +4107,14 @@ package android.media.tv { method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void addBlockedRating(@NonNull android.media.tv.TvContentRating); method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean captureFrame(String, android.view.Surface, android.media.tv.TvStreamConfig); method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(String); + method @NonNull @RequiresPermission("android.permission.DVB_DEVICE") public java.util.List<android.media.tv.DvbDeviceInfo> getDvbDeviceList(); method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public java.util.List<android.media.tv.TvInputHardwareInfo> getHardwareList(); method @RequiresPermission(android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS) public java.util.List<android.media.tv.TvContentRatingSystemInfo> getTvContentRatingSystemList(); method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean isSingleSessionActive(); method @RequiresPermission(android.Manifest.permission.NOTIFY_TV_INPUTS) public void notifyPreviewProgramAddedToWatchNext(String, long, long); method @RequiresPermission(android.Manifest.permission.NOTIFY_TV_INPUTS) public void notifyPreviewProgramBrowsableDisabled(String, long); method @RequiresPermission(android.Manifest.permission.NOTIFY_TV_INPUTS) public void notifyWatchNextProgramBrowsableDisabled(String, long); + method @Nullable @RequiresPermission("android.permission.DVB_DEVICE") public android.os.ParcelFileDescriptor openDvbDevice(@NonNull android.media.tv.DvbDeviceInfo, int); method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public void releaseTvInputHardware(int, android.media.tv.TvInputManager.Hardware); method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void removeBlockedRating(@NonNull android.media.tv.TvContentRating); method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void setParentalControlsEnabled(boolean); @@ -6099,11 +6122,13 @@ package android.os { public class UpdateEngine { ctor public UpdateEngine(); + method @NonNull public android.os.UpdateEngine.AllocateSpaceResult allocateSpace(@NonNull String, @NonNull String[]); method public void applyPayload(String, long, long, String[]); method public void applyPayload(@NonNull android.os.ParcelFileDescriptor, long, long, @NonNull String[]); method public boolean bind(android.os.UpdateEngineCallback, android.os.Handler); method public boolean bind(android.os.UpdateEngineCallback); method public void cancel(); + method public int cleanupAppliedPayload(); method public void resetStatus(); method public void resume(); method public void suspend(); @@ -6111,14 +6136,21 @@ package android.os { method public boolean verifyPayloadMetadata(String); } + public static final class UpdateEngine.AllocateSpaceResult { + method public int errorCode(); + method public long freeSpaceRequired(); + } + public static final class UpdateEngine.ErrorCodeConstants { ctor public UpdateEngine.ErrorCodeConstants(); + field public static final int DEVICE_CORRUPTED = 61; // 0x3d field public static final int DOWNLOAD_PAYLOAD_VERIFICATION_ERROR = 12; // 0xc field public static final int DOWNLOAD_TRANSFER_ERROR = 9; // 0x9 field public static final int ERROR = 1; // 0x1 field public static final int FILESYSTEM_COPIER_ERROR = 4; // 0x4 field public static final int INSTALL_DEVICE_OPEN_ERROR = 7; // 0x7 field public static final int KERNEL_DEVICE_OPEN_ERROR = 8; // 0x8 + field public static final int NOT_ENOUGH_SPACE = 60; // 0x3c field public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10; // 0xa field public static final int PAYLOAD_MISMATCHED_TYPE_ERROR = 6; // 0x6 field public static final int PAYLOAD_SIZE_MISMATCH_ERROR = 11; // 0xb @@ -6735,7 +6767,9 @@ package android.provider { public static final class Telephony.Carriers implements android.provider.BaseColumns { field public static final String APN_SET_ID = "apn_set_id"; field public static final int CARRIER_EDITED = 4; // 0x4 + field @NonNull public static final android.net.Uri DPC_URI; field public static final String EDITED_STATUS = "edited"; + field public static final int INVALID_APN_ID = -1; // 0xffffffff field public static final String MAX_CONNECTIONS = "max_conns"; field public static final String MODEM_PERSIST = "modem_cognitive"; field public static final String MTU = "mtu"; @@ -8727,6 +8761,8 @@ package android.telephony { } public class ServiceState implements android.os.Parcelable { + method @NonNull public android.telephony.ServiceState createLocationInfoSanitizedCopy(boolean); + method public int getDataRegistrationState(); method @Nullable public android.telephony.NetworkRegistrationInfo getNetworkRegistrationInfo(int, int); method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoList(); method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForDomain(int); @@ -8879,6 +8915,8 @@ package android.telephony { public class SubscriptionManager { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean canDisablePhysicalSubscription(); + method public boolean canManageSubscription(@Nullable android.telephony.SubscriptionInfo, @Nullable String); + method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String); method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int); method @NonNull public static android.content.res.Resources getResourcesForSubId(@NonNull android.content.Context, int); @@ -8890,6 +8928,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultVoiceSubscriptionId(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPreferredDataSubscriptionId(int, boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setSubscriptionEnabled(int, boolean); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUiccApplicationsEnabled(boolean, int); field @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS) public static final String ACTION_SUBSCRIPTION_PLANS_CHANGED = "android.telephony.action.SUBSCRIPTION_PLANS_CHANGED"; field @NonNull public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI; field public static final int PROFILE_CLASS_DEFAULT = -1; // 0xffffffff @@ -8931,6 +8970,7 @@ package android.telephony { } public class TelephonyManager { + method public int addDevicePolicyOverrideApn(@NonNull android.content.Context, @NonNull android.telephony.data.ApnSetting); method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void call(String, String); method public int checkCarrierPrivilegesForPackage(String); method public int checkCarrierPrivilegesForPackageAnyPhone(String); @@ -8957,6 +8997,7 @@ package android.telephony { method @Deprecated public boolean getDataEnabled(); method @Deprecated public boolean getDataEnabled(int); method @Nullable public static android.content.ComponentName getDefaultRespondViaMessageApplication(@NonNull android.content.Context, boolean); + method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceSoftwareVersion(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode(); method public int getEmergencyNumberDbVersion(); @@ -8991,15 +9032,17 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isInEmergencySmsMode(); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isManualNetworkSelectionAllowed(); method public boolean isModemEnabledForSlot(int); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRadioOn(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging(); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isTetheringApnRequired(); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isTetheringApnRequired(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isVideoCallingEnabled(); method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle); + method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting); method public boolean needsOtaServiceProvisioning(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio(); diff --git a/api/test-current.txt b/api/test-current.txt index 22ac3abcefd9..8de2bec0fc1c 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -352,6 +352,7 @@ package android.app { public class StatusBarManager { method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo(); method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean); + method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSimNetworkLock(boolean); } public static final class StatusBarManager.DisableInfo { @@ -3040,14 +3041,17 @@ package android.telephony { } public class TelephonyManager { + method public int addDevicePolicyOverrideApn(@NonNull android.content.Context, @NonNull android.telephony.data.ApnSetting); method public int checkCarrierPrivilegesForPackage(String); method public int getCarrierIdListVersion(); method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent); method @Nullable public static android.content.ComponentName getDefaultRespondViaMessageApplication(@NonNull android.content.Context, boolean); + method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context); method public int getEmergencyNumberDbVersion(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag(); method @NonNull @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getNetworkCountryIso(int); method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion(); + method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile(); method @Deprecated public void setCarrierTestOverride(String, String, String, String, String, String, String); method public void setCarrierTestOverride(String, String, String, String, String, String, String, String, String); diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 28413be29a1d..dc24467406cf 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -153,6 +153,11 @@ public class StatusBarManager { */ public static final int DEFAULT_SETUP_DISABLE2_FLAGS = DISABLE2_ROTATE_SUGGESTIONS; + /** + * disable flags to be applied when the device is sim-locked. + */ + private static final int DEFAULT_SIM_LOCKED_DISABLED_FLAGS = DISABLE_EXPAND; + /** @hide */ public static final int NAVIGATION_HINT_BACK_ALT = 1 << 0; /** @hide */ @@ -385,6 +390,30 @@ public class StatusBarManager { } /** + * Enable or disable expansion of the status bar. When the device is SIM-locked, the status + * bar should not be expandable. + * + * @param disabled If {@code true}, the status bar will be set to non-expandable. If + * {@code false}, re-enables expansion of the status bar. + * @hide + */ + @SystemApi + @TestApi + @RequiresPermission(android.Manifest.permission.STATUS_BAR) + public void setDisabledForSimNetworkLock(boolean disabled) { + try { + final int userId = Binder.getCallingUserHandle().getIdentifier(); + final IStatusBarService svc = getService(); + if (svc != null) { + svc.disableForUser(disabled ? DEFAULT_SIM_LOCKED_DISABLED_FLAGS : DISABLE_NONE, + mToken, mContext.getPackageName(), userId); + } + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** * Get this app's currently requested disabled components * * @return a new DisableInfo diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java index bb04a2e52712..b9cda6c7c183 100644 --- a/core/java/android/app/role/RoleManager.java +++ b/core/java/android/app/role/RoleManager.java @@ -629,9 +629,12 @@ public final class RoleManager { * {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as required by * {@link android.provider.Telephony.Sms#getDefaultSmsPackage(Context)} * + * @param userId The user ID to get the default SMS package for. + * @return the package name of the default SMS app, or {@code null} if not configured. * @hide */ @Nullable + @SystemApi public String getDefaultSmsPackage(@UserIdInt int userId) { try { return mService.getDefaultSmsPackage(userId); diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl index 9877fc741b7b..de8f4700de2d 100644 --- a/core/java/android/app/timedetector/ITimeDetectorService.aidl +++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl @@ -17,6 +17,7 @@ package android.app.timedetector; import android.app.timedetector.ManualTimeSuggestion; +import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.PhoneTimeSuggestion; /** @@ -35,4 +36,5 @@ import android.app.timedetector.PhoneTimeSuggestion; interface ITimeDetectorService { void suggestPhoneTime(in PhoneTimeSuggestion timeSuggestion); void suggestManualTime(in ManualTimeSuggestion timeSuggestion); + void suggestNetworkTime(in NetworkTimeSuggestion timeSuggestion); } diff --git a/core/java/android/app/timedetector/NetworkTimeSuggestion.aidl b/core/java/android/app/timedetector/NetworkTimeSuggestion.aidl new file mode 100644 index 000000000000..731c907f6837 --- /dev/null +++ b/core/java/android/app/timedetector/NetworkTimeSuggestion.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.timedetector; + +parcelable NetworkTimeSuggestion; diff --git a/core/java/android/app/timedetector/NetworkTimeSuggestion.java b/core/java/android/app/timedetector/NetworkTimeSuggestion.java new file mode 100644 index 000000000000..4c55ba12d881 --- /dev/null +++ b/core/java/android/app/timedetector/NetworkTimeSuggestion.java @@ -0,0 +1,129 @@ +/* + * 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.app.timedetector; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.TimestampedValue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * A time signal from a network time source like NTP. The value consists of the number of + * milliseconds elapsed since 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime + * clock when that number was established. The elapsed realtime clock is considered accurate but + * volatile, so time signals must not be persisted across device resets. + * + * @hide + */ +public final class NetworkTimeSuggestion implements Parcelable { + + public static final @NonNull Creator<NetworkTimeSuggestion> CREATOR = + new Creator<NetworkTimeSuggestion>() { + public NetworkTimeSuggestion createFromParcel(Parcel in) { + return NetworkTimeSuggestion.createFromParcel(in); + } + + public NetworkTimeSuggestion[] newArray(int size) { + return new NetworkTimeSuggestion[size]; + } + }; + + @NonNull + private final TimestampedValue<Long> mUtcTime; + @Nullable + private ArrayList<String> mDebugInfo; + + public NetworkTimeSuggestion(@NonNull TimestampedValue<Long> utcTime) { + mUtcTime = Objects.requireNonNull(utcTime); + Objects.requireNonNull(utcTime.getValue()); + } + + private static NetworkTimeSuggestion createFromParcel(Parcel in) { + TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */); + NetworkTimeSuggestion suggestion = new NetworkTimeSuggestion(utcTime); + @SuppressWarnings("unchecked") + ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */); + suggestion.mDebugInfo = debugInfo; + return suggestion; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeParcelable(mUtcTime, 0); + dest.writeList(mDebugInfo); + } + + @NonNull + public TimestampedValue<Long> getUtcTime() { + return mUtcTime; + } + + @NonNull + public List<String> getDebugInfo() { + return mDebugInfo == null + ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo); + } + + /** + * Associates information with the instance that can be useful for debugging / logging. The + * information is present in {@link #toString()} but is not considered for + * {@link #equals(Object)} and {@link #hashCode()}. + */ + public void addDebugInfo(String... debugInfos) { + if (mDebugInfo == null) { + mDebugInfo = new ArrayList<>(); + } + mDebugInfo.addAll(Arrays.asList(debugInfos)); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + NetworkTimeSuggestion that = (NetworkTimeSuggestion) o; + return Objects.equals(mUtcTime, that.mUtcTime); + } + + @Override + public int hashCode() { + return Objects.hash(mUtcTime); + } + + @Override + public String toString() { + return "NetworkTimeSuggestion{" + + "mUtcTime=" + mUtcTime + + ", mDebugInfo=" + mDebugInfo + + '}'; + } +} diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java index 48d5cd2d65a5..af9ece00f491 100644 --- a/core/java/android/app/timedetector/TimeDetector.java +++ b/core/java/android/app/timedetector/TimeDetector.java @@ -48,7 +48,7 @@ public class TimeDetector { * signal if better signals are available such as those that come from more reliable sources or * were determined more recently. */ - @RequiresPermission(android.Manifest.permission.SET_TIME) + @RequiresPermission(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE) public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) { if (DEBUG) { Log.d(TAG, "suggestPhoneTime called: " + timeSuggestion); @@ -63,7 +63,7 @@ public class TimeDetector { /** * Suggests the user's manually entered current time to the detector. */ - @RequiresPermission(android.Manifest.permission.SET_TIME) + @RequiresPermission(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE) public void suggestManualTime(@NonNull ManualTimeSuggestion timeSuggestion) { if (DEBUG) { Log.d(TAG, "suggestManualTime called: " + timeSuggestion); @@ -85,4 +85,19 @@ public class TimeDetector { manualTimeSuggestion.addDebugInfo(why); return manualTimeSuggestion; } + + /** + * Suggests the time according to a network time source like NTP. + */ + @RequiresPermission(android.Manifest.permission.SET_TIME) + public void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion) { + if (DEBUG) { + Log.d(TAG, "suggestNetworkTime called: " + timeSuggestion); + } + try { + mITimeDetectorService.suggestNetworkTime(timeSuggestion); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java index 387a36bba608..e165d8a76caa 100644 --- a/core/java/android/app/timezonedetector/TimeZoneDetector.java +++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java @@ -47,7 +47,7 @@ public class TimeZoneDetector { * detector may ignore the signal based on system settings, whether better information is * available, and so on. */ - @RequiresPermission(android.Manifest.permission.SET_TIME_ZONE) + @RequiresPermission(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE) public void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion timeZoneSuggestion) { if (DEBUG) { Log.d(TAG, "suggestPhoneTimeZone called: " + timeZoneSuggestion); @@ -63,7 +63,7 @@ public class TimeZoneDetector { * Suggests the current time zone, determined for the user's manually information, to the * detector. The detector may ignore the signal based on system settings. */ - @RequiresPermission(android.Manifest.permission.SET_TIME_ZONE) + @RequiresPermission(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE) public void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion) { if (DEBUG) { Log.d(TAG, "suggestManualTimeZone called: " + timeZoneSuggestion); diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java index 8f5cdf01f572..c1233b800a51 100644 --- a/core/java/android/bluetooth/BluetoothHidHost.java +++ b/core/java/android/bluetooth/BluetoothHidHost.java @@ -17,9 +17,12 @@ package android.bluetooth; import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.content.Context; import android.os.Binder; @@ -43,6 +46,7 @@ import java.util.List; * * @hide */ +@SystemApi public final class BluetoothHidHost implements BluetoothProfile { private static final String TAG = "BluetoothHidHost"; private static final boolean DBG = true; @@ -66,6 +70,7 @@ public final class BluetoothHidHost implements BluetoothProfile { * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to * receive. */ + @SuppressLint("ActionValue") @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED"; @@ -325,7 +330,7 @@ public final class BluetoothHidHost implements BluetoothProfile { * {@inheritDoc} */ @Override - public List<BluetoothDevice> getConnectedDevices() { + public @NonNull List<BluetoothDevice> getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); final IBluetoothHidHost service = getService(); if (service != null && isEnabled()) { @@ -342,6 +347,8 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * {@inheritDoc} + * + * @hide */ @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { @@ -363,7 +370,7 @@ public final class BluetoothHidHost implements BluetoothProfile { * {@inheritDoc} */ @Override - public int getConnectionState(BluetoothDevice device) { + public int getConnectionState(@Nullable BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -409,7 +416,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { + public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -457,7 +464,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getConnectionPolicy(BluetoothDevice device) { + public int getConnectionPolicy(@Nullable BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java index df0289643ee0..291015070385 100644 --- a/core/java/android/bluetooth/BluetoothPbap.java +++ b/core/java/android/bluetooth/BluetoothPbap.java @@ -16,7 +16,10 @@ package android.bluetooth; +import android.Manifest; +import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SuppressLint; import android.annotation.SystemApi; @@ -271,6 +274,42 @@ public class BluetoothPbap implements BluetoothProfile { } /** + * Pbap does not store connection policy, so this function only disconnects Pbap if + * connectionPolicy is CONNECTION_POLICY_FORBIDDEN. + * + * <p> The device should already be paired. + * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, + * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} + * + * @param device Paired bluetooth device + * @param connectionPolicy is the connection policy to set to for this profile + * @return true if pbap is successfully disconnected, false otherwise + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public boolean setConnectionPolicy(@NonNull BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { + if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); + try { + final IBluetoothPbap service = mService; + if (service != null && isEnabled() + && isValidDevice(device)) { + if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { + return false; + } + return service.setConnectionPolicy(device, connectionPolicy); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + /** * Disconnects the current Pbap client (PCE). Currently this call blocks, * it may soon be made asynchronous. Returns false if this proxy object is * not currently connected to the Pbap service. diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 902c6b9dd36d..f3596411fb6b 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3918,6 +3918,7 @@ public abstract class Context { */ public static final String NETWORK_STATS_SERVICE = "netstats"; /** {@hide} */ + @SystemApi public static final String NETWORK_POLICY_SERVICE = "netpolicy"; /** {@hide} */ public static final String NETWORK_WATCHLIST_SERVICE = "network_watchlist"; diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java index cc5d3b16ff08..3e10a6c0b849 100644 --- a/core/java/android/database/AbstractCursor.java +++ b/core/java/android/database/AbstractCursor.java @@ -17,7 +17,7 @@ package android.database; import android.annotation.NonNull; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentResolver; import android.net.Uri; import android.os.Build; diff --git a/core/java/android/database/AbstractWindowedCursor.java b/core/java/android/database/AbstractWindowedCursor.java index a988f0684fca..daf7d2b1e908 100644 --- a/core/java/android/database/AbstractWindowedCursor.java +++ b/core/java/android/database/AbstractWindowedCursor.java @@ -16,7 +16,7 @@ package android.database; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** * A base class for Cursors that store their data in {@link CursorWindow}s. diff --git a/core/java/android/database/BulkCursorNative.java b/core/java/android/database/BulkCursorNative.java index 77a13cf80073..8ea450c6ca44 100644 --- a/core/java/android/database/BulkCursorNative.java +++ b/core/java/android/database/BulkCursorNative.java @@ -16,7 +16,7 @@ package android.database; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; diff --git a/core/java/android/database/ContentObserver.java b/core/java/android/database/ContentObserver.java index 798b7830ac50..69ca581e1559 100644 --- a/core/java/android/database/ContentObserver.java +++ b/core/java/android/database/ContentObserver.java @@ -16,7 +16,7 @@ package android.database; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.net.Uri; import android.os.Handler; import android.os.UserHandle; diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java index 6873577553a7..063a2d00a306 100644 --- a/core/java/android/database/CursorWindow.java +++ b/core/java/android/database/CursorWindow.java @@ -17,7 +17,7 @@ package android.database; import android.annotation.BytesLong; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.res.Resources; import android.database.sqlite.SQLiteClosable; import android.database.sqlite.SQLiteException; diff --git a/core/java/android/database/CursorWrapper.java b/core/java/android/database/CursorWrapper.java index c9cafafe4041..4496f805cc2e 100644 --- a/core/java/android/database/CursorWrapper.java +++ b/core/java/android/database/CursorWrapper.java @@ -16,7 +16,7 @@ package android.database; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentResolver; import android.net.Uri; import android.os.Bundle; diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java index 992da312201d..4246b84dc52f 100644 --- a/core/java/android/database/DatabaseUtils.java +++ b/core/java/android/database/DatabaseUtils.java @@ -17,7 +17,7 @@ package android.database; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentValues; import android.content.Context; import android.content.OperationApplicationException; diff --git a/core/java/android/database/MatrixCursor.java b/core/java/android/database/MatrixCursor.java index b0d399c64ea9..050a49ac959e 100644 --- a/core/java/android/database/MatrixCursor.java +++ b/core/java/android/database/MatrixCursor.java @@ -16,7 +16,7 @@ package android.database; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import java.util.ArrayList; diff --git a/core/java/android/database/sqlite/DatabaseObjectNotClosedException.java b/core/java/android/database/sqlite/DatabaseObjectNotClosedException.java index 2af06e116da0..ba546f34c9ea 100644 --- a/core/java/android/database/sqlite/DatabaseObjectNotClosedException.java +++ b/core/java/android/database/sqlite/DatabaseObjectNotClosedException.java @@ -16,7 +16,7 @@ package android.database.sqlite; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** * An exception that indicates that garbage-collector is finalizing a database object diff --git a/core/java/android/database/sqlite/SQLiteClosable.java b/core/java/android/database/sqlite/SQLiteClosable.java index d6a71da105dd..2fca729f2551 100644 --- a/core/java/android/database/sqlite/SQLiteClosable.java +++ b/core/java/android/database/sqlite/SQLiteClosable.java @@ -16,7 +16,8 @@ package android.database.sqlite; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; + import java.io.Closeable; /** diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java index e3c409891c54..4559e918acc5 100644 --- a/core/java/android/database/sqlite/SQLiteCursor.java +++ b/core/java/android/database/sqlite/SQLiteCursor.java @@ -16,7 +16,7 @@ package android.database.sqlite; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.database.AbstractWindowedCursor; import android.database.CursorWindow; import android.database.DatabaseUtils; diff --git a/core/java/android/database/sqlite/SQLiteCustomFunction.java b/core/java/android/database/sqlite/SQLiteCustomFunction.java index 41b78d3a7745..1ace40d7e913 100644 --- a/core/java/android/database/sqlite/SQLiteCustomFunction.java +++ b/core/java/android/database/sqlite/SQLiteCustomFunction.java @@ -16,7 +16,7 @@ package android.database.sqlite; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; /** diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index caf3e9337b92..7a0e183c03fe 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -20,9 +20,9 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; import android.app.ActivityManager; import android.app.ActivityThread; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentResolver; import android.content.ContentValues; import android.database.Cursor; diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java index fcdaf0abafcb..6a52b72a9e1c 100644 --- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java +++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java @@ -16,7 +16,7 @@ package android.database.sqlite; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import java.util.ArrayList; import java.util.Locale; diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java index 3d0ac611b2df..165f863ccde7 100644 --- a/core/java/android/database/sqlite/SQLiteDebug.java +++ b/core/java/android/database/sqlite/SQLiteDebug.java @@ -17,14 +17,13 @@ package android.database.sqlite; import android.annotation.TestApi; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Process; import android.os.SystemProperties; import android.util.Log; import android.util.Printer; -import dalvik.annotation.compat.UnsupportedAppUsage; - import java.util.ArrayList; /** diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java index 62cec0e1aa92..943afc7e04c3 100644 --- a/core/java/android/database/sqlite/SQLiteOpenHelper.java +++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java @@ -19,7 +19,7 @@ package android.database.sqlite; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.database.DatabaseErrorHandler; import android.database.SQLException; diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java index 8304133cd41e..de1c54321985 100644 --- a/core/java/android/database/sqlite/SQLiteProgram.java +++ b/core/java/android/database/sqlite/SQLiteProgram.java @@ -16,7 +16,7 @@ package android.database.sqlite; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.database.DatabaseUtils; import android.os.CancellationSignal; diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java index 58901798b5f7..02c8a97a6902 100644 --- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java +++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java @@ -18,7 +18,7 @@ package android.database.sqlite; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentValues; import android.database.Cursor; import android.database.DatabaseUtils; diff --git a/core/java/android/database/sqlite/SQLiteSession.java b/core/java/android/database/sqlite/SQLiteSession.java index a9ac9e7f882f..24b62b89454d 100644 --- a/core/java/android/database/sqlite/SQLiteSession.java +++ b/core/java/android/database/sqlite/SQLiteSession.java @@ -16,7 +16,7 @@ package android.database.sqlite; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.database.CursorWindow; import android.database.DatabaseUtils; import android.os.CancellationSignal; diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java index 42e7ac7e8431..9fda8b011e52 100644 --- a/core/java/android/database/sqlite/SQLiteStatement.java +++ b/core/java/android/database/sqlite/SQLiteStatement.java @@ -16,7 +16,7 @@ package android.database.sqlite; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.ParcelFileDescriptor; /** diff --git a/core/java/android/database/sqlite/SqliteWrapper.java b/core/java/android/database/sqlite/SqliteWrapper.java index e3171640a25c..f335fbdd89e0 100644 --- a/core/java/android/database/sqlite/SqliteWrapper.java +++ b/core/java/android/database/sqlite/SqliteWrapper.java @@ -17,12 +17,11 @@ package android.database.sqlite; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; -import android.database.sqlite.SQLiteException; import android.net.Uri; import android.util.Log; import android.widget.Toast; diff --git a/core/java/android/ddm/DdmHandleAppName.java b/core/java/android/ddm/DdmHandleAppName.java index 956078772ca8..e6fb4ec25210 100644 --- a/core/java/android/ddm/DdmHandleAppName.java +++ b/core/java/android/ddm/DdmHandleAppName.java @@ -16,11 +16,13 @@ package android.ddm; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; +import android.util.Log; + import org.apache.harmony.dalvik.ddmc.Chunk; import org.apache.harmony.dalvik.ddmc.ChunkHandler; import org.apache.harmony.dalvik.ddmc.DdmServer; -import android.util.Log; + import java.nio.ByteBuffer; diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 33e51c9fa11c..c847f86f6b62 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -16,14 +16,20 @@ package android.hardware; -import static android.system.OsConstants.*; +import static android.system.OsConstants.EACCES; +import static android.system.OsConstants.EBUSY; +import static android.system.OsConstants.EINVAL; +import static android.system.OsConstants.ENODEV; +import static android.system.OsConstants.ENOSYS; +import static android.system.OsConstants.EOPNOTSUPP; +import static android.system.OsConstants.EUSERS; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.UnsupportedAppUsage; import android.app.ActivityThread; import android.app.AppOpsManager; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.graphics.ImageFormat; import android.graphics.Point; diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java index c569e05112a3..67797510870a 100644 --- a/core/java/android/hardware/HardwareBuffer.java +++ b/core/java/android/hardware/HardwareBuffer.java @@ -20,7 +20,7 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.LongDef; import android.annotation.NonNull; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.graphics.GraphicBuffer; import android.os.Build; import android.os.Parcel; diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index e78fb7f00797..a71a7b63f30e 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -18,7 +18,7 @@ package android.hardware; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; /** diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java index 8c910b254b9c..64c45bf290ec 100644 --- a/core/java/android/hardware/SensorEvent.java +++ b/core/java/android/hardware/SensorEvent.java @@ -16,7 +16,7 @@ package android.hardware; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** * This class represents a {@link android.hardware.Sensor Sensor} event and diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index 32504282d98f..6036d7938b95 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -18,7 +18,7 @@ package android.hardware; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Build; import android.os.Handler; diff --git a/core/java/android/hardware/SerialManager.java b/core/java/android/hardware/SerialManager.java index 571c3cc0451f..b51382e01ccd 100644 --- a/core/java/android/hardware/SerialManager.java +++ b/core/java/android/hardware/SerialManager.java @@ -17,7 +17,7 @@ package android.hardware; import android.annotation.SystemService; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.ParcelFileDescriptor; import android.os.RemoteException; diff --git a/core/java/android/hardware/SerialPort.java b/core/java/android/hardware/SerialPort.java index 78ac3c007793..0fcaa4989210 100644 --- a/core/java/android/hardware/SerialPort.java +++ b/core/java/android/hardware/SerialPort.java @@ -16,12 +16,11 @@ package android.hardware; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.ParcelFileDescriptor; import java.io.FileDescriptor; import java.io.IOException; - import java.nio.ByteBuffer; /** diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 7abfabf4a2ac..974913b290b1 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -16,7 +16,7 @@ package android.hardware; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java index 27c04b407315..2333251d0802 100644 --- a/core/java/android/hardware/biometrics/BiometricConstants.java +++ b/core/java/android/hardware/biometrics/BiometricConstants.java @@ -16,8 +16,8 @@ package android.hardware.biometrics; -import android.annotation.UnsupportedAppUsage; import android.app.KeyguardManager; +import android.compat.annotation.UnsupportedAppUsage; /** diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java index b025508fc70e..5c74456eb60a 100644 --- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java +++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java @@ -16,8 +16,8 @@ package android.hardware.biometrics; -import android.annotation.UnsupportedAppUsage; import android.app.KeyguardManager; +import android.compat.annotation.UnsupportedAppUsage; import android.hardware.fingerprint.FingerprintManager; /** diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 48588e6d40bc..c697d4ce0c4f 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -18,7 +18,7 @@ package android.hardware.camera2; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.impl.PublicKey; import android.hardware.camera2.impl.SyntheticKey; diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 6a2fc8a2ca34..d027de88a564 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -18,7 +18,7 @@ package android.hardware.camera2; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.impl.PublicKey; import android.hardware.camera2.impl.SyntheticKey; diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index ba451e585d27..af05b2980bd9 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -18,7 +18,7 @@ package android.hardware.camera2; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.impl.CaptureResultExtras; import android.hardware.camera2.impl.PublicKey; diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index e909c0075f38..706aa4ef1979 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -16,12 +16,11 @@ package android.hardware.camera2.impl; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.graphics.ImageFormat; import android.graphics.Point; import android.graphics.Rect; import android.hardware.camera2.CameraCharacteristics; -import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.marshal.MarshalQueryable; @@ -52,7 +51,6 @@ import android.hardware.camera2.params.Face; import android.hardware.camera2.params.HighSpeedVideoConfiguration; import android.hardware.camera2.params.LensShadingMap; import android.hardware.camera2.params.MandatoryStreamCombination; -import android.hardware.camera2.params.MandatoryStreamCombination.MandatoryStreamInformation; import android.hardware.camera2.params.OisSample; import android.hardware.camera2.params.RecommendedStreamConfiguration; import android.hardware.camera2.params.RecommendedStreamConfigurationMap; diff --git a/core/java/android/hardware/camera2/utils/HashCodeHelpers.java b/core/java/android/hardware/camera2/utils/HashCodeHelpers.java index 526f086f4baa..16f3f2ab2023 100644 --- a/core/java/android/hardware/camera2/utils/HashCodeHelpers.java +++ b/core/java/android/hardware/camera2/utils/HashCodeHelpers.java @@ -16,7 +16,7 @@ package android.hardware.camera2.utils; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** * Provide hashing functions using the Modified Bernstein hash diff --git a/core/java/android/hardware/camera2/utils/SurfaceUtils.java b/core/java/android/hardware/camera2/utils/SurfaceUtils.java index d3c4505c37f0..abe1372ebde4 100644 --- a/core/java/android/hardware/camera2/utils/SurfaceUtils.java +++ b/core/java/android/hardware/camera2/utils/SurfaceUtils.java @@ -16,7 +16,7 @@ package android.hardware.camera2.utils; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.graphics.ImageFormat; import android.hardware.camera2.legacy.LegacyCameraDevice; import android.hardware.camera2.legacy.LegacyExceptionUtils.BufferQueueAbandonedException; diff --git a/core/java/android/hardware/camera2/utils/TypeReference.java b/core/java/android/hardware/camera2/utils/TypeReference.java index d9ba31b4c6f4..435ed153e042 100644 --- a/core/java/android/hardware/camera2/utils/TypeReference.java +++ b/core/java/android/hardware/camera2/utils/TypeReference.java @@ -16,7 +16,10 @@ package android.hardware.camera2.utils; -import android.annotation.UnsupportedAppUsage; +import static com.android.internal.util.Preconditions.checkNotNull; + +import android.compat.annotation.UnsupportedAppUsage; + import java.lang.reflect.Array; import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; @@ -24,8 +27,6 @@ import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; -import static com.android.internal.util.Preconditions.*; - /** * Super type token; allows capturing generic types at runtime by forcing them to be reified. * diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 0b25dbd78611..799dff9632c8 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -23,8 +23,8 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; import android.app.KeyguardManager; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.graphics.Point; import android.media.projection.MediaProjection; diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index ea63776dc5b6..2a584951887f 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -18,7 +18,7 @@ package android.hardware.display; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.ParceledListSlice; import android.content.res.Resources; diff --git a/core/java/android/hardware/display/WifiDisplay.java b/core/java/android/hardware/display/WifiDisplay.java index 55e60519707a..5bbbbf9bae77 100644 --- a/core/java/android/hardware/display/WifiDisplay.java +++ b/core/java/android/hardware/display/WifiDisplay.java @@ -16,7 +16,7 @@ package android.hardware.display; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; diff --git a/core/java/android/hardware/display/WifiDisplayStatus.java b/core/java/android/hardware/display/WifiDisplayStatus.java index 1973e51b1bba..e2a825fd990a 100644 --- a/core/java/android/hardware/display/WifiDisplayStatus.java +++ b/core/java/android/hardware/display/WifiDisplayStatus.java @@ -16,7 +16,7 @@ package android.hardware.display; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index d0622c88b4ca..315af32580aa 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -26,8 +26,8 @@ import android.annotation.Nullable; import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.SystemService; -import android.annotation.UnsupportedAppUsage; import android.app.ActivityManager; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.PackageManager; import android.hardware.biometrics.BiometricAuthenticator; diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index 0c0f248e3222..8d32db013fb3 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -20,7 +20,7 @@ import android.annotation.IntDef; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemService; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.media.AudioAttributes; import android.os.Binder; diff --git a/core/java/android/hardware/location/GeofenceHardware.java b/core/java/android/hardware/location/GeofenceHardware.java index 23d8d01dbe9d..a1866af43706 100644 --- a/core/java/android/hardware/location/GeofenceHardware.java +++ b/core/java/android/hardware/location/GeofenceHardware.java @@ -16,7 +16,7 @@ package android.hardware.location; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.location.Location; import android.os.Build; import android.os.RemoteException; diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java index f96f47dfaffc..2f0265acf4bd 100644 --- a/core/java/android/hardware/soundtrigger/SoundTrigger.java +++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java @@ -25,8 +25,8 @@ import static android.system.OsConstants.EPIPE; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; import android.app.ActivityThread; +import android.compat.annotation.UnsupportedAppUsage; import android.media.AudioFormat; import android.os.Handler; import android.os.Parcel; diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java index 911354862ff4..694d60531396 100644 --- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java +++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java @@ -16,7 +16,7 @@ package android.hardware.soundtrigger; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Handler; import android.os.Looper; import android.os.Message; diff --git a/core/java/android/hardware/usb/UsbDevice.java b/core/java/android/hardware/usb/UsbDevice.java index 08c9eea6e012..fdb4cc079bd7 100644 --- a/core/java/android/hardware/usb/UsbDevice.java +++ b/core/java/android/hardware/usb/UsbDevice.java @@ -18,8 +18,8 @@ package android.hardware.usb; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; import android.app.ActivityThread; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java index 71297c187e5c..215ac24fcb08 100644 --- a/core/java/android/hardware/usb/UsbDeviceConnection.java +++ b/core/java/android/hardware/usb/UsbDeviceConnection.java @@ -20,7 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Build; import android.os.ParcelFileDescriptor; diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index eb148b94ac7b..73b9d1739061 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -26,8 +26,8 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.UnsupportedAppUsage; import android.app.PendingIntent; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; diff --git a/core/java/android/hardware/usb/UsbRequest.java b/core/java/android/hardware/usb/UsbRequest.java index 7abf3e9bcc7a..fab2c0baa10e 100644 --- a/core/java/android/hardware/usb/UsbRequest.java +++ b/core/java/android/hardware/usb/UsbRequest.java @@ -17,7 +17,7 @@ package android.hardware.usb; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.util.Log; diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java index 7d4849f7562d..9327b241c6c5 100644 --- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java @@ -16,7 +16,7 @@ package android.inputmethodservice; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.graphics.Rect; import android.os.Bundle; diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index a47f601033cf..69b93b19fc41 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -18,7 +18,7 @@ package android.inputmethodservice; import android.annotation.BinderThread; import android.annotation.MainThread; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.PackageManager; import android.os.Binder; diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 82d4d1d10d7e..44825a8f1502 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -30,9 +30,9 @@ import android.annotation.IntDef; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; import android.app.ActivityManager; import android.app.Dialog; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; diff --git a/core/java/android/inputmethodservice/Keyboard.java b/core/java/android/inputmethodservice/Keyboard.java index 3f25113874e8..c85fcd990564 100644 --- a/core/java/android/inputmethodservice/Keyboard.java +++ b/core/java/android/inputmethodservice/Keyboard.java @@ -16,8 +16,8 @@ package android.inputmethodservice; -import android.annotation.UnsupportedAppUsage; import android.annotation.XmlRes; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java index 45f067b95298..b780b21bf3d0 100644 --- a/core/java/android/inputmethodservice/KeyboardView.java +++ b/core/java/android/inputmethodservice/KeyboardView.java @@ -16,7 +16,7 @@ package android.inputmethodservice; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java new file mode 100644 index 000000000000..6afdb5ef1b16 --- /dev/null +++ b/core/java/android/net/ConnectivityDiagnosticsManager.java @@ -0,0 +1,242 @@ +/* + * 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.IntDef; +import android.annotation.NonNull; +import android.os.PersistableBundle; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; + +/** + * Class that provides utilities for collecting network connectivity diagnostics information. + * Connectivity information is made available through triggerable diagnostics tools and by listening + * to System validations. Some diagnostics information may be permissions-restricted. + * + * <p>ConnectivityDiagnosticsManager is intended for use by applications offering network + * connectivity on a user device. These tools will provide several mechanisms for these applications + * to be alerted to network conditions as well as diagnose potential network issues themselves. + * + * <p>The primary responsibilities of this class are to: + * + * <ul> + * <li>Allow permissioned applications to register and unregister callbacks for network event + * notifications + * <li>Invoke callbacks for network event notifications, including: + * <ul> + * <li>Network validations + * <li>Data stalls + * <li>Connectivity reports from applications + * </ul> + * </ul> + */ +public class ConnectivityDiagnosticsManager { + public static final int DETECTION_METHOD_DNS_EVENTS = 1; + public static final int DETECTION_METHOD_TCP_METRICS = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef( + prefix = {"DETECTION_METHOD_"}, + value = {DETECTION_METHOD_DNS_EVENTS, DETECTION_METHOD_TCP_METRICS}) + public @interface DetectionMethod {} + + /** @hide */ + public ConnectivityDiagnosticsManager() {} + + /** Class that includes connectivity information for a specific Network at a specific time. */ + public static class ConnectivityReport { + /** The Network for which this ConnectivityReport applied */ + @NonNull public final Network network; + + /** + * The timestamp for the report. The timestamp is taken from {@link + * System#currentTimeMillis}. + */ + public final long reportTimestamp; + + /** LinkProperties available on the Network at the reported timestamp */ + @NonNull public final LinkProperties linkProperties; + + /** NetworkCapabilities available on the Network at the reported timestamp */ + @NonNull public final NetworkCapabilities networkCapabilities; + + /** PersistableBundle that may contain additional info about the report */ + @NonNull public final PersistableBundle additionalInfo; + + /** + * Constructor for ConnectivityReport. + * + * <p>Apps should obtain instances through {@link + * ConnectivityDiagnosticsCallback#onConnectivityReport} instead of instantiating their own + * instances (unless for testing purposes). + * + * @param network The Network for which this ConnectivityReport applies + * @param reportTimestamp The timestamp for the report + * @param linkProperties The LinkProperties available on network at reportTimestamp + * @param networkCapabilities The NetworkCapabilities available on network at + * reportTimestamp + * @param additionalInfo A PersistableBundle that may contain additional info about the + * report + */ + public ConnectivityReport( + @NonNull Network network, + long reportTimestamp, + @NonNull LinkProperties linkProperties, + @NonNull NetworkCapabilities networkCapabilities, + @NonNull PersistableBundle additionalInfo) { + this.network = network; + this.reportTimestamp = reportTimestamp; + this.linkProperties = linkProperties; + this.networkCapabilities = networkCapabilities; + this.additionalInfo = additionalInfo; + } + } + + /** Class that includes information for a suspected data stall on a specific Network */ + public static class DataStallReport { + /** The Network for which this DataStallReport applied */ + @NonNull public final Network network; + + /** + * The timestamp for the report. The timestamp is taken from {@link + * System#currentTimeMillis}. + */ + public final long reportTimestamp; + + /** The detection method used to identify the suspected data stall */ + @DetectionMethod public final int detectionMethod; + + /** PersistableBundle that may contain additional information on the suspected data stall */ + @NonNull public final PersistableBundle stallDetails; + + /** + * Constructor for DataStallReport. + * + * <p>Apps should obtain instances through {@link + * ConnectivityDiagnosticsCallback#onDataStallSuspected} instead of instantiating their own + * instances (unless for testing purposes). + * + * @param network The Network for which this DataStallReport applies + * @param reportTimestamp The timestamp for the report + * @param detectionMethod The detection method used to identify this data stall + * @param stallDetails A PersistableBundle that may contain additional info about the report + */ + public DataStallReport( + @NonNull Network network, + long reportTimestamp, + @DetectionMethod int detectionMethod, + @NonNull PersistableBundle stallDetails) { + this.network = network; + this.reportTimestamp = reportTimestamp; + this.detectionMethod = detectionMethod; + this.stallDetails = stallDetails; + } + } + + /** + * Abstract base class for Connectivity Diagnostics callbacks. Used for notifications about + * network connectivity events. Must be extended by applications wanting notifications. + */ + public abstract static class ConnectivityDiagnosticsCallback { + /** + * Called when the platform completes a data connectivity check. This will also be invoked + * upon registration with the latest report. + * + * <p>The Network specified in the ConnectivityReport may not be active any more when this + * method is invoked. + * + * @param report The ConnectivityReport containing information about a connectivity check + */ + public void onConnectivityReport(@NonNull ConnectivityReport report) {} + + /** + * Called when the platform suspects a data stall on some Network. + * + * <p>The Network specified in the DataStallReport may not be active any more when this + * method is invoked. + * + * @param report The DataStallReport containing information about the suspected data stall + */ + public void onDataStallSuspected(@NonNull DataStallReport report) {} + + /** + * Called when any app reports connectivity to the System. + * + * @param network The Network for which connectivity has been reported + * @param hasConnectivity The connectivity reported to the System + */ + public void onNetworkConnectivityReported( + @NonNull Network network, boolean hasConnectivity) {} + } + + /** + * Registers a ConnectivityDiagnosticsCallback with the System. + * + * <p>Only apps that offer network connectivity to the user are allowed to register callbacks. + * This includes: + * + * <ul> + * <li>Carrier apps with active subscriptions + * <li>Active VPNs + * <li>WiFi Suggesters + * </ul> + * + * <p>Callbacks will be limited to receiving notifications for networks over which apps provide + * connectivity. + * + * <p>If a registering app loses its relevant permissions, any callbacks it registered will + * silently stop receiving callbacks. + * + * <p>Each register() call <b>MUST</b> use a unique ConnectivityDiagnosticsCallback instance. If + * a single instance is registered with multiple NetworkRequests, an IllegalArgumentException + * will be thrown. + * + * @param request The NetworkRequest that will be used to match with Networks for which + * callbacks will be fired + * @param e The Executor to be used for running the callback method invocations + * @param callback The ConnectivityDiagnosticsCallback that the caller wants registered with the + * System + * @throws IllegalArgumentException if the same callback instance is registered with multiple + * NetworkRequests + * @throws SecurityException if the caller does not have appropriate permissions to register a + * callback + */ + public void registerConnectivityDiagnosticsCallback( + @NonNull NetworkRequest request, + @NonNull Executor e, + @NonNull ConnectivityDiagnosticsCallback callback) { + // TODO(b/143187964): implement ConnectivityDiagnostics functionality + throw new UnsupportedOperationException("registerCallback() not supported yet"); + } + + /** + * Unregisters a ConnectivityDiagnosticsCallback with the System. + * + * <p>If the given callback is not currently registered with the System, this operation will be + * a no-op. + * + * @param callback The ConnectivityDiagnosticsCallback to be unregistered from the System. + */ + public void unregisterConnectivityDiagnosticsCallback( + @NonNull ConnectivityDiagnosticsCallback callback) { + // TODO(b/143187964): implement ConnectivityDiagnostics functionality + throw new UnsupportedOperationException("registerCallback() not supported yet"); + } +} diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 82cecb6f20ad..6806d780c506 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -27,8 +27,8 @@ import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; import android.app.PendingIntent; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; import android.net.IpSecManager.UdpEncapsulationSocket; diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java index 97be51a30641..059cd94accfe 100644 --- a/core/java/android/net/DhcpResults.java +++ b/core/java/android/net/DhcpResults.java @@ -16,7 +16,7 @@ package android.net; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.net.shared.InetAddressUtils; import android.os.Parcel; import android.os.Parcelable; diff --git a/core/java/android/net/EthernetManager.java b/core/java/android/net/EthernetManager.java index 72565024d3a5..fd015b4fe52c 100644 --- a/core/java/android/net/EthernetManager.java +++ b/core/java/android/net/EthernetManager.java @@ -17,7 +17,7 @@ package android.net; import android.annotation.SystemService; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Handler; import android.os.Message; diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java index 1ae44e169851..37425ffc18aa 100644 --- a/core/java/android/net/InterfaceConfiguration.java +++ b/core/java/android/net/InterfaceConfiguration.java @@ -16,7 +16,7 @@ package android.net; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; diff --git a/core/java/android/net/IpConfiguration.java b/core/java/android/net/IpConfiguration.java index dddb64d8cece..23d5ff7f3afe 100644 --- a/core/java/android/net/IpConfiguration.java +++ b/core/java/android/net/IpConfiguration.java @@ -20,8 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; -import android.net.StaticIpConfiguration; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java index 93dd2e4d7717..bf8b38fc7f84 100644 --- a/core/java/android/net/LinkAddress.java +++ b/core/java/android/net/LinkAddress.java @@ -30,7 +30,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index ed509cb3da34..2792c564988a 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -20,7 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -63,6 +63,7 @@ public final class LinkProperties implements Parcelable { private String mPrivateDnsServerName; private String mDomains; private ArrayList<RouteInfo> mRoutes = new ArrayList<>(); + private Inet4Address mDhcpServerAddress; private ProxyInfo mHttpProxy; private int mMtu; // in the format "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max" @@ -196,6 +197,7 @@ public final class LinkProperties implements Parcelable { addStackedLink(l); } setMtu(source.mMtu); + setDhcpServerAddress(source.getDhcpServerAddress()); mTcpBufferSizes = source.mTcpBufferSizes; mNat64Prefix = source.mNat64Prefix; mWakeOnLanSupported = source.mWakeOnLanSupported; @@ -460,6 +462,24 @@ public final class LinkProperties implements Parcelable { } /** + * Set DHCP server address. + * + * @param serverAddress the server address to set. + */ + public void setDhcpServerAddress(@Nullable Inet4Address serverAddress) { + mDhcpServerAddress = serverAddress; + } + + /** + * Get DHCP server address + * + * @return The current DHCP server address. + */ + public @Nullable Inet4Address getDhcpServerAddress() { + return mDhcpServerAddress; + } + + /** * Returns the private DNS server name that is in use. If not {@code null}, * private DNS is in strict mode. In this mode, applications should ensure * that all DNS queries are encrypted and sent to this hostname and that @@ -851,6 +871,7 @@ public final class LinkProperties implements Parcelable { mHttpProxy = null; mStackedLinks.clear(); mMtu = 0; + mDhcpServerAddress = null; mTcpBufferSizes = null; mNat64Prefix = null; mWakeOnLanSupported = false; @@ -919,6 +940,11 @@ public final class LinkProperties implements Parcelable { resultJoiner.add("WakeOnLanSupported: true"); } + if (mDhcpServerAddress != null) { + resultJoiner.add("ServerAddress:"); + resultJoiner.add(mDhcpServerAddress.toString()); + } + if (mTcpBufferSizes != null) { resultJoiner.add("TcpBufferSizes:"); resultJoiner.add(mTcpBufferSizes); @@ -1273,6 +1299,17 @@ public final class LinkProperties implements Parcelable { } /** + * Compares this {@code LinkProperties} DHCP server address against the target + * + * @param target LinkProperties to compare. + * @return {@code true} if both are identical, {@code false} otherwise. + * @hide + */ + public boolean isIdenticalDhcpServerAddress(@NonNull LinkProperties target) { + return Objects.equals(mDhcpServerAddress, target.mDhcpServerAddress); + } + + /** * Compares this {@code LinkProperties} interface addresses against the target * * @param target LinkProperties to compare. @@ -1489,6 +1526,7 @@ public final class LinkProperties implements Parcelable { */ return isIdenticalInterfaceName(target) && isIdenticalAddresses(target) + && isIdenticalDhcpServerAddress(target) && isIdenticalDnses(target) && isIdenticalPrivateDns(target) && isIdenticalValidatedPrivateDnses(target) @@ -1613,6 +1651,7 @@ public final class LinkProperties implements Parcelable { + mMtu * 51 + ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode()) + (mUsePrivateDns ? 57 : 0) + + ((null == mDhcpServerAddress) ? 0 : mDhcpServerAddress.hashCode()) + mPcscfs.size() * 67 + ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode()) + Objects.hash(mNat64Prefix) @@ -1635,6 +1674,7 @@ public final class LinkProperties implements Parcelable { dest.writeString(mPrivateDnsServerName); writeAddresses(dest, mPcscfs); dest.writeString(mDomains); + writeAddress(dest, mDhcpServerAddress); dest.writeInt(mMtu); dest.writeString(mTcpBufferSizes); dest.writeInt(mRoutes.size()); @@ -1663,8 +1703,9 @@ public final class LinkProperties implements Parcelable { } } - private static void writeAddress(@NonNull Parcel dest, @NonNull InetAddress addr) { - dest.writeByteArray(addr.getAddress()); + private static void writeAddress(@NonNull Parcel dest, @Nullable InetAddress addr) { + byte[] addressBytes = (addr == null ? null : addr.getAddress()); + dest.writeByteArray(addressBytes); if (addr instanceof Inet6Address) { final Inet6Address v6Addr = (Inet6Address) addr; final boolean hasScopeId = v6Addr.getScopeId() != 0; @@ -1673,9 +1714,11 @@ public final class LinkProperties implements Parcelable { } } - @NonNull + @Nullable private static InetAddress readAddress(@NonNull Parcel p) throws UnknownHostException { final byte[] addr = p.createByteArray(); + if (addr == null) return null; + if (addr.length == INET6_ADDR_LENGTH) { final boolean hasScopeId = p.readBoolean(); final int scopeId = hasScopeId ? p.readInt() : 0; @@ -1722,6 +1765,10 @@ public final class LinkProperties implements Parcelable { } catch (UnknownHostException e) { } } netProp.setDomains(in.readString()); + try { + netProp.setDhcpServerAddress((Inet4Address) InetAddress + .getByAddress(in.createByteArray())); + } catch (UnknownHostException e) { } netProp.setMtu(in.readInt()); netProp.setTcpBufferSizes(in.readString()); addressCount = in.readInt(); diff --git a/core/java/android/net/LinkQualityInfo.java b/core/java/android/net/LinkQualityInfo.java index 2e6915ff4aa5..aa56cff50f63 100644 --- a/core/java/android/net/LinkQualityInfo.java +++ b/core/java/android/net/LinkQualityInfo.java @@ -16,7 +16,7 @@ package android.net; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; diff --git a/core/java/android/net/LocalSocket.java b/core/java/android/net/LocalSocket.java index 6a2031b4131d..5b38f78782a8 100644 --- a/core/java/android/net/LocalSocket.java +++ b/core/java/android/net/LocalSocket.java @@ -16,7 +16,8 @@ package android.net; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; + import java.io.Closeable; import java.io.FileDescriptor; import java.io.IOException; diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java index b066a15106af..e80e3a6f93ec 100644 --- a/core/java/android/net/LocalSocketImpl.java +++ b/core/java/android/net/LocalSocketImpl.java @@ -16,7 +16,7 @@ package android.net; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.system.ErrnoException; import android.system.Int32Ref; import android.system.Os; diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java index 87295142bec4..74c9aac05b41 100644 --- a/core/java/android/net/MacAddress.java +++ b/core/java/android/net/MacAddress.java @@ -19,7 +19,7 @@ package android.net; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.net.wifi.WifiInfo; import android.os.Parcel; import android.os.Parcelable; diff --git a/core/java/android/net/MobileLinkQualityInfo.java b/core/java/android/net/MobileLinkQualityInfo.java index a10a14d750d8..a65de6bb2908 100644 --- a/core/java/android/net/MobileLinkQualityInfo.java +++ b/core/java/android/net/MobileLinkQualityInfo.java @@ -16,7 +16,7 @@ package android.net; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; /** diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java index f12ba1367d98..8d1ab332ce12 100644 --- a/core/java/android/net/Network.java +++ b/core/java/android/net/Network.java @@ -19,7 +19,7 @@ package android.net; import android.annotation.NonNull; import android.annotation.SystemApi; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.system.ErrnoException; diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java index ff4bf2d3474c..60bd5730f8cf 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -17,7 +17,7 @@ package android.net; import android.annotation.NonNull; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Build; import android.os.Bundle; diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 421e61b76fab..a5e567d274c1 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -21,7 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.net.ConnectivityManager.NetworkCallback; import android.os.Build; import android.os.Parcel; diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java index 5b1d12c603b4..42ab9256a6ab 100644 --- a/core/java/android/net/NetworkFactory.java +++ b/core/java/android/net/NetworkFactory.java @@ -16,7 +16,7 @@ package android.net; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Build; import android.os.Handler; diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java index 92f105f77172..d0c536316527 100644 --- a/core/java/android/net/NetworkInfo.java +++ b/core/java/android/net/NetworkInfo.java @@ -17,7 +17,7 @@ package android.net; import android.annotation.NonNull; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java index 33baebbbe857..4f05c9bbb2e3 100644 --- a/core/java/android/net/NetworkPolicy.java +++ b/core/java/android/net/NetworkPolicy.java @@ -16,7 +16,7 @@ package android.net; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.util.BackupUtils; diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index bf272625e713..ca4caf63f23c 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -19,8 +19,8 @@ package android.net; import static android.content.pm.PackageManager.GET_SIGNATURES; import android.annotation.SystemService; -import android.annotation.UnsupportedAppUsage; import android.app.ActivityManager; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; diff --git a/core/java/android/net/NetworkQuotaInfo.java b/core/java/android/net/NetworkQuotaInfo.java index a46cddeda2dd..2e52d9cd19ab 100644 --- a/core/java/android/net/NetworkQuotaInfo.java +++ b/core/java/android/net/NetworkQuotaInfo.java @@ -16,7 +16,7 @@ package android.net; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index 471b23e04775..0c83368a2970 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -20,7 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.net.NetworkCapabilities.NetCapability; import android.net.NetworkCapabilities.Transport; import android.os.Build; diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java index 292cf50ac4ae..e44961528708 100644 --- a/core/java/android/net/NetworkState.java +++ b/core/java/android/net/NetworkState.java @@ -16,7 +16,7 @@ package android.net; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index 6c7aa1379fb1..45bf4d2e1358 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -19,7 +19,7 @@ package android.net; import static android.os.Process.CLAT_UID; import android.annotation.NonNull; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java index f61260ee3ed0..7b799e262419 100644 --- a/core/java/android/net/NetworkStatsHistory.java +++ b/core/java/android/net/NetworkStatsHistory.java @@ -30,7 +30,7 @@ import static android.text.format.DateUtils.SECOND_IN_MILLIS; import static com.android.internal.util.ArrayUtils.total; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.service.NetworkStatsHistoryBucketProto; diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index 87c7118c0ed8..5e6c47a47a8e 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -34,7 +34,7 @@ import static android.net.NetworkStats.ROAMING_NO; import static android.net.NetworkStats.ROAMING_YES; import static android.net.wifi.WifiInfo.removeDoubleQuotes; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.util.BackupUtils; diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java index d0f54b4c7f2d..08cc4e24b245 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -20,7 +20,7 @@ import static android.system.OsConstants.AF_INET; import static android.system.OsConstants.AF_INET6; import android.annotation.NonNull; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.net.shared.Inet4AddressUtils; import android.os.Build; import android.system.ErrnoException; @@ -61,13 +61,6 @@ public class NetworkUtils { public static native void detachBPFFilter(FileDescriptor fd) throws SocketException; /** - * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements. - * @param fd the socket's {@link FileDescriptor}. - * @param ifIndex the interface index. - */ - public native static void setupRaSocket(FileDescriptor fd, int ifIndex) throws SocketException; - - /** * Binds the current process to the network designated by {@code netId}. All sockets created * in the future (and not explicitly bound via a bound {@link SocketFactory} (see * {@link Network#getSocketFactory}) will be bound to this network. Note that if this diff --git a/core/java/android/net/OWNERS b/core/java/android/net/OWNERS index a1c7fce67d30..767b69351065 100644 --- a/core/java/android/net/OWNERS +++ b/core/java/android/net/OWNERS @@ -8,4 +8,4 @@ lorenzo@google.com reminv@google.com satk@google.com -per-file SSL*, Uri*, Url* = flooey@google.com, narayan@google.com, tobiast@google.com +per-file SSL*, Uri*, Url* = prb@google.com, dauletz@google.com, narayan@google.com, tobiast@google.com diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java index 4600942806a8..4ba7394a4bb2 100644 --- a/core/java/android/net/Proxy.java +++ b/core/java/android/net/Proxy.java @@ -18,7 +18,7 @@ package android.net; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.text.TextUtils; import android.util.Log; diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java index 9d92db448668..ffe9ae9521a8 100644 --- a/core/java/android/net/ProxyInfo.java +++ b/core/java/android/net/ProxyInfo.java @@ -18,7 +18,7 @@ package android.net; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java index 52d3fc48a3a5..ea6002ca29e6 100644 --- a/core/java/android/net/RouteInfo.java +++ b/core/java/android/net/RouteInfo.java @@ -21,7 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java index 39cb323d0662..8b6ac4271c30 100644 --- a/core/java/android/net/SSLCertificateSocketFactory.java +++ b/core/java/android/net/SSLCertificateSocketFactory.java @@ -16,7 +16,7 @@ package android.net; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.SystemProperties; import android.util.Log; diff --git a/core/java/android/net/SSLSessionCache.java b/core/java/android/net/SSLSessionCache.java index 9667e82c988c..944bc549acca 100644 --- a/core/java/android/net/SSLSessionCache.java +++ b/core/java/android/net/SSLSessionCache.java @@ -16,7 +16,7 @@ package android.net; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.util.Log; diff --git a/core/java/android/net/SntpClient.java b/core/java/android/net/SntpClient.java index f9c2defc0377..8c6faf6d9970 100644 --- a/core/java/android/net/SntpClient.java +++ b/core/java/android/net/SntpClient.java @@ -16,7 +16,7 @@ package android.net; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.SystemClock; import android.util.Log; diff --git a/core/java/android/net/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java index 990c1142284c..f24a9bd53039 100644 --- a/core/java/android/net/StaticIpConfiguration.java +++ b/core/java/android/net/StaticIpConfiguration.java @@ -20,7 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.net.shared.InetAddressUtils; import android.os.Parcel; import android.os.Parcelable; diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index bf4884aa96a7..162d6e27bd42 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -20,10 +20,10 @@ import android.annotation.NonNull; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; import android.app.DownloadManager; import android.app.backup.BackupManager; import android.app.usage.NetworkStatsManager; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.media.MediaPlayer; import android.os.Build; diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index c3166e91fac7..ddca4b464123 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -19,7 +19,7 @@ package android.net; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Intent; import android.os.Environment; import android.os.Parcel; diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java index ed7cddcc6036..4b804b097d59 100644 --- a/core/java/android/net/VpnService.java +++ b/core/java/android/net/VpnService.java @@ -23,11 +23,11 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; import android.app.Activity; import android.app.PendingIntent; import android.app.Service; import android.app.admin.DevicePolicyManager; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; diff --git a/core/java/android/net/WebAddress.java b/core/java/android/net/WebAddress.java index 994c794e6997..aa3777d55342 100644 --- a/core/java/android/net/WebAddress.java +++ b/core/java/android/net/WebAddress.java @@ -20,7 +20,7 @@ import static android.util.Patterns.GOOD_IRI_CHAR; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import java.util.Locale; diff --git a/core/java/android/net/http/OWNERS b/core/java/android/net/http/OWNERS index 6b8c9edf4257..309261277024 100644 --- a/core/java/android/net/http/OWNERS +++ b/core/java/android/net/http/OWNERS @@ -1,3 +1,4 @@ -flooey@google.com narayan@google.com tobiast@google.com +include platform/libcore:/OWNERS +include platform/external/conscrypt:/OWNERS diff --git a/core/java/android/net/http/SslCertificate.java b/core/java/android/net/http/SslCertificate.java index 01dd08f4ad9c..250cff29944a 100644 --- a/core/java/android/net/http/SslCertificate.java +++ b/core/java/android/net/http/SslCertificate.java @@ -17,7 +17,7 @@ package android.net.http; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Bundle; import android.text.format.DateFormat; diff --git a/core/java/android/net/http/SslError.java b/core/java/android/net/http/SslError.java index b3f2fb79b5c3..d43c616a4f13 100644 --- a/core/java/android/net/http/SslError.java +++ b/core/java/android/net/http/SslError.java @@ -16,8 +16,9 @@ package android.net.http; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; + import java.security.cert.X509Certificate; /** diff --git a/core/java/android/net/metrics/ApfProgramEvent.java b/core/java/android/net/metrics/ApfProgramEvent.java index 8243be9c1355..f93907a37f3f 100644 --- a/core/java/android/net/metrics/ApfProgramEvent.java +++ b/core/java/android/net/metrics/ApfProgramEvent.java @@ -21,7 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; diff --git a/core/java/android/net/metrics/ApfStats.java b/core/java/android/net/metrics/ApfStats.java index eac5579f9eaa..b221cb97b28f 100644 --- a/core/java/android/net/metrics/ApfStats.java +++ b/core/java/android/net/metrics/ApfStats.java @@ -20,7 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; diff --git a/core/java/android/net/metrics/DhcpClientEvent.java b/core/java/android/net/metrics/DhcpClientEvent.java index 5f9f50708a49..8fc1ef80bba9 100644 --- a/core/java/android/net/metrics/DhcpClientEvent.java +++ b/core/java/android/net/metrics/DhcpClientEvent.java @@ -20,7 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java index 6f1bf71f187b..29aaf534eda1 100644 --- a/core/java/android/os/PersistableBundle.java +++ b/core/java/android/os/PersistableBundle.java @@ -16,17 +16,24 @@ package android.os; +import static java.nio.charset.StandardCharsets.UTF_8; + +import android.annotation.NonNull; import android.annotation.Nullable; import android.util.ArrayMap; import android.util.proto.ProtoOutputStream; +import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; import org.xmlpull.v1.XmlSerializer; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.ArrayList; /** @@ -339,4 +346,44 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa proto.end(token); } + + /** + * Writes the content of the {@link PersistableBundle} to a {@link OutputStream}. + * + * <p>The content can be read by a {@link #readFromStream}. + * + * @see #readFromStream + */ + public void writeToStream(@NonNull OutputStream outputStream) throws IOException { + FastXmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(outputStream, UTF_8.name()); + serializer.startTag(null, "bundle"); + try { + saveToXml(serializer); + } catch (XmlPullParserException e) { + throw new IOException(e); + } + serializer.endTag(null, "bundle"); + serializer.flush(); + } + + /** + * Reads a {@link PersistableBundle} from an {@link InputStream}. + * + * <p>The stream must be generated by {@link #writeToStream}. + * + * @see #writeToStream + */ + @NonNull + public static PersistableBundle readFromStream(@NonNull InputStream inputStream) + throws IOException { + try { + XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser(); + parser.setInput(inputStream, UTF_8.name()); + parser.next(); + return PersistableBundle.restoreFromXml(parser); + } catch (XmlPullParserException e) { + throw new IOException(e); + } + } } diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java index a9ddffe7d55c..16f7b398731c 100644 --- a/core/java/android/os/UpdateEngine.java +++ b/core/java/android/os/UpdateEngine.java @@ -16,6 +16,7 @@ package android.os; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.IUpdateEngine; @@ -140,8 +141,43 @@ public class UpdateEngine { * {@code SWITCH_SLOT_ON_REBOOT=0}. See {@link #applyPayload}. */ public static final int UPDATED_BUT_NOT_ACTIVE = 52; + + /** + * Error code: there is not enough space on the device to apply the update. User should + * be prompted to free up space and re-try the update. + * + * <p>See {@link UpdateEngine#allocateSpace}. + */ + public static final int NOT_ENOUGH_SPACE = 60; + + /** + * Error code: the device is corrupted and no further updates may be applied. + * + * <p>See {@link UpdateEngine#cleanupAppliedPayload}. + */ + public static final int DEVICE_CORRUPTED = 61; } + /** @hide */ + @IntDef(value = { + ErrorCodeConstants.SUCCESS, + ErrorCodeConstants.ERROR, + ErrorCodeConstants.FILESYSTEM_COPIER_ERROR, + ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR, + ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR, + ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR, + ErrorCodeConstants.KERNEL_DEVICE_OPEN_ERROR, + ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR, + ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR, + ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR, + ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR, + ErrorCodeConstants.PAYLOAD_TIMESTAMP_ERROR, + ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE, + ErrorCodeConstants.NOT_ENOUGH_SPACE, + ErrorCodeConstants.DEVICE_CORRUPTED, + }) + public @interface ErrorCode {} + /** * Status codes for update engine. Values must agree with the ones in * {@code system/update_engine/client_library/include/update_engine/update_status.h}. @@ -419,4 +455,138 @@ public class UpdateEngine { throw e.rethrowFromSystemServer(); } } + + /** + * Return value of {@link #allocateSpace.} + */ + public static final class AllocateSpaceResult { + private @ErrorCode int mErrorCode = ErrorCodeConstants.SUCCESS; + private long mFreeSpaceRequired = 0; + private AllocateSpaceResult() {} + /** + * Error code. + * + * @return The following error codes: + * <ul> + * <li>{@link ErrorCodeConstants#SUCCESS} if space has been allocated + * successfully.</li> + * <li>{@link ErrorCodeConstants#NOT_ENOUGH_SPACE} if insufficient + * space.</li> + * <li>Other {@link ErrorCodeConstants} for other errors.</li> + * </ul> + */ + @ErrorCode + public int errorCode() { + return mErrorCode; + } + + /** + * Estimated total space that needs to be available on the userdata partition to apply the + * payload (in bytes). + * + * <p> + * Note that in practice, more space needs to be made available before applying the payload + * to keep the device working. + * + * @return The following values: + * <ul> + * <li>zero if {@link #errorCode} returns {@link ErrorCodeConstants#SUCCESS}</li> + * <li>non-zero if {@link #errorCode} returns {@link ErrorCodeConstants#NOT_ENOUGH_SPACE}. + * Value is the estimated total space required on userdata partition.</li> + * </ul> + * @throws IllegalStateException if {@link #errorCode} is not one of the above. + * + */ + public long freeSpaceRequired() { + if (mErrorCode == ErrorCodeConstants.SUCCESS) { + return 0; + } + if (mErrorCode == ErrorCodeConstants.NOT_ENOUGH_SPACE) { + return mFreeSpaceRequired; + } + throw new IllegalStateException(String.format( + "freeSpaceRequired() is not available when error code is %d", mErrorCode)); + } + } + + /** + * Initialize partitions for a payload associated with the given payload + * metadata {@code payloadMetadataFilename} by preallocating required space. + * + * <p>This function should be called after payload has been verified after + * {@link #verifyPayloadMetadata}. This function does not verify whether + * the given payload is applicable or not. + * + * <p>Implementation of {@code allocateSpace} uses + * {@code headerKeyValuePairs} to determine whether space has been allocated + * for a different or same payload previously. If space has been allocated + * for a different payload before, space will be reallocated for the given + * payload. If space has been allocated for the same payload, no actions to + * storage devices are taken. + * + * <p>This function is synchronous and may take a non-trivial amount of + * time. Callers should call this function in a background thread. + * + * @param payloadMetadataFilename See {@link #verifyPayloadMetadata}. + * @param headerKeyValuePairs See {@link #applyPayload}. + * @return See {@link AllocateSpaceResult}. + */ + @NonNull + public AllocateSpaceResult allocateSpace( + @NonNull String payloadMetadataFilename, + @NonNull String[] headerKeyValuePairs) { + AllocateSpaceResult result = new AllocateSpaceResult(); + try { + result.mFreeSpaceRequired = mUpdateEngine.allocateSpaceForPayload( + payloadMetadataFilename, + headerKeyValuePairs); + result.mErrorCode = result.mFreeSpaceRequired == 0 + ? ErrorCodeConstants.SUCCESS + : ErrorCodeConstants.NOT_ENOUGH_SPACE; + return result; + } catch (ServiceSpecificException e) { + result.mErrorCode = e.errorCode; + result.mFreeSpaceRequired = 0; + return result; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Cleanup files used by the previous update and free up space after the + * device has been booted successfully into the new build. + * + * <p>In particular, this function waits until delta files for snapshots for + * Virtual A/B update are merged to OS partitions, then delete these delta + * files. + * + * <p>This function is synchronous and may take a non-trivial amount of + * time. Callers should call this function in a background thread. + * + * <p>This function does not delete payload binaries downloaded for a + * non-streaming OTA update. + * + * @return One of the following: + * <ul> + * <li>{@link ErrorCodeConstants#SUCCESS} if execution is successful.</li> + * <li>{@link ErrorCodeConstants#ERROR} if a transient error has occurred. + * The device should be able to recover after a reboot. The function should + * be retried after the reboot.</li> + * <li>{@link ErrorCodeConstants#DEVICE_CORRUPTED} if a permanent error is + * encountered. Device is corrupted, and future updates must not be applied. + * The device cannot recover without flashing and factory resets. + * </ul> + * + * @throws ServiceSpecificException if other transient errors has occurred. + * A reboot may or may not help resolving the issue. + */ + @ErrorCode + public int cleanupAppliedPayload() { + try { + return mUpdateEngine.cleanupSuccessfulUpdate(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/os/UpdateEngineCallback.java b/core/java/android/os/UpdateEngineCallback.java index f07294e222e2..7fe7024f25b3 100644 --- a/core/java/android/os/UpdateEngineCallback.java +++ b/core/java/android/os/UpdateEngineCallback.java @@ -44,5 +44,6 @@ public abstract class UpdateEngineCallback { * unsuccessfully. The value of {@code errorCode} will be one of the * values from {@link UpdateEngine.ErrorCodeConstants}. */ - public abstract void onPayloadApplicationComplete(int errorCode); + public abstract void onPayloadApplicationComplete( + @UpdateEngine.ErrorCode int errorCode); } diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java index f1d90be9aafe..7285166cdd5f 100644 --- a/core/java/android/provider/CalendarContract.java +++ b/core/java/android/provider/CalendarContract.java @@ -40,7 +40,7 @@ import android.database.DatabaseUtils; import android.net.Uri; import android.os.RemoteException; import android.text.format.DateUtils; -import android.text.format.Time; +import android.text.format.TimeMigrationUtils; import android.util.Log; import com.android.internal.util.Preconditions; @@ -1680,7 +1680,7 @@ public final class CalendarContract { * <h3>Writing to Events</h3> There are further restrictions on all Updates * and Inserts in the Events table: * <ul> - * <li>If allDay is set to 1 eventTimezone must be {@link Time#TIMEZONE_UTC} + * <li>If allDay is set to 1 eventTimezone must be "UTC" * and the time must correspond to a midnight boundary.</li> * <li>Exceptions are not allowed to recur. If rrule or rdate is not empty, * original_id and original_sync_id must be empty.</li> @@ -2609,9 +2609,7 @@ public final class CalendarContract { @UnsupportedAppUsage public static void scheduleAlarm(Context context, AlarmManager manager, long alarmTime) { if (DEBUG) { - Time time = new Time(); - time.set(alarmTime); - String schedTime = time.format(" %a, %b %d, %Y %I:%M%P"); + String schedTime = TimeMigrationUtils.formatMillisWithFixedFormat(alarmTime); Log.d(TAG, "Schedule alarm at " + alarmTime + " " + schedTime); } diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index df36f143393b..13d167d5e99a 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -3522,7 +3522,8 @@ public final class Telephony { * can manage DPC-owned APNs. * @hide */ - public static final Uri DPC_URI = Uri.parse("content://telephony/carriers/dpc"); + @SystemApi + public static final @NonNull Uri DPC_URI = Uri.parse("content://telephony/carriers/dpc"); /** * The {@code content://} style URL to be called from Telephony to query APNs. @@ -3831,6 +3832,13 @@ public final class Telephony { public static final String USER_EDITABLE = "user_editable"; /** + * Integer value denoting an invalid APN id + * @hide + */ + @SystemApi + public static final int INVALID_APN_ID = -1; + + /** * {@link #EDITED_STATUS APN edit status} indicates that this APN has not been edited or * fails to edit. * <p>Type: INTEGER </p> diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index c11a99d231d5..fc2dfb833a8f 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -23,6 +23,8 @@ import android.annotation.TestApi; import android.content.Context; import android.os.Binder; import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerExecutor; import android.os.RemoteException; import android.os.ServiceManager; import android.telephony.Annotation.CallState; diff --git a/core/java/android/text/format/TimeFormatter.java b/core/java/android/text/format/TimeFormatter.java index 5a14092c95a8..f7fd89d7d819 100644 --- a/core/java/android/text/format/TimeFormatter.java +++ b/core/java/android/text/format/TimeFormatter.java @@ -26,6 +26,9 @@ import libcore.icu.LocaleData; import libcore.util.ZoneInfo; import java.nio.CharBuffer; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.Formatter; import java.util.Locale; import java.util.TimeZone; @@ -86,6 +89,59 @@ class TimeFormatter { } /** + * The implementation of {@link TimeMigrationUtils#formatMillisWithFixedFormat(long)} for + * 2038-safe formatting with the pattern "%Y-%m-%d %H:%M:%S" and including the historic + * 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 + // 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 + // behavior. + + // No need to worry about overflow / underflow: long millis is representable by Instant and + // LocalDateTime with room to spare. + Instant instant = Instant.ofEpochMilli(timeMillis); + + // Date/times are calculated in the current system default time zone. + LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + + // You'd think it would be as simple as: + // DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", locale); + // return formatter.format(localDateTime); + // but we retain Time's behavior around digits. + + StringBuilder stringBuilder = new StringBuilder(19); + + // This effectively uses the US locale because number localization is handled separately + // (see below). + stringBuilder.append(localDateTime.getYear()); + stringBuilder.append('-'); + append2DigitNumber(stringBuilder, localDateTime.getMonthValue()); + stringBuilder.append('-'); + append2DigitNumber(stringBuilder, localDateTime.getDayOfMonth()); + stringBuilder.append(' '); + append2DigitNumber(stringBuilder, localDateTime.getHour()); + stringBuilder.append(':'); + append2DigitNumber(stringBuilder, localDateTime.getMinute()); + stringBuilder.append(':'); + append2DigitNumber(stringBuilder, localDateTime.getSecond()); + + String result = stringBuilder.toString(); + return localizeDigits(result); + } + + /** Zero-pads value as needed to achieve a 2-digit number. */ + private static void append2DigitNumber(StringBuilder builder, int value) { + if (value < 10) { + builder.append('0'); + } + builder.append(value); + } + + /** * Format the specified {@code wallTime} using {@code pattern}. The output is returned. */ public String format(String pattern, ZoneInfo.WallTime wallTime, ZoneInfo zoneInfo) { @@ -99,12 +155,9 @@ class TimeFormatter { formatInternal(pattern, wallTime, zoneInfo); String result = stringBuilder.toString(); - // This behavior is the source of a bug since some formats are defined as being - // in ASCII and not localized. - if (localeData.zeroDigit != '0') { - result = localizeDigits(result); - } - return result; + // The localizeDigits() behavior is the source of a bug since some formats are defined + // as being in ASCII and not localized. + return localizeDigits(result); } finally { outputBuilder = null; numberFormatter = null; @@ -112,6 +165,10 @@ class TimeFormatter { } private String localizeDigits(String s) { + if (localeData.zeroDigit == '0') { + return s; + } + int length = s.length(); int offsetToLocalizedDigits = localeData.zeroDigit - '0'; StringBuilder result = new StringBuilder(length); diff --git a/core/java/android/text/format/TimeMigrationUtils.java b/core/java/android/text/format/TimeMigrationUtils.java new file mode 100644 index 000000000000..17bac8d67b26 --- /dev/null +++ b/core/java/android/text/format/TimeMigrationUtils.java @@ -0,0 +1,40 @@ +/* + * 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.text.format; + +/** + * Logic to ease migration away from {@link Time} in Android internal code. {@link Time} is + * afflicted by the Y2038 issue and deprecated. The methods here are intended to allow minimal + * changes to classes that use {@link Time} for common behavior. + * + * @hide + */ +public class TimeMigrationUtils { + + private TimeMigrationUtils() {} + + /** + * A Y2038-safe replacement for various users of the {@link Time#format(String)} with the + * pattern "%Y-%m-%d %H:%M:%S". Note, this method retains the unusual localization behavior + * originally implemented by Time, which can lead to non-latin numbers being produced if the + * default locale does not use latin numbers. + */ + public static String formatMillisWithFixedFormat(long timeMillis) { + // Delegate to TimeFormatter so that the unusual localization / threading behavior can be + // reused. + return new TimeFormatter().formatMillisWithFixedFormat(timeMillis); + } +} diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java index da566c934ef7..ea294f010791 100644 --- a/core/java/android/util/NtpTrustedTime.java +++ b/core/java/android/util/NtpTrustedTime.java @@ -175,4 +175,21 @@ public class NtpTrustedTime implements TrustedTime { public long getCachedNtpTimeReference() { return mCachedNtpElapsedRealtime; } + + /** + * Returns the combination of {@link #getCachedNtpTime()} and {@link + * #getCachedNtpTimeReference()} as a {@link TimestampedValue}. This method is useful when + * passing the time to another component that will adjust for elapsed time. + * + * @throws IllegalStateException if there is no cached value + */ + public TimestampedValue<Long> getCachedNtpTimeSignal() { + if (!mHasCache) { + throw new IllegalStateException("Missing authoritative time source"); + } + if (LOGD) Log.d(TAG, "getCachedNtpTimeSignal() cache hit"); + + return new TimestampedValue<>(mCachedNtpElapsedRealtime, mCachedNtpTime); + } + } diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 8a1fd62c0201..c67fca59fe97 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -870,6 +870,94 @@ public class ViewDebug { return null; } + private static class StreamingPictureCallbackHandler implements AutoCloseable, + HardwareRenderer.PictureCapturedCallback, Runnable { + private final HardwareRenderer mRenderer; + private final Callable<OutputStream> mCallback; + private final Executor mExecutor; + private final ReentrantLock mLock = new ReentrantLock(false); + private final ArrayDeque<byte[]> mQueue = new ArrayDeque<>(3); + private final ByteArrayOutputStream mByteStream = new ByteArrayOutputStream(); + private boolean mStopListening; + private Thread mRenderThread; + + private StreamingPictureCallbackHandler(HardwareRenderer renderer, + Callable<OutputStream> callback, Executor executor) { + mRenderer = renderer; + mCallback = callback; + mExecutor = executor; + mRenderer.setPictureCaptureCallback(this); + } + + @Override + public void close() { + mLock.lock(); + mStopListening = true; + mLock.unlock(); + mRenderer.setPictureCaptureCallback(null); + } + + @Override + public void onPictureCaptured(Picture picture) { + mLock.lock(); + if (mStopListening) { + mLock.unlock(); + mRenderer.setPictureCaptureCallback(null); + return; + } + if (mRenderThread == null) { + mRenderThread = Thread.currentThread(); + } + boolean needsInvoke = true; + if (mQueue.size() == 3) { + mQueue.removeLast(); + needsInvoke = false; + } + picture.writeToStream(mByteStream); + mQueue.add(mByteStream.toByteArray()); + mByteStream.reset(); + mLock.unlock(); + + if (needsInvoke) { + mExecutor.execute(this); + } + } + + @Override + public void run() { + mLock.lock(); + final byte[] picture = mQueue.poll(); + final boolean isStopped = mStopListening; + mLock.unlock(); + if (Thread.currentThread() == mRenderThread) { + close(); + throw new IllegalStateException( + "ViewDebug#startRenderingCommandsCapture must be given an executor that " + + "invokes asynchronously"); + } + if (isStopped) { + return; + } + OutputStream stream = null; + try { + stream = mCallback.call(); + } catch (Exception ex) { + Log.w("ViewDebug", "Aborting rendering commands capture " + + "because callback threw exception", ex); + } + if (stream != null) { + try { + stream.write(picture); + } catch (IOException ex) { + Log.w("ViewDebug", "Aborting rendering commands capture " + + "due to IOException writing to output stream", ex); + } + } else { + close(); + } + } + } + /** * Begins capturing the entire rendering commands for the view tree referenced by the given * view. The view passed may be any View in the tree as long as it is attached. That is, @@ -915,18 +1003,7 @@ public class ViewDebug { } final HardwareRenderer renderer = attachInfo.mThreadedRenderer; if (renderer != null) { - return new PictureCallbackHandler(renderer, (picture -> { - try { - OutputStream stream = callback.call(); - if (stream != null) { - picture.writeToStream(stream); - return true; - } - } catch (Exception ex) { - // fall through - } - return false; - }), executor); + return new StreamingPictureCallbackHandler(renderer, callback, executor); } return null; } diff --git a/core/java/com/android/internal/compat/AndroidBuildClassifier.java b/core/java/com/android/internal/compat/AndroidBuildClassifier.java new file mode 100644 index 000000000000..0b937fad7df1 --- /dev/null +++ b/core/java/com/android/internal/compat/AndroidBuildClassifier.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.compat; + +import android.os.Build; + +/** + * Platform private class for determining the type of Android build installed. + * + */ +public class AndroidBuildClassifier { + + public boolean isDebuggableBuild() { + return Build.IS_DEBUGGABLE; + } + + public boolean isFinalBuild() { + return "REL".equals(Build.VERSION.CODENAME); + } +} diff --git a/core/java/com/android/internal/compat/IOverrideValidator.aidl b/core/java/com/android/internal/compat/IOverrideValidator.aidl new file mode 100644 index 000000000000..add4708863aa --- /dev/null +++ b/core/java/com/android/internal/compat/IOverrideValidator.aidl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.compat; + +import android.content.pm.ApplicationInfo; + +import com.android.internal.compat.OverrideAllowedState; + +/** + * Platform private API for determining whether a changeId can be overridden. + * + * {@hide} + */ +interface IOverrideValidator +{ + /** + * Validation function. + * @param changeId id of the change to be toggled on or off. + * @param packageName package of the app for which the change should be overridden. + * @return {@link OverrideAllowedState} specifying whether the change can be overridden for + * the given package or a reason why not. + */ + OverrideAllowedState getOverrideAllowedState(long changeId, String packageName); +} diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl index 7dcb12c9e72b..4c203d394759 100644 --- a/core/java/com/android/internal/compat/IPlatformCompat.aidl +++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl @@ -17,6 +17,7 @@ package com.android.internal.compat; import android.content.pm.ApplicationInfo; +import com.android.internal.compat.IOverrideValidator; import java.util.Map; parcelable CompatibilityChangeConfig; @@ -195,4 +196,9 @@ interface IPlatformCompat * @return An array of {@link CompatChangeInfo} known to the service. */ CompatibilityChangeInfo[] listAllChanges(); + + /** + * Get an instance that can determine whether a changeid can be overridden for a package name. + */ + IOverrideValidator getOverrideValidator(); } diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.aidl b/core/java/com/android/internal/compat/OverrideAllowedState.aidl new file mode 100644 index 000000000000..10ceac786841 --- /dev/null +++ b/core/java/com/android/internal/compat/OverrideAllowedState.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.compat; + +parcelable OverrideAllowedState;
\ No newline at end of file diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.java b/core/java/com/android/internal/compat/OverrideAllowedState.java new file mode 100644 index 000000000000..56216c251070 --- /dev/null +++ b/core/java/com/android/internal/compat/OverrideAllowedState.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.compat; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * This class contains all the possible override allowed states. + */ +public final class OverrideAllowedState implements Parcelable { + @IntDef({ + ALLOWED, + DISABLED_NOT_DEBUGGABLE, + DISABLED_NON_TARGET_SDK, + DISABLED_TARGET_SDK_TOO_HIGH, + PACKAGE_DOES_NOT_EXIST + }) + @Retention(RetentionPolicy.SOURCE) + public @interface State { + } + + /** + * Change can be overridden. + */ + public static final int ALLOWED = 0; + /** + * Change cannot be overridden, due to the app not being debuggable. + */ + public static final int DISABLED_NOT_DEBUGGABLE = 1; + /** + * Change cannot be overridden, due to the build being non-debuggable and the change being + * non-targetSdk. + */ + public static final int DISABLED_NON_TARGET_SDK = 2; + /** + * Change cannot be overridden, due to the app's targetSdk being above the change's targetSdk. + */ + public static final int DISABLED_TARGET_SDK_TOO_HIGH = 3; + /** + * Package does not exist. + */ + public static final int PACKAGE_DOES_NOT_EXIST = 4; + + @State + public final int state; + public final int appTargetSdk; + public final int changeIdTargetSdk; + + private OverrideAllowedState(Parcel parcel) { + state = parcel.readInt(); + appTargetSdk = parcel.readInt(); + changeIdTargetSdk = parcel.readInt(); + } + + public OverrideAllowedState(@State int state, int appTargetSdk, int changeIdTargetSdk) { + this.state = state; + this.appTargetSdk = appTargetSdk; + this.changeIdTargetSdk = changeIdTargetSdk; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(state); + out.writeInt(appTargetSdk); + out.writeInt(changeIdTargetSdk); + } + + /** + * Enforces the policy for overriding compat changes. + * + * @param changeId the change id that was attempted to be overridden. + * @param packageName the package for which the attempt was made. + * @throws SecurityException if the policy forbids this operation. + */ + public void enforce(long changeId, String packageName) + throws SecurityException { + switch (state) { + case ALLOWED: + return; + case DISABLED_NOT_DEBUGGABLE: + throw new SecurityException( + "Cannot override a change on a non-debuggable app and user build."); + case DISABLED_NON_TARGET_SDK: + throw new SecurityException( + "Cannot override a default enabled/disabled change on a user build."); + case DISABLED_TARGET_SDK_TOO_HIGH: + throw new SecurityException(String.format( + "Cannot override %1$d for %2$s because the app's targetSdk (%3$d) is " + + "above the change's targetSdk threshold (%4$d)", + changeId, packageName, appTargetSdk, changeIdTargetSdk)); + case PACKAGE_DOES_NOT_EXIST: + throw new SecurityException(String.format( + "Cannot override %1$d for %2$s because the package does not exist, and " + + "the change is targetSdk gated.", + changeId, packageName)); + } + } + + public static final @NonNull + Parcelable.Creator<OverrideAllowedState> CREATOR = + new Parcelable.Creator<OverrideAllowedState>() { + public OverrideAllowedState createFromParcel(Parcel parcel) { + OverrideAllowedState info = new OverrideAllowedState(parcel); + return info; + } + + public OverrideAllowedState[] newArray(int size) { + return new OverrideAllowedState[size]; + } + }; + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof OverrideAllowedState)) { + return false; + } + OverrideAllowedState otherState = (OverrideAllowedState) obj; + return state == otherState.state + && appTargetSdk == otherState.appTargetSdk + && changeIdTargetSdk == otherState.changeIdTargetSdk; + } +} diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp index 08aa1d97fa1c..ba7fe7ff8739 100644 --- a/core/jni/android_net_NetUtils.cpp +++ b/core/jni/android_net_NetUtils.cpp @@ -24,9 +24,7 @@ #include <linux/tcp.h> #include <net/if.h> #include <netinet/ether.h> -#include <netinet/icmp6.h> #include <netinet/ip.h> -#include <netinet/ip6.h> #include <netinet/udp.h> #include <android_runtime/AndroidRuntime.h> @@ -102,98 +100,6 @@ static void android_net_utils_detachBPFFilter(JNIEnv *env, jobject clazz, jobjec } } -static void android_net_utils_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd, - jint ifIndex) -{ - static const int kLinkLocalHopLimit = 255; - - int fd = jniGetFDFromFileDescriptor(env, javaFd); - - // Set an ICMPv6 filter that only passes Router Solicitations. - struct icmp6_filter rs_only; - ICMP6_FILTER_SETBLOCKALL(&rs_only); - ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &rs_only); - socklen_t len = sizeof(rs_only); - if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &rs_only, len) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(ICMP6_FILTER): %s", strerror(errno)); - return; - } - - // Most/all of the rest of these options can be set via Java code, but - // because we're here on account of setting an icmp6_filter go ahead - // and do it all natively for now. - // - // TODO: Consider moving these out to Java. - - // Set the multicast hoplimit to 255 (link-local only). - int hops = kLinkLocalHopLimit; - len = sizeof(hops); - if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, len) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(IPV6_MULTICAST_HOPS): %s", strerror(errno)); - return; - } - - // Set the unicast hoplimit to 255 (link-local only). - hops = kLinkLocalHopLimit; - len = sizeof(hops); - if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, len) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(IPV6_UNICAST_HOPS): %s", strerror(errno)); - return; - } - - // Explicitly disable multicast loopback. - int off = 0; - len = sizeof(off); - if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, len) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(IPV6_MULTICAST_LOOP): %s", strerror(errno)); - return; - } - - // Specify the IPv6 interface to use for outbound multicast. - len = sizeof(ifIndex); - if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifIndex, len) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(IPV6_MULTICAST_IF): %s", strerror(errno)); - return; - } - - // Additional options to be considered: - // - IPV6_TCLASS - // - IPV6_RECVPKTINFO - // - IPV6_RECVHOPLIMIT - - // Bind to [::]. - const struct sockaddr_in6 sin6 = { - .sin6_family = AF_INET6, - .sin6_port = 0, - .sin6_flowinfo = 0, - .sin6_addr = IN6ADDR_ANY_INIT, - .sin6_scope_id = 0, - }; - auto sa = reinterpret_cast<const struct sockaddr *>(&sin6); - len = sizeof(sin6); - if (bind(fd, sa, len) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "bind(IN6ADDR_ANY): %s", strerror(errno)); - return; - } - - // Join the all-routers multicast group, ff02::2%index. - struct ipv6_mreq all_rtrs = { - .ipv6mr_multiaddr = {{{0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2}}}, - .ipv6mr_interface = ifIndex, - }; - len = sizeof(all_rtrs); - if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &all_rtrs, len) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(IPV6_JOIN_GROUP): %s", strerror(errno)); - return; - } -} static jboolean android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId) { @@ -370,7 +276,6 @@ static const JNINativeMethod gNetworkUtilMethods[] = { { "attachDropAllBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDropAllBPFFilter }, { "detachBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_detachBPFFilter }, { "getTcpRepairWindow", "(Ljava/io/FileDescriptor;)Landroid/net/TcpRepairWindow;", (void*) android_net_utils_getTcpRepairWindow }, - { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_setupRaSocket }, { "resNetworkSend", "(I[BII)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkSend }, { "resNetworkQuery", "(ILjava/lang/String;III)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkQuery }, { "resNetworkResult", "(Ljava/io/FileDescriptor;)Landroid/net/DnsResolver$DnsResponse;", (void*) android_net_utils_resNetworkResult }, diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp index c8e901e1e15e..ee74f6d7ab94 100644 --- a/core/jni/fd_utils.cpp +++ b/core/jni/fd_utils.cpp @@ -37,8 +37,6 @@ static const char* kPathWhitelist[] = { "/apex/com.android.ipsec/javalib/ike.jar", "/apex/com.android.media/javalib/updatable-media.jar", "/apex/com.android.sdkext/javalib/framework-sdkext.jar", - "/apex/com.android.telephony/javalib/telephony-common.jar", - "/apex/com.android.telephony/javalib/ims-common.jar", "/apex/com.android.tethering/javalib/framework-tethering.jar", "/dev/null", "/dev/socket/zygote", diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 31ac2bc7407f..a42ba17c100e 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -451,7 +451,6 @@ <protected-broadcast android:name="android.intent.action.internal_sim_state_changed" /> <protected-broadcast android:name="android.intent.action.LOCKED_BOOT_COMPLETED" /> <protected-broadcast android:name="android.intent.action.PRECISE_CALL_STATE" /> - <protected-broadcast android:name="android.intent.action.PRECISE_DATA_CONNECTION_STATE_CHANGED" /> <protected-broadcast android:name="android.intent.action.SUBSCRIPTION_PHONE_STATE" /> <protected-broadcast android:name="android.intent.action.USER_INFO_CHANGED" /> <protected-broadcast android:name="android.intent.action.USER_UNLOCKED" /> @@ -2524,17 +2523,17 @@ <permission android:name="android.permission.READ_WALLPAPER_INTERNAL" android:protectionLevel="signature|privileged" /> - <!-- ============================================ --> - <!-- Permissions for changing the system clock --> - <!-- ============================================ --> + <!-- ===================================================== --> + <!-- Permissions for changing the system clock / time zone --> + <!-- ===================================================== --> <eat-comment /> - <!-- Allows applications to set the system time. - <p>Not for use by third-party applications. --> + <!-- Allows applications to set the system time directly. + <p>Not for use by third-party applications. --> <permission android:name="android.permission.SET_TIME" android:protectionLevel="signature|privileged" /> - <!-- Allows applications to set the system time zone. + <!-- Allows applications to set the system time zone directly. <p>Not for use by third-party applications. --> <permission android:name="android.permission.SET_TIME_ZONE" @@ -2542,6 +2541,20 @@ android:description="@string/permdesc_setTimeZone" android:protectionLevel="signature|privileged" /> + <!-- Allows telephony to suggest the time / time zone. + <p>Not for use by third-party applications. + @hide + --> + <permission android:name="android.permission.SUGGEST_PHONE_TIME_AND_ZONE" + android:protectionLevel="signature|telephony" /> + + <!-- Allows applications like settings to suggest the user's manually chosen time / time zone. + <p>Not for use by third-party applications. + @hide + --> + <permission android:name="android.permission.SUGGEST_MANUAL_TIME_AND_ZONE" + android:protectionLevel="signature" /> + <!-- ==================================================== --> <!-- Permissions related to changing status bar --> <!-- ==================================================== --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index a404e2ed6f52..04d1eef9cba1 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2266,9 +2266,6 @@ <!-- Number of times to try again with the shorter interval, before backing off until the normal polling interval. A value < 0 indicates infinite. --> <integer name="config_ntpRetry">3</integer> - <!-- If the time difference is greater than this threshold in milliseconds, - then update the time. --> - <integer name="config_ntpThreshold">5000</integer> <!-- Timeout to wait for NTP server response in milliseconds. --> <integer name="config_ntpTimeout">5000</integer> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 2507787e2847..383fcd4753f0 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -489,7 +489,6 @@ <java-symbol type="integer" name="config_ntpPollingInterval" /> <java-symbol type="integer" name="config_ntpPollingIntervalShorter" /> <java-symbol type="integer" name="config_ntpRetry" /> - <java-symbol type="integer" name="config_ntpThreshold" /> <java-symbol type="integer" name="config_ntpTimeout" /> <java-symbol type="integer" name="config_shortPressOnPowerBehavior" /> <java-symbol type="integer" name="config_toastDefaultGravity" /> diff --git a/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java new file mode 100644 index 000000000000..9b3d0c9eaff6 --- /dev/null +++ b/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java @@ -0,0 +1,66 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.timedetector; + +import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable; +import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import android.util.TimestampedValue; + +import org.junit.Test; + +public class NetworkTimeSuggestionTest { + + private static final TimestampedValue<Long> ARBITRARY_TIME = + new TimestampedValue<>(1111L, 2222L); + + @Test + public void testEquals() { + NetworkTimeSuggestion one = new NetworkTimeSuggestion(ARBITRARY_TIME); + assertEquals(one, one); + + NetworkTimeSuggestion two = new NetworkTimeSuggestion(ARBITRARY_TIME); + assertEquals(one, two); + assertEquals(two, one); + + TimestampedValue<Long> differentTime = new TimestampedValue<>( + ARBITRARY_TIME.getReferenceTimeMillis() + 1, + ARBITRARY_TIME.getValue()); + NetworkTimeSuggestion three = new NetworkTimeSuggestion(differentTime); + assertNotEquals(one, three); + assertNotEquals(three, one); + + // DebugInfo must not be considered in equals(). + one.addDebugInfo("Debug info 1"); + two.addDebugInfo("Debug info 2"); + assertEquals(one, two); + } + + @Test + public void testParcelable() { + NetworkTimeSuggestion suggestion = new NetworkTimeSuggestion(ARBITRARY_TIME); + assertRoundTripParcelable(suggestion); + + // DebugInfo should also be stored (but is not checked by equals() + suggestion.addDebugInfo("This is debug info"); + NetworkTimeSuggestion rtSuggestion = roundTripParcelable(suggestion); + assertEquals(suggestion.getDebugInfo(), rtSuggestion.getDebugInfo()); + } +} diff --git a/core/tests/coretests/src/android/text/format/TimeMigrationUtilsTest.java b/core/tests/coretests/src/android/text/format/TimeMigrationUtilsTest.java new file mode 100644 index 000000000000..b605520c659d --- /dev/null +++ b/core/tests/coretests/src/android/text/format/TimeMigrationUtilsTest.java @@ -0,0 +1,120 @@ +/* + * 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.text.format; + +import static org.junit.Assert.assertEquals; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Locale; +import java.util.TimeZone; + +@Presubmit +@SmallTest +@RunWith(AndroidJUnit4.class) +public class TimeMigrationUtilsTest { + + private static final int ONE_DAY_IN_SECONDS = 24 * 60 * 60; + + private Locale mDefaultLocale; + private TimeZone mDefaultTimeZone; + + @Before + public void setUp() { + mDefaultLocale = Locale.getDefault(); + mDefaultTimeZone = TimeZone.getDefault(); + } + + @After + public void tearDown() { + Locale.setDefault(mDefaultLocale); + TimeZone.setDefault(mDefaultTimeZone); + } + + @Test + public void formatMillisWithFixedFormat_fixes2038Issue() { + Locale.setDefault(Locale.UK); + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + + // The following cannot be represented properly using Time because they are outside of the + // supported range. + long y2038Issue1 = (((long) Integer.MIN_VALUE) - ONE_DAY_IN_SECONDS) * 1000L; + assertEquals( + "1901-12-12 20:45:52", TimeMigrationUtils.formatMillisWithFixedFormat(y2038Issue1)); + long y2038Issue2 = (((long) Integer.MAX_VALUE) + ONE_DAY_IN_SECONDS) * 1000L; + assertEquals( + "2038-01-20 03:14:07", TimeMigrationUtils.formatMillisWithFixedFormat(y2038Issue2)); + } + + /** + * Compares TimeMigrationUtils.formatSimpleDateTime() with the code it is replacing. + */ + @Test + public void formatMillisAsDateTime_matchesOldBehavior() { + // A selection of interesting locales. + Locale[] locales = new Locale[] { + Locale.US, + Locale.UK, + Locale.FRANCE, + Locale.JAPAN, + Locale.CHINA, + // Android supports RTL locales like arabic and arabic with latin numbers. + Locale.forLanguageTag("ar-AE"), + Locale.forLanguageTag("ar-AE-u-nu-latn"), + }; + // A selection of interesting time zones. + String[] timeZoneIds = new String[] { + "UTC", "Europe/London", "America/New_York", "America/Los_Angeles", "Asia/Shanghai", + }; + // Some arbitrary times when the two formatters should agree. + long[] timesMillis = new long[] { + System.currentTimeMillis(), + 0, + // The Time class only works in 32-bit range, the replacement works beyond that. To + // avoid messing around with offsets and complicating the test, below there are a + // day after / before the known limits. + (Integer.MIN_VALUE + ONE_DAY_IN_SECONDS) * 1000L, + (Integer.MAX_VALUE - ONE_DAY_IN_SECONDS) * 1000L, + }; + + for (Locale locale : locales) { + Locale.setDefault(locale); + for (String timeZoneId : timeZoneIds) { + TimeZone timeZone = TimeZone.getTimeZone(timeZoneId); + TimeZone.setDefault(timeZone); + for (long timeMillis : timesMillis) { + Time time = new Time(); + time.set(timeMillis); + String oldResult = time.format("%Y-%m-%d %H:%M:%S"); + String newResult = TimeMigrationUtils.formatMillisWithFixedFormat(timeMillis); + assertEquals( + "locale=" + locale + ", timeZoneId=" + timeZoneId + + ", timeMillis=" + timeMillis, + oldResult, newResult); + } + } + } + } +} diff --git a/core/xsd/permission.xsd b/core/xsd/permission.xsd index 9520db767350..cc01a31224bc 100644 --- a/core/xsd/permission.xsd +++ b/core/xsd/permission.xsd @@ -126,12 +126,12 @@ </xs:complexType> <xs:complexType name="privapp-permissions"> <xs:sequence> - <xs:element name="permission" maxOccurs="unbounded"> + <xs:element name="permission" minOccurs="0" maxOccurs="unbounded"> <xs:complexType> <xs:attribute name="name" type="xs:string"/> </xs:complexType> </xs:element> - <xs:element name="deny-permission" maxOccurs="unbounded"> + <xs:element name="deny-permission" minOccurs="0" maxOccurs="unbounded"> <xs:complexType> <xs:attribute name="name" type="xs:string"/> </xs:complexType> @@ -141,12 +141,12 @@ </xs:complexType> <xs:complexType name="oem-permissions"> <xs:sequence> - <xs:element name="permission" maxOccurs="unbounded"> + <xs:element name="permission" minOccurs="0" maxOccurs="unbounded"> <xs:complexType> <xs:attribute name="name" type="xs:string"/> </xs:complexType> </xs:element> - <xs:element name="deny-permission" maxOccurs="unbounded"> + <xs:element name="deny-permission" minOccurs="0" maxOccurs="unbounded"> <xs:complexType> <xs:attribute name="name" type="xs:string"/> </xs:complexType> diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml index ba877f8d0d02..cc1ce9bbeecb 100644 --- a/data/etc/com.android.settings.xml +++ b/data/etc/com.android.settings.xml @@ -42,8 +42,8 @@ <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> <permission name="android.permission.READ_SEARCH_INDEXABLES"/> <permission name="android.permission.REBOOT"/> - <permission name="android.permission.SET_TIME"/> <permission name="android.permission.STATUS_BAR"/> + <permission name="android.permission.SUGGEST_MANUAL_TIME_AND_ZONE"/> <permission name="android.permission.TETHER_PRIVILEGED"/> <permission name="android.permission.USE_RESERVED_DISK"/> <permission name="android.permission.USER_ACTIVITY"/> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index c9097341a18e..0756647d5774 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -155,12 +155,12 @@ applications that come with the platform <permission name="android.permission.REGISTER_CALL_PROVIDER"/> <permission name="android.permission.REGISTER_SIM_SUBSCRIPTION"/> <permission name="android.permission.SEND_RESPOND_VIA_MESSAGE"/> - <permission name="android.permission.SET_TIME"/> <permission name="android.permission.SET_TIME_ZONE"/> <permission name="android.permission.SHUTDOWN"/> <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/> <permission name="android.permission.STATUS_BAR"/> <permission name="android.permission.STOP_APP_SWITCHES"/> + <permission name="android.permission.SUGGEST_PHONE_TIME_AND_ZONE"/> <permission name="android.permission.UPDATE_APP_OPS_STATS"/> <permission name="android.permission.UPDATE_DEVICE_STATS"/> <permission name="android.permission.UPDATE_LOCK"/> diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java index c6515efd2c61..feb6101cb080 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java @@ -176,7 +176,7 @@ abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreC KeyStore keyStore, IBinder operationToken) { KeyStoreCryptoOperationStreamer streamer = new KeyStoreCryptoOperationChunkedStreamer( new KeyStoreCryptoOperationChunkedStreamer.MainDataStream( - keyStore, operationToken)); + keyStore, operationToken), 0); if (isEncrypting()) { return streamer; } else { @@ -191,7 +191,7 @@ abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreC protected final KeyStoreCryptoOperationStreamer createAdditionalAuthenticationDataStreamer( KeyStore keyStore, IBinder operationToken) { return new KeyStoreCryptoOperationChunkedStreamer( - new AdditionalAuthenticationDataStream(keyStore, operationToken)); + new AdditionalAuthenticationDataStream(keyStore, operationToken), 0); } @Override diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java index 5bcb34a67a54..ccc3153749fb 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java @@ -299,7 +299,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor KeyStore keyStore, IBinder operationToken) { return new KeyStoreCryptoOperationChunkedStreamer( new KeyStoreCryptoOperationChunkedStreamer.MainDataStream( - keyStore, operationToken)); + keyStore, operationToken), 0); } /** diff --git a/keystore/java/android/security/keystore/ArrayUtils.java b/keystore/java/android/security/keystore/ArrayUtils.java index 26172d276b8c..f519c7cdd3d2 100644 --- a/keystore/java/android/security/keystore/ArrayUtils.java +++ b/keystore/java/android/security/keystore/ArrayUtils.java @@ -55,6 +55,34 @@ public abstract class ArrayUtils { } } + /** + * Copies a subset of the source array to the destination array. + * Length will be limited to the bounds of source and destination arrays. + * The length actually copied is returned, which will be <= length argument. + * @param src is the source array + * @param srcOffset is the offset in the source array. + * @param dst is the destination array. + * @param dstOffset is the offset in the destination array. + * @param length is the length to be copied from source to destination array. + * @return The length actually copied from source array. + */ + public static int copy(byte[] src, int srcOffset, byte[] dst, int dstOffset, int length) { + if (dst == null || src == null) { + return 0; + } + if (length > dst.length - dstOffset) { + length = dst.length - dstOffset; + } + if (length > src.length - srcOffset) { + length = src.length - srcOffset; + } + if (length <= 0) { + return 0; + } + System.arraycopy(src, srcOffset, dst, dstOffset, length); + return length; + } + public static byte[] subarray(byte[] arr, int offset, int len) { if (len == 0) { return EmptyArray.BYTE; diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java index 75bea26aecef..2c0f40d528d2 100644 --- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java +++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java @@ -24,19 +24,20 @@ import android.security.keymaster.OperationResult; import libcore.util.EmptyArray; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.security.ProviderException; - /** * Helper for streaming a crypto operation's input and output via {@link KeyStore} service's * {@code update} and {@code finish} operations. * - * <p>The helper abstracts away to issues that need to be solved in most code that uses KeyStore's + * <p>The helper abstracts away issues that need to be solved in most code that uses KeyStore's * update and finish operations. Firstly, KeyStore's update operation can consume only a limited * amount of data in one go because the operations are marshalled via Binder. Secondly, the update * operation may consume less data than provided, in which case the caller has to buffer the - * remainder for next time. The helper exposes {@link #update(byte[], int, int) update} and + * remainder for next time. Thirdly, when the input is smaller than a threshold, skipping update + * and passing input data directly to final improves performance. This threshold is configurable; + * using a threshold <= 1 causes the helper act eagerly, which may be required for some types of + * operations (e.g. ciphers). + * + * <p>The helper exposes {@link #update(byte[], int, int) update} and * {@link #doFinal(byte[], int, int, byte[], byte[]) doFinal} operations which can be used to * conveniently implement various JCA crypto primitives. * @@ -67,240 +68,122 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS // Binder buffer is about 1MB, but it's shared between all active transactions of the process. // Thus, it's safer to use a much smaller upper bound. - private static final int DEFAULT_MAX_CHUNK_SIZE = 64 * 1024; + private static final int DEFAULT_CHUNK_SIZE_MAX = 64 * 1024; + // The chunk buffer will be sent to update until its size under this threshold. + // This threshold should be <= the max input allowed for finish. + // Setting this threshold <= 1 will effectivley disable buffering between updates. + private static final int DEFAULT_CHUNK_SIZE_THRESHOLD = 2 * 1024; private final Stream mKeyStoreStream; - private final int mMaxChunkSize; - - private byte[] mBuffered = EmptyArray.BYTE; - private int mBufferedOffset; - private int mBufferedLength; + private final int mChunkSizeMax; + private final int mChunkSizeThreshold; + private final byte[] mChunk; + private int mChunkLength = 0; private long mConsumedInputSizeBytes; private long mProducedOutputSizeBytes; - public KeyStoreCryptoOperationChunkedStreamer(Stream operation) { - this(operation, DEFAULT_MAX_CHUNK_SIZE); + KeyStoreCryptoOperationChunkedStreamer(Stream operation) { + this(operation, DEFAULT_CHUNK_SIZE_THRESHOLD, DEFAULT_CHUNK_SIZE_MAX); } - public KeyStoreCryptoOperationChunkedStreamer(Stream operation, int maxChunkSize) { + KeyStoreCryptoOperationChunkedStreamer(Stream operation, int chunkSizeThreshold) { + this(operation, chunkSizeThreshold, DEFAULT_CHUNK_SIZE_MAX); + } + + KeyStoreCryptoOperationChunkedStreamer(Stream operation, int chunkSizeThreshold, + int chunkSizeMax) { mKeyStoreStream = operation; - mMaxChunkSize = maxChunkSize; + mChunkSizeMax = chunkSizeMax; + if (chunkSizeThreshold <= 0) { + mChunkSizeThreshold = 1; + } else if (chunkSizeThreshold > chunkSizeMax) { + mChunkSizeThreshold = chunkSizeMax; + } else { + mChunkSizeThreshold = chunkSizeThreshold; + } + mChunk = new byte[mChunkSizeMax]; } - @Override public byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyStoreException { - if (inputLength == 0) { + if (inputLength == 0 || input == null) { // No input provided return EmptyArray.BYTE; } + if (inputLength < 0 || inputOffset < 0 || (inputOffset + inputLength) > input.length) { + throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR, + "Input offset and length out of bounds of input array"); + } - ByteArrayOutputStream bufferedOutput = null; + byte[] output = EmptyArray.BYTE; - while (inputLength > 0) { - byte[] chunk; - int inputBytesInChunk; - if ((mBufferedLength + inputLength) > mMaxChunkSize) { - // Too much input for one chunk -- extract one max-sized chunk and feed it into the - // update operation. - inputBytesInChunk = mMaxChunkSize - mBufferedLength; - chunk = ArrayUtils.concat(mBuffered, mBufferedOffset, mBufferedLength, - input, inputOffset, inputBytesInChunk); - } else { - // All of available input fits into one chunk. - if ((mBufferedLength == 0) && (inputOffset == 0) - && (inputLength == input.length)) { - // Nothing buffered and all of input array needs to be fed into the update - // operation. - chunk = input; - inputBytesInChunk = input.length; - } else { - // Need to combine buffered data with input data into one array. - inputBytesInChunk = inputLength; - chunk = ArrayUtils.concat(mBuffered, mBufferedOffset, mBufferedLength, - input, inputOffset, inputBytesInChunk); - } - } - // Update input array references to reflect that some of its bytes are now in mBuffered. - inputOffset += inputBytesInChunk; - inputLength -= inputBytesInChunk; - mConsumedInputSizeBytes += inputBytesInChunk; + while (inputLength > 0 || mChunkLength >= mChunkSizeThreshold) { + int inputConsumed = ArrayUtils.copy(input, inputOffset, mChunk, mChunkLength, + inputLength); + inputLength -= inputConsumed; + inputOffset += inputConsumed; + mChunkLength += inputConsumed; + mConsumedInputSizeBytes += inputConsumed; - OperationResult opResult = mKeyStoreStream.update(chunk); - if (opResult == null) { - throw new KeyStoreConnectException(); - } else if (opResult.resultCode != KeyStore.NO_ERROR) { - throw KeyStore.getKeyStoreException(opResult.resultCode); + if (mChunkLength > mChunkSizeMax) { + throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH, + "Chunk size exceeded max chunk size. Max: " + mChunkSizeMax + + " Actual: " + mChunkLength); } - if (opResult.inputConsumed == chunk.length) { - // The whole chunk was consumed - mBuffered = EmptyArray.BYTE; - mBufferedOffset = 0; - mBufferedLength = 0; - } else if (opResult.inputConsumed <= 0) { - // Nothing was consumed. More input needed. - if (inputLength > 0) { - // More input is available, but it wasn't included into the previous chunk - // because the chunk reached its maximum permitted size. - // Shouldn't have happened. + if (mChunkLength >= mChunkSizeThreshold) { + OperationResult opResult = mKeyStoreStream.update( + ArrayUtils.subarray(mChunk, 0, mChunkLength)); + + if (opResult == null) { + throw new KeyStoreConnectException(); + } else if (opResult.resultCode != KeyStore.NO_ERROR) { + throw KeyStore.getKeyStoreException(opResult.resultCode); + } + if (opResult.inputConsumed <= 0) { + throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH, + "Keystore consumed 0 of " + mChunkLength + " bytes provided."); + } else if (opResult.inputConsumed > mChunkLength) { throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR, - "Keystore consumed nothing from max-sized chunk: " + chunk.length - + " bytes"); + "Keystore consumed more input than provided. Provided: " + + mChunkLength + ", consumed: " + opResult.inputConsumed); } - mBuffered = chunk; - mBufferedOffset = 0; - mBufferedLength = chunk.length; - } else if (opResult.inputConsumed < chunk.length) { - // The chunk was consumed only partially -- buffer the rest of the chunk - mBuffered = chunk; - mBufferedOffset = opResult.inputConsumed; - mBufferedLength = chunk.length - opResult.inputConsumed; - } else { - throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR, - "Keystore consumed more input than provided. Provided: " + chunk.length - + ", consumed: " + opResult.inputConsumed); - } + mChunkLength -= opResult.inputConsumed; - if ((opResult.output != null) && (opResult.output.length > 0)) { - if (inputLength + mBufferedLength > 0) { - // More output might be produced in this loop -- buffer the current output - if (bufferedOutput == null) { - bufferedOutput = new ByteArrayOutputStream(); - } - try { - bufferedOutput.write(opResult.output); - } catch (IOException e) { - throw new ProviderException("Failed to buffer output", e); - } - } else { - // No more output will be produced in this loop - byte[] result; - if (bufferedOutput == null) { - // No previously buffered output - result = opResult.output; - } else { - // There was some previously buffered output - try { - bufferedOutput.write(opResult.output); - } catch (IOException e) { - throw new ProviderException("Failed to buffer output", e); - } - result = bufferedOutput.toByteArray(); - } - mProducedOutputSizeBytes += result.length; - return result; + if (mChunkLength > 0) { + // Partialy consumed, shift chunk contents + ArrayUtils.copy(mChunk, opResult.inputConsumed, mChunk, 0, mChunkLength); } - } - } - byte[] result; - if (bufferedOutput == null) { - // No output produced - result = EmptyArray.BYTE; - } else { - result = bufferedOutput.toByteArray(); + if ((opResult.output != null) && (opResult.output.length > 0)) { + // Output was produced + mProducedOutputSizeBytes += opResult.output.length; + output = ArrayUtils.concat(output, opResult.output); + } + } } - mProducedOutputSizeBytes += result.length; - return result; + return output; } - @Override public byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] signature, byte[] additionalEntropy) throws KeyStoreException { - if (inputLength == 0) { - // No input provided -- simplify the rest of the code - input = EmptyArray.BYTE; - inputOffset = 0; - } - - // Flush all buffered input and provided input into keystore/keymaster. byte[] output = update(input, inputOffset, inputLength); - output = ArrayUtils.concat(output, flush()); + byte[] finalChunk = ArrayUtils.subarray(mChunk, 0, mChunkLength); + OperationResult opResult = mKeyStoreStream.finish(finalChunk, signature, additionalEntropy); - OperationResult opResult = mKeyStoreStream.finish(EmptyArray.BYTE, signature, - additionalEntropy); if (opResult == null) { throw new KeyStoreConnectException(); } else if (opResult.resultCode != KeyStore.NO_ERROR) { throw KeyStore.getKeyStoreException(opResult.resultCode); } - mProducedOutputSizeBytes += opResult.output.length; - - return ArrayUtils.concat(output, opResult.output); - } - - public byte[] flush() throws KeyStoreException { - if (mBufferedLength <= 0) { - return EmptyArray.BYTE; - } - - // Keep invoking the update operation with remaining buffered data until either all of the - // buffered data is consumed or until update fails to consume anything. - ByteArrayOutputStream bufferedOutput = null; - while (mBufferedLength > 0) { - byte[] chunk = ArrayUtils.subarray(mBuffered, mBufferedOffset, mBufferedLength); - OperationResult opResult = mKeyStoreStream.update(chunk); - if (opResult == null) { - throw new KeyStoreConnectException(); - } else if (opResult.resultCode != KeyStore.NO_ERROR) { - throw KeyStore.getKeyStoreException(opResult.resultCode); - } - - if (opResult.inputConsumed <= 0) { - // Nothing was consumed. Break out of the loop to avoid an infinite loop. - break; - } - - if (opResult.inputConsumed >= chunk.length) { - // All of the input was consumed - mBuffered = EmptyArray.BYTE; - mBufferedOffset = 0; - mBufferedLength = 0; - } else { - // Some of the input was not consumed - mBuffered = chunk; - mBufferedOffset = opResult.inputConsumed; - mBufferedLength = chunk.length - opResult.inputConsumed; - } - - if (opResult.inputConsumed > chunk.length) { - throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR, - "Keystore consumed more input than provided. Provided: " - + chunk.length + ", consumed: " + opResult.inputConsumed); - } - - if ((opResult.output != null) && (opResult.output.length > 0)) { - // Some output was produced by this update operation - if (bufferedOutput == null) { - // No output buffered yet. - if (mBufferedLength == 0) { - // No more output will be produced by this flush operation - mProducedOutputSizeBytes += opResult.output.length; - return opResult.output; - } else { - // More output might be produced by this flush operation -- buffer output. - bufferedOutput = new ByteArrayOutputStream(); - } - } - // Buffer the output from this update operation - try { - bufferedOutput.write(opResult.output); - } catch (IOException e) { - throw new ProviderException("Failed to buffer output", e); - } - } - } + // If no error, assume all input consumed + mConsumedInputSizeBytes += finalChunk.length; - if (mBufferedLength > 0) { - throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH, - "Keystore failed to consume last " - + ((mBufferedLength != 1) ? (mBufferedLength + " bytes") : "byte") - + " of input"); + if ((opResult.output != null) && (opResult.output.length > 0)) { + mProducedOutputSizeBytes += opResult.output.length; + output = ArrayUtils.concat(output, opResult.output); } - byte[] result = (bufferedOutput != null) ? bufferedOutput.toByteArray() : EmptyArray.BYTE; - mProducedOutputSizeBytes += result.length; - return result; + return output; } @Override diff --git a/media/java/android/media/tv/DvbDeviceInfo.java b/media/java/android/media/tv/DvbDeviceInfo.java index a574fe18fb63..96c852812a74 100644 --- a/media/java/android/media/tv/DvbDeviceInfo.java +++ b/media/java/android/media/tv/DvbDeviceInfo.java @@ -16,6 +16,8 @@ package android.media.tv; +import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; @@ -26,10 +28,11 @@ import android.util.Log; * * @hide */ +@SystemApi public final class DvbDeviceInfo implements Parcelable { static final String TAG = "DvbDeviceInfo"; - public static final @android.annotation.NonNull Parcelable.Creator<DvbDeviceInfo> CREATOR = + public static final @NonNull Parcelable.Creator<DvbDeviceInfo> CREATOR = new Parcelable.Creator<DvbDeviceInfo>() { @Override public DvbDeviceInfo createFromParcel(Parcel source) { @@ -86,7 +89,7 @@ public final class DvbDeviceInfo implements Parcelable { } @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mAdapterId); dest.writeInt(mDeviceId); } diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index d22a29893540..854ea43f17c3 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -103,6 +103,12 @@ public final class TvInputManager { /** @hide */ @Retention(RetentionPolicy.SOURCE) + @IntDef({DVB_DEVICE_DEMUX, DVB_DEVICE_DVR, DVB_DEVICE_FRONTEND}) + public @interface DvbDeviceType {} + + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) @IntDef({VIDEO_UNAVAILABLE_REASON_UNKNOWN, VIDEO_UNAVAILABLE_REASON_TUNING, VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL, VIDEO_UNAVAILABLE_REASON_BUFFERING, VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY}) @@ -1663,6 +1669,9 @@ public final class TvInputManager { * @return the list of {@link DvbDeviceInfo} objects representing available DVB devices. * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.DVB_DEVICE) + @NonNull public List<DvbDeviceInfo> getDvbDeviceList() { try { return mService.getDvbDeviceList(); @@ -1676,19 +1685,24 @@ public final class TvInputManager { * {@link DvbDeviceInfo} * * @param info A {@link DvbDeviceInfo} to open a DVB device. - * @param device A DVB device. The DVB device can be {@link #DVB_DEVICE_DEMUX}, + * @param deviceType A DVB device type. The type can be {@link #DVB_DEVICE_DEMUX}, * {@link #DVB_DEVICE_DVR} or {@link #DVB_DEVICE_FRONTEND}. * @return a {@link ParcelFileDescriptor} of a specified DVB device for a given - * {@link DvbDeviceInfo}, or {@code null} if the given {@link DvbDeviceInfo} was invalid - * or the specified DVB device was busy with a previous request. + * {@link DvbDeviceInfo}, or {@code null} if the given {@link DvbDeviceInfo} + * failed to open. + * @throws IllegalArgumentException if {@code deviceType} is invalid or the device is not found. * @hide */ - public ParcelFileDescriptor openDvbDevice(DvbDeviceInfo info, int device) { + @SystemApi + @RequiresPermission(android.Manifest.permission.DVB_DEVICE) + @Nullable + public ParcelFileDescriptor openDvbDevice(@NonNull DvbDeviceInfo info, + @DvbDeviceType int deviceType) { try { - if (DVB_DEVICE_START > device || DVB_DEVICE_END < device) { - throw new IllegalArgumentException("Invalid DVB device: " + device); + if (DVB_DEVICE_START > deviceType || DVB_DEVICE_END < deviceType) { + throw new IllegalArgumentException("Invalid DVB device: " + deviceType); } - return mService.openDvbDevice(info, device); + return mService.openDvbDevice(info, deviceType); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java index ea00d6eff12e..c17beba2b9d1 100644 --- a/media/java/android/media/tv/TvTrackInfo.java +++ b/media/java/android/media/tv/TvTrackInfo.java @@ -61,6 +61,8 @@ public final class TvTrackInfo implements Parcelable { private final boolean mEncrypted; private final int mAudioChannelCount; private final int mAudioSampleRate; + private final boolean mAudioDescription; + private final boolean mHardOfHearing; private final int mVideoWidth; private final int mVideoHeight; private final float mVideoFrameRate; @@ -70,9 +72,9 @@ public final class TvTrackInfo implements Parcelable { private final Bundle mExtra; private TvTrackInfo(int type, String id, String language, CharSequence description, - boolean encrypted, int audioChannelCount, int audioSampleRate, int videoWidth, - int videoHeight, float videoFrameRate, float videoPixelAspectRatio, - byte videoActiveFormatDescription, Bundle extra) { + boolean encrypted, int audioChannelCount, int audioSampleRate, boolean audioDescription, + boolean hardOfHearing, int videoWidth, int videoHeight, float videoFrameRate, + float videoPixelAspectRatio, byte videoActiveFormatDescription, Bundle extra) { mType = type; mId = id; mLanguage = language; @@ -80,6 +82,8 @@ public final class TvTrackInfo implements Parcelable { mEncrypted = encrypted; mAudioChannelCount = audioChannelCount; mAudioSampleRate = audioSampleRate; + mAudioDescription = audioDescription; + mHardOfHearing = hardOfHearing; mVideoWidth = videoWidth; mVideoHeight = videoHeight; mVideoFrameRate = videoFrameRate; @@ -96,6 +100,8 @@ public final class TvTrackInfo implements Parcelable { mEncrypted = in.readInt() != 0; mAudioChannelCount = in.readInt(); mAudioSampleRate = in.readInt(); + mAudioDescription = in.readInt() != 0; + mHardOfHearing = in.readInt() != 0; mVideoWidth = in.readInt(); mVideoHeight = in.readInt(); mVideoFrameRate = in.readFloat(); @@ -172,6 +178,40 @@ public final class TvTrackInfo implements Parcelable { } /** + * Returns {@code true} if the track is an audio description intended for people with visual + * impairment, {@code false} otherwise. Valid only for {@link #TYPE_AUDIO} tracks. + * + * <p>For example of broadcast, audio description information may be referred to broadcast + * standard (e.g. ISO 639 Language Descriptor of ISO/IEC 13818-1, Supplementary Audio Language + * Descriptor, AC-3 Descriptor, Enhanced AC-3 Descriptor, AAC Descriptor of ETSI EN 300 468). + * + * @throws IllegalStateException if not called on an audio track + */ + public boolean isAudioDescription() { + if (mType != TYPE_AUDIO) { + throw new IllegalStateException("Not an audio track"); + } + return mAudioDescription; + } + + /** + * Returns {@code true} if the track is intended for people with hearing impairment, {@code + * false} otherwise. Valid only for {@link #TYPE_AUDIO} and {@link #TYPE_SUBTITLE} tracks. + * + * <p>For example of broadcast, hard of hearing information may be referred to broadcast + * standard (e.g. ISO 639 Language Descriptor of ISO/IEC 13818-1, Supplementary Audio Language + * Descriptor, AC-3 Descriptor, Enhanced AC-3 Descriptor, AAC Descriptor of ETSI EN 300 468). + * + * @throws IllegalStateException if not called on an audio track or a subtitle track + */ + public boolean isHardOfHearing() { + if (mType != TYPE_AUDIO && mType != TYPE_SUBTITLE) { + throw new IllegalStateException("Not an audio or a subtitle track"); + } + return mHardOfHearing; + } + + /** * Returns the width of the video, in the unit of pixels. Valid only for {@link #TYPE_VIDEO} * tracks. * @@ -266,6 +306,8 @@ public final class TvTrackInfo implements Parcelable { dest.writeInt(mEncrypted ? 1 : 0); dest.writeInt(mAudioChannelCount); dest.writeInt(mAudioSampleRate); + dest.writeInt(mAudioDescription ? 1 : 0); + dest.writeInt(mHardOfHearing ? 1 : 0); dest.writeInt(mVideoWidth); dest.writeInt(mVideoHeight); dest.writeFloat(mVideoFrameRate); @@ -296,7 +338,9 @@ public final class TvTrackInfo implements Parcelable { switch (mType) { case TYPE_AUDIO: return mAudioChannelCount == obj.mAudioChannelCount - && mAudioSampleRate == obj.mAudioSampleRate; + && mAudioSampleRate == obj.mAudioSampleRate + && mAudioDescription == obj.mAudioDescription + && mHardOfHearing == obj.mHardOfHearing; case TYPE_VIDEO: return mVideoWidth == obj.mVideoWidth @@ -304,6 +348,9 @@ public final class TvTrackInfo implements Parcelable { && mVideoFrameRate == obj.mVideoFrameRate && mVideoPixelAspectRatio == obj.mVideoPixelAspectRatio && mVideoActiveFormatDescription == obj.mVideoActiveFormatDescription; + + case TYPE_SUBTITLE: + return mHardOfHearing == obj.mHardOfHearing; } return true; @@ -338,6 +385,8 @@ public final class TvTrackInfo implements Parcelable { private boolean mEncrypted; private int mAudioChannelCount; private int mAudioSampleRate; + private boolean mAudioDescription; + private boolean mHardOfHearing; private int mVideoWidth; private int mVideoHeight; private float mVideoFrameRate; @@ -430,6 +479,48 @@ public final class TvTrackInfo implements Parcelable { } /** + * Sets the audio description attribute of the audio. Valid only for {@link #TYPE_AUDIO} + * tracks. + * + * <p>For example of broadcast, audio description information may be referred to broadcast + * standard (e.g. ISO 639 Language Descriptor of ISO/IEC 13818-1, Supplementary Audio + * Language Descriptor, AC-3 Descriptor, Enhanced AC-3 Descriptor, AAC Descriptor of ETSI EN + * 300 468). + * + * @param audioDescription The audio description attribute of the audio. + * @throws IllegalStateException if not called on an audio track + */ + @NonNull + public Builder setAudioDescription(boolean audioDescription) { + if (mType != TYPE_AUDIO) { + throw new IllegalStateException("Not an audio track"); + } + mAudioDescription = audioDescription; + return this; + } + + /** + * Sets the hard of hearing attribute of the track. Valid only for {@link #TYPE_AUDIO} and + * {@link #TYPE_SUBTITLE} tracks. + * + * <p>For example of broadcast, hard of hearing information may be referred to broadcast + * standard (e.g. ISO 639 Language Descriptor of ISO/IEC 13818-1, Supplementary Audio + * Language Descriptor, AC-3 Descriptor, Enhanced AC-3 Descriptor, AAC Descriptor of ETSI EN + * 300 468). + * + * @param hardOfHearing The hard of hearing attribute of the track. + * @throws IllegalStateException if not called on an audio track or a subtitle track + */ + @NonNull + public Builder setHardOfHearing(boolean hardOfHearing) { + if (mType != TYPE_AUDIO && mType != TYPE_SUBTITLE) { + throw new IllegalStateException("Not an audio track or a subtitle track"); + } + mHardOfHearing = hardOfHearing; + return this; + } + + /** * Sets the width of the video, in the unit of pixels. Valid only for {@link #TYPE_VIDEO} * tracks. * @@ -531,8 +622,9 @@ public final class TvTrackInfo implements Parcelable { */ public TvTrackInfo build() { return new TvTrackInfo(mType, mId, mLanguage, mDescription, mEncrypted, - mAudioChannelCount, mAudioSampleRate, mVideoWidth, mVideoHeight, - mVideoFrameRate, mVideoPixelAspectRatio, mVideoActiveFormatDescription, mExtra); + mAudioChannelCount, mAudioSampleRate, mAudioDescription, mHardOfHearing, + mVideoWidth, mVideoHeight, mVideoFrameRate, mVideoPixelAspectRatio, + mVideoActiveFormatDescription, mExtra); } } } diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index 8d420e2c5598..05aaa82f8ac8 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -743,8 +743,10 @@ status_t JMediaCodec::getCodecInfo(JNIEnv *env, jobject *codecInfoObject) const } status_t JMediaCodec::getMetrics(JNIEnv *, MediaAnalyticsItem * &reply) const { - - status_t status = mCodec->getMetrics(reply); + mediametrics_handle_t reply2 = MediaAnalyticsItem::convert(reply); + status_t status = mCodec->getMetrics(reply2); + // getMetrics() updates reply2, pass the converted update along to our caller. + reply = MediaAnalyticsItem::convert(reply2); return status; } @@ -1848,7 +1850,7 @@ android_media_MediaCodec_native_getMetrics(JNIEnv *env, jobject thiz) } // get what we have for the metrics from the codec - MediaAnalyticsItem *item = NULL; + MediaAnalyticsItem *item = 0; status_t err = codec->getMetrics(env, item); if (err != OK) { @@ -1860,7 +1862,7 @@ android_media_MediaCodec_native_getMetrics(JNIEnv *env, jobject thiz) // housekeeping delete item; - item = NULL; + item = 0; return mybundle; } diff --git a/mms/java/android/telephony/MmsManager.java b/mms/java/android/telephony/MmsManager.java index 4bcf04691652..65542673a607 100644 --- a/mms/java/android/telephony/MmsManager.java +++ b/mms/java/android/telephony/MmsManager.java @@ -97,22 +97,4 @@ public class MmsManager { // Ignore it } } - - /** - * Get carrier-dependent configuration values. - * - * @param subId the subscription id - * @return bundle key/values pairs of configuration values - */ - public Bundle getCarrierConfigValues(int subId) { - try { - IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); - if (iMms != null) { - return iMms.getCarrierConfigValues(subId); - } - } catch (RemoteException ex) { - // ignore it - } - return null; - } } diff --git a/mms/java/com/android/internal/telephony/IMms.aidl b/mms/java/com/android/internal/telephony/IMms.aidl index fa5073ef1c7e..8be511186800 100644 --- a/mms/java/com/android/internal/telephony/IMms.aidl +++ b/mms/java/com/android/internal/telephony/IMms.aidl @@ -60,13 +60,6 @@ interface IMms { in PendingIntent downloadedIntent); /** - * Get carrier-dependent configuration values. - * - * @param subId the SIM id - */ - Bundle getCarrierConfigValues(int subId); - - /** * Import a text message into system's SMS store * * @param callingPkg the calling app's package name diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java index 9e49826f70c3..c2ce84023869 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java @@ -262,10 +262,11 @@ public class DynamicSystemInstallationService extends Service return; } + stopForeground(true); mJustCancelledByUser = true; if (mInstallTask.cancel(false)) { - // Will cleanup and post status in onResult() + // Will stopSelf() in onResult() Log.d(TAG, "Cancel request filed successfully"); } else { Log.e(TAG, "Trying to cancel installation while it's already completed."); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java index 4700baae8fab..18d436ff7659 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java @@ -421,7 +421,7 @@ public class NotificationGuts extends FrameLayout { } /** Listener for animations executed in {@link #animateClose(int, int, boolean)}. */ - private static class AnimateCloseListener extends AnimatorListenerAdapter { + private class AnimateCloseListener extends AnimatorListenerAdapter { final View mView; private final GutsContent mGutsContent; @@ -433,8 +433,10 @@ public class NotificationGuts extends FrameLayout { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); - mView.setVisibility(View.GONE); - mGutsContent.onFinishedClosing(); + if (!isExposed()) { + mView.setVisibility(View.GONE); + mGutsContent.onFinishedClosing(); + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 8f7671a5dd96..719ec3215d42 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -100,6 +100,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx protected String mKeyToRemoveOnGutsClosed; private StatusBar mStatusBar; + private Runnable mOpenRunnable; @Inject public NotificationGutsManager( @@ -343,6 +344,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls, int x, int y, boolean resetMenu) { if (mNotificationGutsExposed != null) { + mNotificationGutsExposed.removeCallbacks(mOpenRunnable); mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force); } if (resetMenu) { @@ -445,7 +447,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx // ensure that it's laid but not visible until actually laid out guts.setVisibility(View.INVISIBLE); // Post to ensure the the guts are properly laid out. - guts.post(new Runnable() { + mOpenRunnable = new Runnable() { @Override public void run() { if (row.getWindowToken() == null) { @@ -470,7 +472,8 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mListContainer.onHeightChanged(row, true /* needsAnimation */); mGutsMenuItem = menuItem; } - }); + }; + guts.post(mOpenRunnable); return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt index f4635d1270a8..f7b8a2e29129 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt @@ -67,6 +67,9 @@ class KeyguardLiftController constructor( } private fun updateListeningState() { + if (pickupSensor == null) { + return + } val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible && !statusBarStateController.isDozing diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 0c47d1468a7f..68ee8bbbb69b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -62,6 +62,8 @@ import com.android.systemui.statusbar.policy.KeyguardMonitorImpl; import java.io.PrintWriter; import java.util.ArrayList; +import androidx.annotation.VisibleForTesting; + /** * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back * via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done, @@ -161,6 +163,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private boolean mLastLockVisible; private OnDismissAction mAfterKeyguardGoneAction; + private Runnable mKeyguardGoneCancelAction; private final ArrayList<Runnable> mAfterKeyguardGoneRunnables = new ArrayList<>(); // Dismiss action to be launched when we stop dozing or the keyguard is gone. @@ -328,10 +331,20 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb return false; } - private void hideBouncer(boolean destroyView) { + @VisibleForTesting + void hideBouncer(boolean destroyView) { if (mBouncer == null) { return; } + if (mShowing) { + // If we were showing the bouncer and then aborting, we need to also clear out any + // potential actions unless we actually unlocked. + mAfterKeyguardGoneAction = null; + if (mKeyguardGoneCancelAction != null) { + mKeyguardGoneCancelAction.run(); + mKeyguardGoneCancelAction = null; + } + } mBouncer.hide(destroyView); cancelPendingWakeupAction(); } @@ -364,6 +377,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mBouncer.showWithDismissAction(r, cancelAction); } else { mAfterKeyguardGoneAction = r; + mKeyguardGoneCancelAction = cancelAction; mBouncer.show(false /* resetSecuritySelection */); } } @@ -671,6 +685,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mAfterKeyguardGoneAction.onDismiss(); mAfterKeyguardGoneAction = null; } + mKeyguardGoneCancelAction = null; for (int i = 0; i < mAfterKeyguardGoneRunnables.size(); i++) { mAfterKeyguardGoneRunnables.get(i).run(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 63f653b0b303..0da0e7647707 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -221,6 +221,31 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { verify(mStatusBar, never()).animateKeyguardUnoccluding(); } + @Test + public void testHiding_cancelsGoneRunnable() { + OnDismissAction action = mock(OnDismissAction.class); + Runnable cancelAction = mock(Runnable.class); + mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction, + true /* afterKeyguardGone */); + + mStatusBarKeyguardViewManager.hideBouncer(true); + mStatusBarKeyguardViewManager.hide(0, 30); + verify(action, never()).onDismiss(); + verify(cancelAction).run(); + } + + @Test + public void testHiding_doesntCancelWhenShowing() { + OnDismissAction action = mock(OnDismissAction.class); + Runnable cancelAction = mock(Runnable.class); + mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction, + true /* afterKeyguardGone */); + + mStatusBarKeyguardViewManager.hide(0, 30); + verify(action).onDismiss(); + verify(cancelAction, never()).run(); + } + private class TestableStatusBarKeyguardViewManager extends StatusBarKeyguardViewManager { public TestableStatusBarKeyguardViewManager(Context context, diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp index 08552cbfd394..596f62fad23e 100644 --- a/packages/Tethering/Android.bp +++ b/packages/Tethering/Android.bp @@ -45,9 +45,9 @@ android_library { // Due to b/143733063, APK can't access a jni lib that is in APEX (but not in the APK). cc_library { - name: "libtetheroffloadjni", + name: "libtetherutilsjni", srcs: [ - "jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp", + "jni/android_net_util_TetheringUtils.cpp", ], shared_libs: [ "libcgrouprc", @@ -87,7 +87,7 @@ java_defaults { "libcgrouprc", "libnativehelper_compat_libc++", "libvndksupport", - "libtetheroffloadjni", + "libtetherutilsjni", ], resource_dirs: [ "res", diff --git a/packages/Tethering/jarjar-rules.txt b/packages/Tethering/jarjar-rules.txt index dd9eab74e851..d93531bac58e 100644 --- a/packages/Tethering/jarjar-rules.txt +++ b/packages/Tethering/jarjar-rules.txt @@ -13,3 +13,5 @@ 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 android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1 + +rule android.net.shared.Inet4AddressUtils* com.android.networkstack.tethering.shared.Inet4AddressUtils@1 diff --git a/packages/Tethering/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp b/packages/Tethering/jni/android_net_util_TetheringUtils.cpp index 663154a49048..1cf8f988432c 100644 --- a/packages/Tethering/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp +++ b/packages/Tethering/jni/android_net_util_TetheringUtils.cpp @@ -19,13 +19,16 @@ #include <hidl/HidlSupport.h> #include <jni.h> #include <nativehelper/JNIHelp.h> +#include <nativehelper/ScopedUtfChars.h> #include <linux/netfilter/nfnetlink.h> #include <linux/netlink.h> +#include <net/if.h> +#include <netinet/icmp6.h> #include <sys/socket.h> #include <android-base/unique_fd.h> #include <android/hardware/tetheroffload/config/1.0/IOffloadConfig.h> -#define LOG_TAG "OffloadHardwareInterface" +#define LOG_TAG "TetheringUtils" #include <utils/Log.h> namespace android { @@ -87,7 +90,7 @@ hidl_handle handleFromFileDescriptor(base::unique_fd fd) { } // namespace -static jboolean android_server_connectivity_tethering_OffloadHardwareInterface_configOffload( +static jboolean android_net_util_configOffload( JNIEnv* /* env */) { sp<IOffloadConfig> configInterface = IOffloadConfig::getService(); if (configInterface.get() == nullptr) { @@ -130,18 +133,109 @@ static jboolean android_server_connectivity_tethering_OffloadHardwareInterface_c return rval; } +static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd, + jint ifIndex) +{ + static const int kLinkLocalHopLimit = 255; + + int fd = jniGetFDFromFileDescriptor(env, javaFd); + + // Set an ICMPv6 filter that only passes Router Solicitations. + struct icmp6_filter rs_only; + ICMP6_FILTER_SETBLOCKALL(&rs_only); + ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &rs_only); + socklen_t len = sizeof(rs_only); + if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &rs_only, len) != 0) { + jniThrowExceptionFmt(env, "java/net/SocketException", + "setsockopt(ICMP6_FILTER): %s", strerror(errno)); + return; + } + + // Most/all of the rest of these options can be set via Java code, but + // because we're here on account of setting an icmp6_filter go ahead + // and do it all natively for now. + + // Set the multicast hoplimit to 255 (link-local only). + int hops = kLinkLocalHopLimit; + len = sizeof(hops); + if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, len) != 0) { + jniThrowExceptionFmt(env, "java/net/SocketException", + "setsockopt(IPV6_MULTICAST_HOPS): %s", strerror(errno)); + return; + } + + // Set the unicast hoplimit to 255 (link-local only). + hops = kLinkLocalHopLimit; + len = sizeof(hops); + if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, len) != 0) { + jniThrowExceptionFmt(env, "java/net/SocketException", + "setsockopt(IPV6_UNICAST_HOPS): %s", strerror(errno)); + return; + } + + // Explicitly disable multicast loopback. + int off = 0; + len = sizeof(off); + if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, len) != 0) { + jniThrowExceptionFmt(env, "java/net/SocketException", + "setsockopt(IPV6_MULTICAST_LOOP): %s", strerror(errno)); + return; + } + + // Specify the IPv6 interface to use for outbound multicast. + len = sizeof(ifIndex); + if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifIndex, len) != 0) { + jniThrowExceptionFmt(env, "java/net/SocketException", + "setsockopt(IPV6_MULTICAST_IF): %s", strerror(errno)); + return; + } + + // Additional options to be considered: + // - IPV6_TCLASS + // - IPV6_RECVPKTINFO + // - IPV6_RECVHOPLIMIT + + // Bind to [::]. + const struct sockaddr_in6 sin6 = { + .sin6_family = AF_INET6, + .sin6_port = 0, + .sin6_flowinfo = 0, + .sin6_addr = IN6ADDR_ANY_INIT, + .sin6_scope_id = 0, + }; + auto sa = reinterpret_cast<const struct sockaddr *>(&sin6); + len = sizeof(sin6); + if (bind(fd, sa, len) != 0) { + jniThrowExceptionFmt(env, "java/net/SocketException", + "bind(IN6ADDR_ANY): %s", strerror(errno)); + return; + } + + // Join the all-routers multicast group, ff02::2%index. + struct ipv6_mreq all_rtrs = { + .ipv6mr_multiaddr = {{{0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2}}}, + .ipv6mr_interface = ifIndex, + }; + len = sizeof(all_rtrs); + if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &all_rtrs, len) != 0) { + jniThrowExceptionFmt(env, "java/net/SocketException", + "setsockopt(IPV6_JOIN_GROUP): %s", strerror(errno)); + return; + } +} + /* * JNI registration. */ static const JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ - { "configOffload", "()Z", - (void*) android_server_connectivity_tethering_OffloadHardwareInterface_configOffload }, + { "configOffload", "()Z", (void*) android_net_util_configOffload }, + { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_util_setupRaSocket }, }; -int register_android_server_connectivity_tethering_OffloadHardwareInterface(JNIEnv* env) { +int register_android_net_util_TetheringUtils(JNIEnv* env) { return jniRegisterNativeMethods(env, - "com/android/server/connectivity/tethering/OffloadHardwareInterface", + "android/net/util/TetheringUtils", gMethods, NELEM(gMethods)); } @@ -152,7 +246,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void*) { return JNI_ERR; } - if (register_android_server_connectivity_tethering_OffloadHardwareInterface(env) < 0) { + if (register_android_net_util_TetheringUtils(env) < 0) { return JNI_ERR; } diff --git a/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java b/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java index 1fe2328f1cdb..d6bc063210b3 100644 --- a/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java +++ b/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java @@ -20,11 +20,11 @@ import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH; import android.annotation.NonNull; import android.net.LinkAddress; - -import com.google.android.collect.Sets; +import android.util.ArraySet; import java.net.Inet4Address; import java.util.Collection; +import java.util.Collections; import java.util.Set; /** @@ -68,7 +68,7 @@ public class DhcpServingParamsParcelExt extends DhcpServingParamsParcel { * but it must always be set explicitly. */ public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Inet4Address... defaultRouters) { - return setDefaultRouters(Sets.newArraySet(defaultRouters)); + return setDefaultRouters(newArraySet(defaultRouters)); } /** @@ -96,7 +96,7 @@ public class DhcpServingParamsParcelExt extends DhcpServingParamsParcel { * <p>This may be an empty list of servers, but it must always be set explicitly. */ public DhcpServingParamsParcelExt setDnsServers(@NonNull Inet4Address... dnsServers) { - return setDnsServers(Sets.newArraySet(dnsServers)); + return setDnsServers(newArraySet(dnsServers)); } /** @@ -126,7 +126,7 @@ public class DhcpServingParamsParcelExt extends DhcpServingParamsParcel { * and do not need to be set here. */ public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Inet4Address... excludedAddrs) { - return setExcludedAddrs(Sets.newArraySet(excludedAddrs)); + return setExcludedAddrs(newArraySet(excludedAddrs)); } /** @@ -169,4 +169,10 @@ public class DhcpServingParamsParcelExt extends DhcpServingParamsParcel { } return res; } + + private static ArraySet<Inet4Address> newArraySet(Inet4Address... addrs) { + ArraySet<Inet4Address> addrSet = new ArraySet<>(addrs.length); + Collections.addAll(addrSet, addrs); + return addrSet; + } } diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java index 8fde52040ecc..abfb33c7af9e 100644 --- a/packages/Tethering/src/android/net/ip/IpServer.java +++ b/packages/Tethering/src/android/net/ip/IpServer.java @@ -17,10 +17,12 @@ package android.net.ip; import static android.net.InetAddresses.parseNumericAddress; +import static android.net.RouteInfo.RTN_UNICAST; import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; import static android.net.util.NetworkConstants.FF; import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH; import static android.net.util.NetworkConstants.asByte; +import static android.net.util.TetheringMessageBase.BASE_IPSERVER; import android.net.ConnectivityManager; import android.net.INetd; @@ -46,11 +48,9 @@ import android.os.Message; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.util.Log; -import android.util.Slog; import android.util.SparseArray; import com.android.internal.util.MessageUtils; -import com.android.internal.util.Protocol; import com.android.internal.util.State; import com.android.internal.util.StateMachine; @@ -153,27 +153,26 @@ public class IpServer extends StateMachine { DhcpServerCallbacks cb); } - private static final int BASE_IFACE = Protocol.BASE_TETHERING + 100; // request from the user that it wants to tether - public static final int CMD_TETHER_REQUESTED = BASE_IFACE + 2; + public static final int CMD_TETHER_REQUESTED = BASE_IPSERVER + 1; // request from the user that it wants to untether - public static final int CMD_TETHER_UNREQUESTED = BASE_IFACE + 3; + public static final int CMD_TETHER_UNREQUESTED = BASE_IPSERVER + 2; // notification that this interface is down - public static final int CMD_INTERFACE_DOWN = BASE_IFACE + 4; + public static final int CMD_INTERFACE_DOWN = BASE_IPSERVER + 3; // notification from the master SM that it had trouble enabling IP Forwarding - public static final int CMD_IP_FORWARDING_ENABLE_ERROR = BASE_IFACE + 7; + public static final int CMD_IP_FORWARDING_ENABLE_ERROR = BASE_IPSERVER + 4; // notification from the master SM that it had trouble disabling IP Forwarding - public static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IFACE + 8; + public static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IPSERVER + 5; // notification from the master SM that it had trouble starting tethering - public static final int CMD_START_TETHERING_ERROR = BASE_IFACE + 9; + public static final int CMD_START_TETHERING_ERROR = BASE_IPSERVER + 6; // notification from the master SM that it had trouble stopping tethering - public static final int CMD_STOP_TETHERING_ERROR = BASE_IFACE + 10; + public static final int CMD_STOP_TETHERING_ERROR = BASE_IPSERVER + 7; // notification from the master SM that it had trouble setting the DNS forwarders - public static final int CMD_SET_DNS_FORWARDERS_ERROR = BASE_IFACE + 11; + public static final int CMD_SET_DNS_FORWARDERS_ERROR = BASE_IPSERVER + 8; // the upstream connection has changed - public static final int CMD_TETHER_CONNECTION_CHANGED = BASE_IFACE + 12; + public static final int CMD_TETHER_CONNECTION_CHANGED = BASE_IPSERVER + 9; // new IPv6 tethering parameters need to be processed - public static final int CMD_IPV6_TETHER_UPDATE = BASE_IFACE + 13; + public static final int CMD_IPV6_TETHER_UPDATE = BASE_IPSERVER + 10; private final State mInitialState; private final State mLocalHotspotState; @@ -486,7 +485,9 @@ public class IpServer extends StateMachine { } // Directly-connected route. - final RouteInfo route = new RouteInfo(linkAddr); + final IpPrefix ipv4Prefix = new IpPrefix(linkAddr.getAddress(), + linkAddr.getPrefixLength()); + final RouteInfo route = new RouteInfo(ipv4Prefix, null, null, RTN_UNICAST); if (enabled) { mLinkProperties.addLinkAddress(linkAddr); mLinkProperties.addRoute(route); @@ -1007,7 +1008,7 @@ public class IpServer extends StateMachine { String ifname, HashSet<IpPrefix> prefixes) { final ArrayList<RouteInfo> localRoutes = new ArrayList<RouteInfo>(); for (IpPrefix ipp : prefixes) { - localRoutes.add(new RouteInfo(ipp, null, ifname)); + localRoutes.add(new RouteInfo(ipp, null, ifname, RTN_UNICAST)); } return localRoutes; } @@ -1019,7 +1020,7 @@ public class IpServer extends StateMachine { try { return Inet6Address.getByAddress(null, dnsBytes, 0); } catch (UnknownHostException e) { - Slog.wtf(TAG, "Failed to construct Inet6Address from: " + localPrefix); + Log.wtf(TAG, "Failed to construct Inet6Address from: " + localPrefix); return null; } } diff --git a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java index 414741309ef0..bba61d72d8d6 100644 --- a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java +++ b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java @@ -22,14 +22,14 @@ import static android.system.OsConstants.AF_INET6; import static android.system.OsConstants.IPPROTO_ICMPV6; import static android.system.OsConstants.SOCK_RAW; import static android.system.OsConstants.SOL_SOCKET; -import static android.system.OsConstants.SO_BINDTODEVICE; import static android.system.OsConstants.SO_SNDTIMEO; import android.net.IpPrefix; import android.net.LinkAddress; -import android.net.NetworkUtils; import android.net.TrafficStats; import android.net.util.InterfaceParams; +import android.net.util.SocketUtils; +import android.net.util.TetheringUtils; import android.system.ErrnoException; import android.system.Os; import android.system.StructTimeval; @@ -38,8 +38,6 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.TrafficStatsConstants; -import libcore.io.IoBridge; - import java.io.FileDescriptor; import java.io.IOException; import java.net.Inet6Address; @@ -611,9 +609,8 @@ public class RouterAdvertisementDaemon { // Setting SNDTIMEO is purely for defensive purposes. Os.setsockoptTimeval( mSocket, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(send_timout_ms)); - Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mInterface.name); - NetworkUtils.protectFromVpn(mSocket); - NetworkUtils.setupRaSocket(mSocket, mInterface.index); + SocketUtils.bindSocketToInterface(mSocket, mInterface.name); + TetheringUtils.setupRaSocket(mSocket, mInterface.index); } catch (ErrnoException | IOException e) { Log.e(TAG, "Failed to create RA daemon socket: " + e); return false; @@ -627,7 +624,7 @@ public class RouterAdvertisementDaemon { private void closeSocket() { if (mSocket != null) { try { - IoBridge.closeAndSignalBlockedThreads(mSocket); + SocketUtils.closeSocket(mSocket); } catch (IOException ignored) { } } mSocket = null; diff --git a/packages/Tethering/src/android/net/util/TetheringMessageBase.java b/packages/Tethering/src/android/net/util/TetheringMessageBase.java new file mode 100644 index 000000000000..1b763ce920da --- /dev/null +++ b/packages/Tethering/src/android/net/util/TetheringMessageBase.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.net.util; + +/** + * This class defines Message.what base addresses for various state machine. + */ +public class TetheringMessageBase { + public static final int BASE_MASTER = 0; + public static final int BASE_IPSERVER = 100; + +} diff --git a/packages/Tethering/src/android/net/util/TetheringUtils.java b/packages/Tethering/src/android/net/util/TetheringUtils.java new file mode 100644 index 000000000000..fa543bdce735 --- /dev/null +++ b/packages/Tethering/src/android/net/util/TetheringUtils.java @@ -0,0 +1,49 @@ +/* + * 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.util; + +import java.io.FileDescriptor; +import java.net.SocketException; + +/** + * Native methods for tethering utilization. + * + * {@hide} + */ +public class TetheringUtils { + + /** + * Offload management process need to know conntrack rules to support NAT, but it may not have + * permission to create netlink netfilter sockets. Create two netlink netfilter sockets and + * share them with offload management process. + */ + public static native boolean configOffload(); + + /** + * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements. + * @param fd the socket's {@link FileDescriptor}. + * @param ifIndex the interface index. + */ + public static native void setupRaSocket(FileDescriptor fd, int ifIndex) + throws SocketException; + + /** + * Read s as an unsigned 16-bit integer. + */ + public static int uint16(short s) { + return s & 0xffff; + } +} diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java index ba5d08dce4ca..7e685fbe97f1 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java @@ -38,7 +38,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; import android.net.util.SharedLog; -import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -48,7 +47,6 @@ import android.os.PersistableBundle; import android.os.ResultReceiver; import android.os.SystemClock; import android.os.SystemProperties; -import android.os.UserHandle; import android.provider.Settings; import android.telephony.CarrierConfigManager; import android.util.ArraySet; @@ -196,9 +194,9 @@ public class EntitlementManager { // till upstream change to cellular. if (mUsingCellularAsUpstream) { if (showProvisioningUi) { - runUiTetherProvisioning(type, config.subId); + runUiTetherProvisioning(type, config.activeDataSubId); } else { - runSilentTetherProvisioning(type, config.subId); + runSilentTetherProvisioning(type, config.activeDataSubId); } mNeedReRunProvisioningUi = false; } else { @@ -270,9 +268,9 @@ public class EntitlementManager { if (mCellularPermitted.indexOfKey(downstream) < 0) { if (mNeedReRunProvisioningUi) { mNeedReRunProvisioningUi = false; - runUiTetherProvisioning(downstream, config.subId); + runUiTetherProvisioning(downstream, config.activeDataSubId); } else { - runSilentTetherProvisioning(downstream, config.subId); + runSilentTetherProvisioning(downstream, config.activeDataSubId); } } } @@ -336,7 +334,8 @@ public class EntitlementManager { .getSystemService(Context.CARRIER_CONFIG_SERVICE); if (configManager == null) return null; - final PersistableBundle carrierConfig = configManager.getConfigForSubId(config.subId); + final PersistableBundle carrierConfig = configManager.getConfigForSubId( + config.activeDataSubId); if (CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfig)) { return carrierConfig; @@ -379,12 +378,9 @@ public class EntitlementManager { intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); intent.putExtra(EXTRA_SUBID, subId); intent.setComponent(TETHER_SERVICE); - final long ident = Binder.clearCallingIdentity(); - try { - mContext.startServiceAsUser(intent, UserHandle.CURRENT); - } finally { - Binder.restoreCallingIdentity(ident); - } + // Only admin user can change tethering and SilentTetherProvisioning don't need to + // show UI, it is fine to always start setting's background service as system user. + mContext.startService(intent); } private void runUiTetherProvisioning(int type, int subId) { @@ -407,12 +403,9 @@ public class EntitlementManager { intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); intent.putExtra(EXTRA_SUBID, subId); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - final long ident = Binder.clearCallingIdentity(); - try { - mContext.startActivityAsUser(intent, UserHandle.CURRENT); - } finally { - Binder.restoreCallingIdentity(ident); - } + // Only launch entitlement UI for system user. Entitlement UI should not appear for other + // user because only admin user is allowed to change tethering. + mContext.startActivity(intent); } // Not needed to check if this don't run on the handler thread because it's private. @@ -671,7 +664,7 @@ public class EntitlementManager { receiver.send(cacheValue, null); } else { ResultReceiver proxy = buildProxyReceiver(downstream, false/* notifyFail */, receiver); - runUiTetherProvisioning(downstream, config.subId, proxy); + runUiTetherProvisioning(downstream, config.activeDataSubId, proxy); } } } diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/packages/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java index 93054140213d..66b9ade81019 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java @@ -29,6 +29,7 @@ import android.util.Log; import java.net.Inet6Address; import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; @@ -257,7 +258,7 @@ public class IPv6TetheringCoordinator { final LinkProperties lp = new LinkProperties(); final IpPrefix local48 = makeUniqueLocalPrefix(ulp, (short) 0, 48); - lp.addRoute(new RouteInfo(local48, null, null)); + lp.addRoute(new RouteInfo(local48, null, null, RouteInfo.RTN_UNICAST)); final IpPrefix local64 = makeUniqueLocalPrefix(ulp, subnetId, 64); // Because this is a locally-generated ULA, we don't have an upstream @@ -273,7 +274,13 @@ public class IPv6TetheringCoordinator { final byte[] bytes = Arrays.copyOf(in6addr, in6addr.length); bytes[7] = (byte) (subnetId >> 8); bytes[8] = (byte) subnetId; - return new IpPrefix(bytes, prefixlen); + final InetAddress addr; + try { + addr = InetAddress.getByAddress(bytes); + } catch (UnknownHostException e) { + throw new IllegalStateException("Invalid address length: " + bytes.length, e); + } + return new IpPrefix(addr, prefixlen); } // Generates a Unique Locally-assigned Prefix: diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java index 16734d83e3aa..38fa91e7387e 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java @@ -25,6 +25,7 @@ import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; import android.content.ContentResolver; import android.net.ITetheringStatsProvider; +import android.net.InetAddresses; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; @@ -33,7 +34,6 @@ import android.net.RouteInfo; import android.net.netlink.ConntrackMessage; import android.net.netlink.NetlinkConstants; import android.net.netlink.NetlinkSocket; -import android.net.util.IpUtils; import android.net.util.SharedLog; import android.os.Handler; import android.os.INetworkManagementService; @@ -477,9 +477,10 @@ public class OffloadController { if (!ri.hasGateway()) continue; final String gateway = ri.getGateway().getHostAddress(); - if (ri.isIPv4Default()) { + final InetAddress address = ri.getDestination().getAddress(); + if (ri.isDefaultRoute() && address instanceof Inet4Address) { v4gateway = gateway; - } else if (ri.isIPv6Default()) { + } else if (ri.isDefaultRoute() && address instanceof Inet6Address) { v6gateways.add(gateway); } } @@ -547,7 +548,10 @@ public class OffloadController { private static boolean shouldIgnoreDownstreamRoute(RouteInfo route) { // Ignore any link-local routes. - if (!route.getDestinationLinkAddress().isGlobalPreferred()) return true; + final IpPrefix destination = route.getDestination(); + final LinkAddress linkAddr = new LinkAddress(destination.getAddress(), + destination.getPrefixLength()); + if (!linkAddr.isGlobalPreferred()) return true; return false; } @@ -588,7 +592,7 @@ public class OffloadController { return; } - if (!IpUtils.isValidUdpOrTcpPort(srcPort)) { + if (!isValidUdpOrTcpPort(srcPort)) { mLog.e("Invalid src port: " + srcPort); return; } @@ -599,7 +603,7 @@ public class OffloadController { return; } - if (!IpUtils.isValidUdpOrTcpPort(dstPort)) { + if (!isValidUdpOrTcpPort(dstPort)) { mLog.e("Invalid dst port: " + dstPort); return; } @@ -628,7 +632,7 @@ public class OffloadController { private static Inet4Address parseIPv4Address(String addrString) { try { - final InetAddress ip = InetAddress.parseNumericAddress(addrString); + final InetAddress ip = InetAddresses.parseNumericAddress(addrString); // TODO: Consider other sanitization steps here, including perhaps: // not eql to 0.0.0.0 // not within 169.254.0.0/16 @@ -668,4 +672,8 @@ public class OffloadController { return 180; } } + + private static boolean isValidUdpOrTcpPort(int port) { + return port > 0 && port < 65536; + } } diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java index 00a677338b1b..4a8ef1f92754 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java @@ -16,7 +16,7 @@ package com.android.server.connectivity.tethering; -import static com.android.internal.util.BitUtils.uint16; +import static android.net.util.TetheringUtils.uint16; import android.hardware.tetheroffload.control.V1_0.IOffloadControl; import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback; @@ -24,6 +24,7 @@ import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate; import android.hardware.tetheroffload.control.V1_0.NetworkProtocol; import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent; import android.net.util.SharedLog; +import android.net.util.TetheringUtils; import android.os.Handler; import android.os.RemoteException; import android.system.OsConstants; @@ -47,8 +48,6 @@ public class OffloadHardwareInterface { private static final String NO_IPV4_ADDRESS = ""; private static final String NO_IPV4_GATEWAY = ""; - private static native boolean configOffload(); - private final Handler mHandler; private final SharedLog mLog; private IOffloadControl mOffloadControl; @@ -107,8 +106,6 @@ public class OffloadHardwareInterface { public OffloadHardwareInterface(Handler h, SharedLog log) { mHandler = h; mLog = log.forSubComponent(TAG); - - System.loadLibrary("tetheroffloadjni"); } /** Get default value indicating whether offload is supported. */ @@ -118,7 +115,7 @@ public class OffloadHardwareInterface { /** Configure offload management process. */ public boolean initOffloadConfig() { - return configOffload(); + return TetheringUtils.configOffload(); } /** Initialize the tethering offload HAL. */ diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 3116321e0954..2c8858eeafd3 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -37,6 +37,7 @@ import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL; import static android.net.ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE; import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE; +import static android.net.util.TetheringMessageBase.BASE_MASTER; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; @@ -106,7 +107,6 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.MessageUtils; -import com.android.internal.util.Protocol; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.networkstack.tethering.R; @@ -120,6 +120,8 @@ import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; /** * @@ -185,10 +187,10 @@ public class Tethering { private final TetheringDependencies mDeps; private final EntitlementManager mEntitlementMgr; private final Handler mHandler; - private final PhoneStateListener mPhoneStateListener; private final INetd mNetd; private final NetdCallback mNetdCallback; private final UserRestrictionActionListener mTetheringRestriction; + private final ActiveDataSubIdListener mActiveDataSubIdListener; private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID; // All the usage of mTetheringEventCallback should run in the same thread. private ITetheringEventCallback mTetheringEventCallback = null; @@ -252,26 +254,6 @@ public class Tethering { mEntitlementMgr.reevaluateSimCardProvisioning(mConfig); }); - mPhoneStateListener = new PhoneStateListener(mLooper) { - @Override - public void onActiveDataSubscriptionIdChanged(int subId) { - mLog.log("OBSERVED active data subscription change, from " + mActiveDataSubId - + " to " + subId); - if (subId == mActiveDataSubId) return; - - mActiveDataSubId = subId; - updateConfiguration(); - // To avoid launching unexpected provisioning checks, ignore re-provisioning when - // no CarrierConfig loaded yet. Assume reevaluateSimCardProvisioning() will be - // triggered again when CarrierConfig is loaded. - if (mEntitlementMgr.getCarrierConfig(mConfig) != null) { - mEntitlementMgr.reevaluateSimCardProvisioning(mConfig); - } else { - mLog.log("IGNORED reevaluate provisioning due to no carrier config loaded"); - } - } - }; - mStateReceiver = new StateReceiver(); mNetdCallback = new NetdCallback(); @@ -284,6 +266,8 @@ public class Tethering { final UserManager userManager = (UserManager) mContext.getSystemService( Context.USER_SERVICE); mTetheringRestriction = new UserRestrictionActionListener(userManager, this); + final TetheringThreadExecutor executor = new TetheringThreadExecutor(mHandler); + mActiveDataSubIdListener = new ActiveDataSubIdListener(executor); // Load tethering configuration. updateConfiguration(); @@ -294,8 +278,8 @@ public class Tethering { private void startStateMachineUpdaters(Handler handler) { mCarrierConfigChange.startListening(); - mContext.getSystemService(TelephonyManager.class).listen( - mPhoneStateListener, PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE); + mContext.getSystemService(TelephonyManager.class).listen(mActiveDataSubIdListener, + PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE); IntentFilter filter = new IntentFilter(); filter.addAction(UsbManager.ACTION_USB_STATE); @@ -314,6 +298,43 @@ public class Tethering { } + private class TetheringThreadExecutor implements Executor { + private final Handler mTetherHandler; + TetheringThreadExecutor(Handler handler) { + mTetherHandler = handler; + } + @Override + public void execute(Runnable command) { + if (!mTetherHandler.post(command)) { + throw new RejectedExecutionException(mTetherHandler + " is shutting down"); + } + } + } + + private class ActiveDataSubIdListener extends PhoneStateListener { + ActiveDataSubIdListener(Executor executor) { + super(executor); + } + + @Override + public void onActiveDataSubscriptionIdChanged(int subId) { + mLog.log("OBSERVED active data subscription change, from " + mActiveDataSubId + + " to " + subId); + if (subId == mActiveDataSubId) return; + + mActiveDataSubId = subId; + updateConfiguration(); + // To avoid launching unexpected provisioning checks, ignore re-provisioning + // when no CarrierConfig loaded yet. Assume reevaluateSimCardProvisioning() + // ill be triggered again when CarrierConfig is loaded. + if (mEntitlementMgr.getCarrierConfig(mConfig) != null) { + mEntitlementMgr.reevaluateSimCardProvisioning(mConfig); + } else { + mLog.log("IGNORED reevaluate provisioning, no carrier config loaded"); + } + } + } + private WifiManager getWifiManager() { return (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); } @@ -326,8 +347,7 @@ public class Tethering { } private void maybeDunSettingChanged() { - final boolean isDunRequired = TetheringConfiguration.checkDunRequired( - mContext, mActiveDataSubId); + final boolean isDunRequired = TetheringConfiguration.checkDunRequired(mContext); if (isDunRequired == mConfig.isDunRequired) return; updateConfiguration(); } @@ -633,8 +653,7 @@ public class Tethering { reportTetherStateChanged(mTetherStatesParcel); final Intent bcast = new Intent(ACTION_TETHER_STATE_CHANGED); - bcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING - | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + bcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); bcast.putStringArrayListExtra(EXTRA_AVAILABLE_TETHER, availableList); bcast.putStringArrayListExtra(EXTRA_ACTIVE_LOCAL_ONLY, localOnlyList); bcast.putStringArrayListExtra(EXTRA_ACTIVE_TETHER, tetherList); @@ -1163,7 +1182,6 @@ public class Tethering { } class TetherMasterSM extends StateMachine { - private static final int BASE_MASTER = Protocol.BASE_TETHERING; // an interface SM has requested Tethering/Local Hotspot static final int EVENT_IFACE_SERVING_STATE_ACTIVE = BASE_MASTER + 1; // an interface SM has unrequested Tethering/Local Hotspot @@ -1180,7 +1198,6 @@ public class Tethering { static final int EVENT_IFACE_UPDATE_LINKPROPERTIES = BASE_MASTER + 7; // Events from EntitlementManager to choose upstream again. static final int EVENT_UPSTREAM_PERMISSION_CHANGED = BASE_MASTER + 8; - private final State mInitialState; private final State mTetherModeAliveState; diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java index 0ab4d634e84d..490614b03149 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java @@ -100,13 +100,13 @@ public class TetheringConfiguration { public final String provisioningAppNoUi; public final int provisioningCheckPeriod; - public final int subId; + public final int activeDataSubId; public TetheringConfiguration(Context ctx, SharedLog log, int id) { final SharedLog configLog = log.forSubComponent("config"); - subId = id; - Resources res = getResources(ctx, subId); + activeDataSubId = id; + Resources res = getResources(ctx, activeDataSubId); tetherableUsbRegexs = getResourceStringArray(res, config_tether_usb_regexs); // TODO: Evaluate deleting this altogether now that Wi-Fi always passes @@ -116,7 +116,7 @@ public class TetheringConfiguration { tetherableWifiP2pRegexs = getResourceStringArray(res, config_tether_wifi_p2p_regexs); tetherableBluetoothRegexs = getResourceStringArray(res, config_tether_bluetooth_regexs); - isDunRequired = checkDunRequired(ctx, subId); + isDunRequired = checkDunRequired(ctx); chooseUpstreamAutomatically = getResourceBoolean(res, config_tether_upstream_automatic); preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, isDunRequired); @@ -166,8 +166,8 @@ public class TetheringConfiguration { /** Does the dumping.*/ public void dump(PrintWriter pw) { - pw.print("subId: "); - pw.println(subId); + pw.print("activeDataSubId: "); + pw.println(activeDataSubId); dumpStringArray(pw, "tetherableUsbRegexs", tetherableUsbRegexs); dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs); @@ -196,7 +196,7 @@ public class TetheringConfiguration { /** Returns the string representation of this object.*/ public String toString() { final StringJoiner sj = new StringJoiner(" "); - sj.add(String.format("subId:%d", subId)); + sj.add(String.format("activeDataSubId:%d", activeDataSubId)); sj.add(String.format("tetherableUsbRegexs:%s", makeString(tetherableUsbRegexs))); sj.add(String.format("tetherableWifiRegexs:%s", makeString(tetherableWifiRegexs))); sj.add(String.format("tetherableWifiP2pRegexs:%s", makeString(tetherableWifiP2pRegexs))); @@ -250,9 +250,11 @@ public class TetheringConfiguration { } /** Check whether dun is required. */ - public static boolean checkDunRequired(Context ctx, int id) { + public static boolean checkDunRequired(Context ctx) { final TelephonyManager tm = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE); - return (tm != null) ? tm.isTetheringApnRequired(id) : false; + // TelephonyManager would uses the active data subscription, which should be the one used + // by tethering. + return (tm != null) ? tm.isTetheringApnRequired() : false; } private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) { @@ -391,7 +393,7 @@ public class TetheringConfiguration { */ public TetheringConfigurationParcel toStableParcelable() { final TetheringConfigurationParcel parcel = new TetheringConfigurationParcel(); - parcel.subId = subId; + parcel.subId = activeDataSubId; parcel.tetherableUsbRegexs = tetherableUsbRegexs; parcel.tetherableWifiRegexs = tetherableWifiRegexs; parcel.tetherableBluetoothRegexs = tetherableBluetoothRegexs; diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java index ba30845e6348..775484eabfa3 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java @@ -21,6 +21,7 @@ import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERM import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION; import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; import static android.net.TetheringManager.TETHER_ERROR_UNSUPPORTED; +import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR; import android.app.Service; import android.content.Context; @@ -84,6 +85,7 @@ public class TetheringService extends Service { */ @VisibleForTesting public Tethering makeTethering(TetheringDependencies deps) { + System.loadLibrary("tetherutilsjni"); return new Tethering(deps); } @@ -339,7 +341,10 @@ public class TetheringService extends Service { service.makeDhcpServer(ifName, params, cb); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + Log.e(TAG, "Fail to make dhcp server"); + try { + cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null); + } catch (RemoteException re) { } } } }; diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java index dc38c49a0c12..22150f623a35 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java @@ -328,13 +328,6 @@ public class UpstreamNetworkMonitor { network, newNc)); } - // Log changes in upstream network signal strength, if available. - if (network.equals(mTetheringUpstreamNetwork) && newNc.hasSignalStrength()) { - final int newSignal = newNc.getSignalStrength(); - final String prevSignal = getSignalStrength(prev.networkCapabilities); - mLog.logf("upstream network signal strength: %s -> %s", prevSignal, newSignal); - } - mNetworkMap.put(network, new UpstreamNetworkState( prev.linkProperties, newNc, network)); // TODO: If sufficient information is available to select a more @@ -557,11 +550,6 @@ public class UpstreamNetworkMonitor { return prefixSet; } - private static String getSignalStrength(NetworkCapabilities nc) { - if (nc == null || !nc.hasSignalStrength()) return "unknown"; - return Integer.toString(nc.getSignalStrength()); - } - private static boolean isCellular(UpstreamNetworkState ns) { return (ns != null) && isCellular(ns.networkCapabilities); } diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp index 81a0548cd718..53782fed1c50 100644 --- a/packages/Tethering/tests/unit/Android.bp +++ b/packages/Tethering/tests/unit/Android.bp @@ -20,7 +20,11 @@ android_test { srcs: [ "src/**/*.java", ], - test_suites: ["device-tests"], + test_suites: [ + "device-tests", + "mts", + ], + compile_multilib: "both", static_libs: [ "androidx.test.rules", "frameworks-base-testutils", diff --git a/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java b/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java index e01ac7f08c2f..e8add9830b5f 100644 --- a/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java +++ b/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java @@ -18,8 +18,6 @@ package android.net.dhcp; import static android.net.InetAddresses.parseNumericAddress; -import static com.google.android.collect.Sets.newHashSet; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -34,6 +32,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.net.Inet4Address; +import java.util.Arrays; +import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -47,9 +47,10 @@ public class DhcpServingParamsParcelExtTest { private static final int TEST_LEASE_TIME_SECS = 120; private static final int TEST_MTU = 1000; private static final Set<Inet4Address> TEST_ADDRESS_SET = - newHashSet(inet4Addr("192.168.1.123"), inet4Addr("192.168.1.124")); + new HashSet<Inet4Address>(Arrays.asList( + new Inet4Address[] {inet4Addr("192.168.1.123"), inet4Addr("192.168.1.124")})); private static final Set<Integer> TEST_ADDRESS_SET_PARCELED = - newHashSet(0xc0a8017b, 0xc0a8017c); + new HashSet<Integer>(Arrays.asList(new Integer[] {0xc0a8017b, 0xc0a8017c})); private DhcpServingParamsParcelExt mParcel; diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index 4358cd6966f1..fd2f708aea30 100644 --- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -510,8 +510,10 @@ public class IpServerTest { } assertNotNull("missing IPv4 address", addr4); + final IpPrefix destination = new IpPrefix(addr4.getAddress(), addr4.getPrefixLength()); // Assert the presence of the associated directly connected route. - final RouteInfo directlyConnected = new RouteInfo(addr4, null, lp.getInterfaceName()); + final RouteInfo directlyConnected = new RouteInfo(destination, null, lp.getInterfaceName(), + RouteInfo.RTN_UNICAST); assertTrue("missing directly connected route: '" + directlyConnected.toString() + "'", lp.getRoutes().contains(directlyConnected)); } diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java index 8574f5401496..7886ca6c132d 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java @@ -21,6 +21,7 @@ import static android.net.NetworkStats.STATS_PER_IFACE; import static android.net.NetworkStats.STATS_PER_UID; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; +import static android.net.RouteInfo.RTN_UNICAST; import static android.net.TrafficStats.UID_TETHERING; import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; @@ -269,7 +270,7 @@ public class OffloadControllerTest { final String ipv4Addr = "192.0.2.5"; final String linkAddr = ipv4Addr + "/24"; lp.addLinkAddress(new LinkAddress(linkAddr)); - lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"))); + lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, null, RTN_UNICAST)); offload.setUpstreamLinkProperties(lp); // IPv4 prefixes and addresses on the upstream are simply left as whole // prefixes (already passed in from UpstreamNetworkMonitor code). If a @@ -285,7 +286,7 @@ public class OffloadControllerTest { inOrder.verifyNoMoreInteractions(); final String ipv4Gateway = "192.0.2.1"; - lp.addRoute(new RouteInfo(InetAddress.getByName(ipv4Gateway))); + lp.addRoute(new RouteInfo(null, InetAddress.getByName(ipv4Gateway), null, RTN_UNICAST)); offload.setUpstreamLinkProperties(lp); // No change in local addresses means no call to setLocalPrefixes(). inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); @@ -296,7 +297,7 @@ public class OffloadControllerTest { inOrder.verifyNoMoreInteractions(); final String ipv6Gw1 = "fe80::cafe"; - lp.addRoute(new RouteInfo(InetAddress.getByName(ipv6Gw1))); + lp.addRoute(new RouteInfo(null, InetAddress.getByName(ipv6Gw1), null, RTN_UNICAST)); offload.setUpstreamLinkProperties(lp); // No change in local addresses means no call to setLocalPrefixes(). inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); @@ -310,7 +311,7 @@ public class OffloadControllerTest { inOrder.verifyNoMoreInteractions(); final String ipv6Gw2 = "fe80::d00d"; - lp.addRoute(new RouteInfo(InetAddress.getByName(ipv6Gw2))); + lp.addRoute(new RouteInfo(null, InetAddress.getByName(ipv6Gw2), null, RTN_UNICAST)); offload.setUpstreamLinkProperties(lp); // No change in local addresses means no call to setLocalPrefixes(). inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); @@ -327,8 +328,10 @@ public class OffloadControllerTest { final LinkProperties stacked = new LinkProperties(); stacked.setInterfaceName("stacked"); stacked.addLinkAddress(new LinkAddress("192.0.2.129/25")); - stacked.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254"))); - stacked.addRoute(new RouteInfo(InetAddress.getByName("fe80::bad:f00"))); + stacked.addRoute(new RouteInfo(null, InetAddress.getByName("192.0.2.254"), null, + RTN_UNICAST)); + stacked.addRoute(new RouteInfo(null, InetAddress.getByName("fe80::bad:f00"), null, + RTN_UNICAST)); assertTrue(lp.addStackedLink(stacked)); offload.setUpstreamLinkProperties(lp); // No change in local addresses means no call to setLocalPrefixes(). @@ -348,7 +351,7 @@ public class OffloadControllerTest { // removed from "local prefixes" and /128s added for the upstream IPv6 // addresses. This is not yet implemented, and for now we simply // expect to see these /128s. - lp.addRoute(new RouteInfo(new IpPrefix("2001:db8::/64"))); + lp.addRoute(new RouteInfo(new IpPrefix("2001:db8::/64"), null, null, RTN_UNICAST)); // "2001:db8::/64" plus "assigned" ASCII in hex lp.addLinkAddress(new LinkAddress("2001:db8::6173:7369:676e:6564/64")); // "2001:db8::/64" plus "random" ASCII in hex @@ -574,13 +577,15 @@ public class OffloadControllerTest { final LinkProperties usbLinkProperties = new LinkProperties(); usbLinkProperties.setInterfaceName(RNDIS0); usbLinkProperties.addLinkAddress(new LinkAddress("192.168.42.1/24")); - usbLinkProperties.addRoute(new RouteInfo(new IpPrefix(USB_PREFIX))); + usbLinkProperties.addRoute( + new RouteInfo(new IpPrefix(USB_PREFIX), null, null, RTN_UNICAST)); offload.notifyDownstreamLinkProperties(usbLinkProperties); inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, USB_PREFIX); inOrder.verifyNoMoreInteractions(); // [2] Routes for IPv6 link-local prefixes should never be added. - usbLinkProperties.addRoute(new RouteInfo(new IpPrefix(IPV6_LINKLOCAL))); + usbLinkProperties.addRoute( + new RouteInfo(new IpPrefix(IPV6_LINKLOCAL), null, null, RTN_UNICAST)); offload.notifyDownstreamLinkProperties(usbLinkProperties); inOrder.verify(mHardware, never()).addDownstreamPrefix(eq(RNDIS0), anyString()); inOrder.verifyNoMoreInteractions(); @@ -588,7 +593,8 @@ public class OffloadControllerTest { // [3] Add an IPv6 prefix for good measure. Only new offload-able // prefixes should be passed to the HAL. usbLinkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64")); - usbLinkProperties.addRoute(new RouteInfo(new IpPrefix(IPV6_DOC_PREFIX))); + usbLinkProperties.addRoute( + new RouteInfo(new IpPrefix(IPV6_DOC_PREFIX), null, null, RTN_UNICAST)); offload.notifyDownstreamLinkProperties(usbLinkProperties); inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, IPV6_DOC_PREFIX); inOrder.verifyNoMoreInteractions(); @@ -601,8 +607,10 @@ public class OffloadControllerTest { // [5] Differences in local routes are converted into addDownstream() // and removeDownstream() invocations accordingly. - usbLinkProperties.removeRoute(new RouteInfo(new IpPrefix(IPV6_DOC_PREFIX), null, RNDIS0)); - usbLinkProperties.addRoute(new RouteInfo(new IpPrefix(IPV6_DISCARD_PREFIX))); + usbLinkProperties.removeRoute( + new RouteInfo(new IpPrefix(IPV6_DOC_PREFIX), null, RNDIS0, RTN_UNICAST)); + usbLinkProperties.addRoute( + new RouteInfo(new IpPrefix(IPV6_DISCARD_PREFIX), null, null, RTN_UNICAST)); offload.notifyDownstreamLinkProperties(usbLinkProperties); inOrder.verify(mHardware, times(1)).removeDownstreamPrefix(RNDIS0, IPV6_DOC_PREFIX); inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, IPV6_DISCARD_PREFIX); @@ -680,19 +688,23 @@ public class OffloadControllerTest { final LinkProperties usbLinkProperties = new LinkProperties(); usbLinkProperties.setInterfaceName(RNDIS0); usbLinkProperties.addLinkAddress(new LinkAddress("192.168.42.1/24")); - usbLinkProperties.addRoute(new RouteInfo(new IpPrefix(USB_PREFIX))); + usbLinkProperties.addRoute( + new RouteInfo(new IpPrefix(USB_PREFIX), null, null, RTN_UNICAST)); offload.notifyDownstreamLinkProperties(usbLinkProperties); final LinkProperties wifiLinkProperties = new LinkProperties(); wifiLinkProperties.setInterfaceName(WLAN0); wifiLinkProperties.addLinkAddress(new LinkAddress("192.168.43.1/24")); - wifiLinkProperties.addRoute(new RouteInfo(new IpPrefix(WIFI_PREFIX))); - wifiLinkProperties.addRoute(new RouteInfo(new IpPrefix(IPV6_LINKLOCAL))); + wifiLinkProperties.addRoute( + new RouteInfo(new IpPrefix(WIFI_PREFIX), null, null, RTN_UNICAST)); + wifiLinkProperties.addRoute( + new RouteInfo(new IpPrefix(IPV6_LINKLOCAL), null, null, RTN_UNICAST)); // Use a benchmark prefix (RFC 5180 + erratum), since the documentation // prefix is included in the excluded prefix list. wifiLinkProperties.addLinkAddress(new LinkAddress("2001:2::1/64")); wifiLinkProperties.addLinkAddress(new LinkAddress("2001:2::2/64")); - wifiLinkProperties.addRoute(new RouteInfo(new IpPrefix("2001:2::/64"))); + wifiLinkProperties.addRoute( + new RouteInfo(new IpPrefix("2001:2::/64"), null, null, RTN_UNICAST)); offload.notifyDownstreamLinkProperties(wifiLinkProperties); offload.removeDownstreamInterface(RNDIS0); diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java index 30bff3560955..7799da4b94a7 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java @@ -34,7 +34,6 @@ import static com.android.internal.R.array.config_tether_wifi_regexs; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.when; import android.content.ContentResolver; @@ -145,7 +144,7 @@ public class TetheringConfigurationTest { @Test public void testDunFromTelephonyManagerMeansDun() { - when(mTelephonyManager.isTetheringApnRequired(anyInt())).thenReturn(true); + when(mTelephonyManager.isTetheringApnRequired()).thenReturn(true); final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI); final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration( @@ -169,7 +168,7 @@ public class TetheringConfigurationTest { @Test public void testDunNotRequiredFromTelephonyManagerMeansNoDun() { - when(mTelephonyManager.isTetheringApnRequired(anyInt())).thenReturn(false); + when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false); final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI); final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration( @@ -212,7 +211,7 @@ public class TetheringConfigurationTest { @Test public void testNoDefinedUpstreamTypesAddsEthernet() { when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[]{}); - when(mTelephonyManager.isTetheringApnRequired(anyInt())).thenReturn(false); + when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false); final TetheringConfiguration cfg = new TetheringConfiguration( mMockContext, mLog, INVALID_SUBSCRIPTION_ID); @@ -235,7 +234,7 @@ public class TetheringConfigurationTest { public void testDefinedUpstreamTypesSansEthernetAddsEthernet() { when(mResources.getIntArray(config_tether_upstream_types)).thenReturn( new int[]{TYPE_WIFI, TYPE_MOBILE_HIPRI}); - when(mTelephonyManager.isTetheringApnRequired(anyInt())).thenReturn(false); + when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false); final TetheringConfiguration cfg = new TetheringConfiguration( mMockContext, mLog, INVALID_SUBSCRIPTION_ID); @@ -253,7 +252,7 @@ public class TetheringConfigurationTest { public void testDefinedUpstreamTypesWithEthernetDoesNotAddEthernet() { when(mResources.getIntArray(config_tether_upstream_types)) .thenReturn(new int[]{TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_HIPRI}); - when(mTelephonyManager.isTetheringApnRequired(anyInt())).thenReturn(false); + when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false); final TetheringConfiguration cfg = new TetheringConfiguration( mMockContext, mLog, INVALID_SUBSCRIPTION_ID); diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index 0bc8c7944615..809f0e994737 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -28,6 +28,7 @@ import static android.net.ConnectivityManager.TETHERING_WIFI; import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE; import static android.net.ConnectivityManager.TYPE_WIFI_P2P; +import static android.net.RouteInfo.RTN_UNICAST; import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; @@ -72,6 +73,7 @@ import android.net.INetd; import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; import android.net.ITetheringEventCallback; +import android.net.InetAddresses; import android.net.InterfaceConfiguration; import android.net.IpPrefix; import android.net.LinkAddress; @@ -81,7 +83,6 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkRequest; -import android.net.NetworkUtils; import android.net.RouteInfo; import android.net.TetherStatesParcel; import android.net.TetheringConfigurationParcel; @@ -365,23 +366,26 @@ public class TetheringTest { if (withIPv4) { prop.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), - NetworkUtils.numericToInetAddress("10.0.0.1"), TEST_MOBILE_IFNAME)); + InetAddresses.parseNumericAddress("10.0.0.1"), + TEST_MOBILE_IFNAME, RTN_UNICAST)); } if (withIPv6) { - prop.addDnsServer(NetworkUtils.numericToInetAddress("2001:db8::2")); + prop.addDnsServer(InetAddresses.parseNumericAddress("2001:db8::2")); prop.addLinkAddress( - new LinkAddress(NetworkUtils.numericToInetAddress("2001:db8::"), + new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::"), NetworkConstants.RFC7421_PREFIX_LENGTH)); prop.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), - NetworkUtils.numericToInetAddress("2001:db8::1"), TEST_MOBILE_IFNAME)); + InetAddresses.parseNumericAddress("2001:db8::1"), + TEST_MOBILE_IFNAME, RTN_UNICAST)); } if (with464xlat) { final LinkProperties stackedLink = new LinkProperties(); stackedLink.setInterfaceName(TEST_XLAT_MOBILE_IFNAME); stackedLink.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), - NetworkUtils.numericToInetAddress("192.0.0.1"), TEST_XLAT_MOBILE_IFNAME)); + InetAddresses.parseNumericAddress("192.0.0.1"), + TEST_XLAT_MOBILE_IFNAME, RTN_UNICAST)); prop.addStackedLink(stackedLink); } @@ -1210,12 +1214,12 @@ public class TetheringTest { @Test public void testMultiSimAware() throws Exception { final TetheringConfiguration initailConfig = mTethering.getTetheringConfiguration(); - assertEquals(INVALID_SUBSCRIPTION_ID, initailConfig.subId); + assertEquals(INVALID_SUBSCRIPTION_ID, initailConfig.activeDataSubId); final int fakeSubId = 1234; mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId); final TetheringConfiguration newConfig = mTethering.getTetheringConfiguration(); - assertEquals(fakeSubId, newConfig.subId); + assertEquals(fakeSubId, newConfig.activeDataSubId); } private void workingWifiP2pGroupOwner( diff --git a/services/Android.bp b/services/Android.bp index 53d792d1033a..80452f19e6c1 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -121,6 +121,12 @@ droidstubs { "framework-all", ], visibility: ["//visibility:private"], + check_api: { + current: { + api_file: "api/current.txt", + removed_api_file: "api/removed.txt", + }, + }, } java_library { diff --git a/services/api/current.txt b/services/api/current.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/services/api/current.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/services/api/removed.txt b/services/api/removed.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/services/api/removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/services/core/Android.bp b/services/core/Android.bp index cbd095b3a045..4c569efe4c0a 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -27,8 +27,8 @@ java_library_static { "android.hardware.light-V2.0-java", "android.hardware.power-V1.0-java", "android.hardware.tv.cec-V1.0-java", + "android.hardware.vibrator-java", "app-compat-annotations", - "vintf-vibrator-java", "framework-tethering", ], diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java index 9dead161390f..aeb3e7fd94de 100644 --- a/services/core/java/com/android/server/DropBoxManagerService.java +++ b/services/core/java/com/android/server/DropBoxManagerService.java @@ -41,7 +41,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; -import android.text.format.Time; +import android.text.format.TimeMigrationUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; @@ -582,11 +582,9 @@ public final class DropBoxManagerService extends SystemService { } int numFound = 0, numArgs = searchArgs.size(); - Time time = new Time(); out.append("\n"); for (EntryFile entry : mAllFiles.contents) { - time.set(entry.timestampMillis); - String date = time.format("%Y-%m-%d %H:%M:%S"); + String date = TimeMigrationUtils.formatMillisWithFixedFormat(entry.timestampMillis); boolean match = true; for (int i = 0; i < numArgs && match; i++) { String arg = searchArgs.get(i); diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java index c0f10a3c86e1..9b1326bc88d7 100644 --- a/services/core/java/com/android/server/MmsServiceBroker.java +++ b/services/core/java/com/android/server/MmsServiceBroker.java @@ -16,6 +16,8 @@ package com.android.server; +import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX; + import android.Manifest; import android.app.AppOpsManager; import android.app.PendingIntent; @@ -37,6 +39,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.service.carrier.CarrierMessagingService; import android.telephony.SmsManager; +import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.Slog; @@ -137,11 +140,6 @@ public class MmsServiceBroker extends SystemService { } @Override - public Bundle getCarrierConfigValues(int subId) throws RemoteException { - return null; - } - - @Override public Uri importTextMessage(String callingPkg, String address, int type, String text, long timestampMillis, boolean seen, boolean read) throws RemoteException { return null; @@ -370,12 +368,6 @@ public class MmsServiceBroker extends SystemService { } @Override - public Bundle getCarrierConfigValues(int subId) throws RemoteException { - Slog.d(TAG, "getCarrierConfigValues() by " + getCallingPackageName()); - return getServiceGuarded().getCarrierConfigValues(subId); - } - - @Override public Uri importTextMessage(String callingPkg, String address, int type, String text, long timestampMillis, boolean seen, boolean read) throws RemoteException { if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), @@ -523,11 +515,11 @@ public class MmsServiceBroker extends SystemService { // Grant permission for the carrier app. Intent intent = new Intent(action); - TelephonyManager telephonyManager = - (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); - List<String> carrierPackages = - telephonyManager.getCarrierPackageNamesForIntentAndPhone( - intent, SubscriptionManager.getPhoneId(subId)); + TelephonyManager telephonyManager = (TelephonyManager) + mContext.getSystemService(Context.TELEPHONY_SERVICE); + List<String> carrierPackages = telephonyManager + .getCarrierPackageNamesForIntentAndPhone( + intent, getPhoneIdFromSubId(subId)); if (carrierPackages != null && carrierPackages.size() == 1) { LocalServices.getService(UriGrantsManagerInternal.class) .grantUriPermissionFromIntent(callingUid, carrierPackages.get(0), @@ -539,4 +531,13 @@ public class MmsServiceBroker extends SystemService { return contentUri; } } + + private int getPhoneIdFromSubId(int subId) { + SubscriptionManager subManager = (SubscriptionManager) + mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); + if (subManager == null) return INVALID_SIM_SLOT_INDEX; + SubscriptionInfo info = subManager.getActiveSubscriptionInfo(subId); + if (info == null) return INVALID_SIM_SLOT_INDEX; + return info.getSimSlotIndex(); + } } diff --git a/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java b/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java index 39be311e902d..cfe56052118f 100644 --- a/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java +++ b/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java @@ -18,6 +18,8 @@ package com.android.server; import android.app.AlarmManager; import android.app.PendingIntent; +import android.app.timedetector.NetworkTimeSuggestion; +import android.app.timedetector.TimeDetector; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -35,10 +37,10 @@ import android.os.Message; import android.os.PowerManager; import android.os.SystemClock; import android.provider.Settings; -import android.telephony.TelephonyManager; import android.util.Log; import android.util.NtpTrustedTime; import android.util.TimeUtils; +import android.util.TimestampedValue; import com.android.internal.util.DumpUtils; @@ -46,21 +48,19 @@ import java.io.FileDescriptor; import java.io.PrintWriter; /** - * Monitors the network time and updates the system time if it is out of sync - * and there hasn't been any NITZ update from the carrier recently. - * If looking up the network time fails for some reason, it tries a few times with a short - * interval and then resets to checking on longer intervals. - * <p> - * If the user enables AUTO_TIME, it will check immediately for the network time, if NITZ wasn't - * available. - * </p> + * Monitors the network time. If looking up the network time fails for some reason, it tries a few + * times with a short interval and then resets to checking on longer intervals. + * + * <p>When available, the time is always suggested to the {@link + * com.android.server.timedetector.TimeDetectorService} where it may be used to set the device + * system clock, depending on user settings and what other signals are available. */ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeUpdateService { private static final String TAG = "NetworkTimeUpdateService"; private static final boolean DBG = false; - private static final int EVENT_AUTO_TIME_CHANGED = 1; + private static final int EVENT_AUTO_TIME_ENABLED = 1; private static final int EVENT_POLL_NETWORK_TIME = 2; private static final int EVENT_NETWORK_CHANGED = 3; @@ -69,20 +69,19 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU private static final int POLL_REQUEST = 0; - private static final long NOT_SET = -1; - private long mNitzTimeSetTime = NOT_SET; private Network mDefaultNetwork = null; private final Context mContext; private final NtpTrustedTime mTime; private final AlarmManager mAlarmManager; + private final TimeDetector mTimeDetector; private final ConnectivityManager mCM; private final PendingIntent mPendingPollIntent; private final PowerManager.WakeLock mWakeLock; // NTP lookup is done on this thread and handler private Handler mHandler; - private SettingsObserver mSettingsObserver; + private AutoTimeSettingObserver mAutoTimeSettingObserver; private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback; // Normal polling frequency @@ -91,8 +90,6 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU private final long mPollingIntervalShorterMs; // Number of times to try again private final int mTryAgainTimesMax; - // If the time difference is greater than this threshold, then update the time. - private final int mTimeErrorThresholdMs; // Keeps track of how many quick attempts were made to fetch NTP time. // During bootup, the network may not have been up yet, or it's taking time for the // connection to happen. @@ -102,6 +99,7 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU mContext = context; mTime = NtpTrustedTime.getInstance(context); mAlarmManager = mContext.getSystemService(AlarmManager.class); + mTimeDetector = mContext.getSystemService(TimeDetector.class); mCM = mContext.getSystemService(ConnectivityManager.class); Intent pollIntent = new Intent(ACTION_POLL, null); @@ -113,8 +111,6 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU com.android.internal.R.integer.config_ntpPollingIntervalShorter); mTryAgainTimesMax = mContext.getResources().getInteger( com.android.internal.R.integer.config_ntpRetry); - mTimeErrorThresholdMs = mContext.getResources().getInteger( - com.android.internal.R.integer.config_ntpThreshold); mWakeLock = context.getSystemService(PowerManager.class).newWakeLock( PowerManager.PARTIAL_WAKE_LOCK, TAG); @@ -122,7 +118,6 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU @Override public void systemRunning() { - registerForTelephonyIntents(); registerForAlarms(); HandlerThread thread = new HandlerThread(TAG); @@ -131,14 +126,9 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback(); mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler); - mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED); - mSettingsObserver.observe(mContext); - } - - private void registerForTelephonyIntents() { - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(TelephonyManager.ACTION_NETWORK_SET_TIME); - mContext.registerReceiver(mNitzReceiver, intentFilter); + mAutoTimeSettingObserver = new AutoTimeSettingObserver(mContext, mHandler, + EVENT_AUTO_TIME_ENABLED); + mAutoTimeSettingObserver.observe(); } private void registerForAlarms() { @@ -152,8 +142,7 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU } private void onPollNetworkTime(int event) { - // If Automatic time is not set, don't bother. Similarly, if we don't - // have any default network, don't bother. + // If we don't have any default network, don't bother. if (mDefaultNetwork == null) return; mWakeLock.acquire(); try { @@ -173,10 +162,12 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU if (mTime.getCacheAge() < mPollingIntervalMs) { // Obtained fresh fix; schedule next normal update resetAlarm(mPollingIntervalMs); - if (isAutomaticTimeRequested()) { - updateSystemClock(event); - } + // Suggest the time to the time detector. It may choose use it to set the system clock. + TimestampedValue<Long> timeSignal = mTime.getCachedNtpTimeSignal(); + NetworkTimeSuggestion timeSuggestion = new NetworkTimeSuggestion(timeSignal); + timeSuggestion.addDebugInfo("Origin: NetworkTimeUpdateServiceImpl. event=" + event); + mTimeDetector.suggestNetworkTime(timeSuggestion); } else { // No fresh fix; schedule retry mTryAgainCounter++; @@ -190,36 +181,6 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU } } - private long getNitzAge() { - if (mNitzTimeSetTime == NOT_SET) { - return Long.MAX_VALUE; - } else { - return SystemClock.elapsedRealtime() - mNitzTimeSetTime; - } - } - - /** - * Consider updating system clock based on current NTP fix, if requested by - * user, significant enough delta, and we don't have a recent NITZ. - */ - private void updateSystemClock(int event) { - final boolean forceUpdate = (event == EVENT_AUTO_TIME_CHANGED); - if (!forceUpdate) { - if (getNitzAge() < mPollingIntervalMs) { - if (DBG) Log.d(TAG, "Ignoring NTP update due to recent NITZ"); - return; - } - - final long skew = Math.abs(mTime.currentTimeMillis() - System.currentTimeMillis()); - if (skew < mTimeErrorThresholdMs) { - if (DBG) Log.d(TAG, "Ignoring NTP update due to low skew"); - return; - } - } - - SystemClock.setCurrentTimeMillis(mTime.currentTimeMillis()); - } - /** * Cancel old alarm and starts a new one for the specified interval. * @@ -232,27 +193,6 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent); } - /** - * Checks if the user prefers to automatically set the time. - */ - private boolean isAutomaticTimeRequested() { - return Settings.Global.getInt( - mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0; - } - - /** Receiver for Nitz time events */ - private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() { - - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (DBG) Log.d(TAG, "Received " + action); - if (TelephonyManager.ACTION_NETWORK_SET_TIME.equals(action)) { - mNitzTimeSetTime = SystemClock.elapsedRealtime(); - } - } - }; - /** Handler to do the network accesses on */ private class MyHandler extends Handler { @@ -263,7 +203,7 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU @Override public void handleMessage(Message msg) { switch (msg.what) { - case EVENT_AUTO_TIME_CHANGED: + case EVENT_AUTO_TIME_ENABLED: case EVENT_POLL_NETWORK_TIME: case EVENT_NETWORK_CHANGED: onPollNetworkTime(msg.what); @@ -287,27 +227,42 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU } } - /** Observer to watch for changes to the AUTO_TIME setting */ - private static class SettingsObserver extends ContentObserver { + /** + * Observer to watch for changes to the AUTO_TIME setting. It only triggers when the setting + * is enabled. + */ + private static class AutoTimeSettingObserver extends ContentObserver { - private int mMsg; - private Handler mHandler; + private final Context mContext; + private final int mMsg; + private final Handler mHandler; - SettingsObserver(Handler handler, int msg) { + AutoTimeSettingObserver(Context context, Handler handler, int msg) { super(handler); + mContext = context; mHandler = handler; mMsg = msg; } - void observe(Context context) { - ContentResolver resolver = context.getContentResolver(); + void observe() { + ContentResolver resolver = mContext.getContentResolver(); resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME), false, this); } @Override public void onChange(boolean selfChange) { - mHandler.obtainMessage(mMsg).sendToTarget(); + if (isAutomaticTimeEnabled()) { + mHandler.obtainMessage(mMsg).sendToTarget(); + } + } + + /** + * Checks if the user prefers to automatically set the time. + */ + private boolean isAutomaticTimeEnabled() { + ContentResolver resolver = mContext.getContentResolver(); + return Settings.Global.getInt(resolver, Settings.Global.AUTO_TIME, 0) != 0; } } @@ -319,8 +274,6 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU pw.print("\nPollingIntervalShorterMs: "); TimeUtils.formatDuration(mPollingIntervalShorterMs, pw); pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax); - pw.print("TimeErrorThresholdMs: "); - TimeUtils.formatDuration(mTimeErrorThresholdMs, pw); pw.println("\nTryAgainCounter: " + mTryAgainCounter); pw.println("NTP cache age: " + mTime.getCacheAge()); pw.println("NTP cache certainty: " + mTime.getCacheCertainty()); diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index b5ecd196ba8a..6a4ef4cd12dd 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -16,6 +16,7 @@ package com.android.server; +import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX; import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED; import static android.telephony.TelephonyRegistryManager.SIM_ACTIVATION_TYPE_DATA; import static android.telephony.TelephonyRegistryManager.SIM_ACTIVATION_TYPE_VOICE; @@ -57,6 +58,7 @@ import android.telephony.PreciseDisconnectCause; import android.telephony.Rlog; import android.telephony.ServiceState; import android.telephony.SignalStrength; +import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.telephony.data.ApnSetting; @@ -95,7 +97,7 @@ import java.util.NoSuchElementException; * and 15973975 by saving the phoneId of the registrant and then using the * phoneId when deciding to to make a callback. This is necessary because * a subId changes from to a dummy value when a SIM is removed and thus won't - * compare properly. Because SubscriptionManager.getPhoneId(int subId) handles + * compare properly. Because getPhoneIdFromSubId(int subId) handles * the dummy value conversion we properly do the callbacks. * * Eventually we may want to remove the notion of dummy value but for now this @@ -128,7 +130,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - int phoneId = SubscriptionManager.INVALID_PHONE_INDEX; + int phoneId = SubscriptionManager.INVALID_SIM_SLOT_INDEX; boolean matchPhoneStateListenerEvent(int events) { return (callback != null) && ((events & this.events) != 0); @@ -226,7 +228,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private int mDefaultSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - private int mDefaultPhoneId = SubscriptionManager.INVALID_PHONE_INDEX; + private int mDefaultPhoneId = SubscriptionManager.INVALID_SIM_SLOT_INDEX; private int[] mRingingCallState; @@ -355,7 +357,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { SubscriptionManager.getDefaultSubscriptionId()); int newDefaultPhoneId = intent.getIntExtra( SubscriptionManager.EXTRA_SLOT_INDEX, - SubscriptionManager.getPhoneId(newDefaultSubId)); + getPhoneIdFromSubId(newDefaultSubId)); if (DBG) { log("onReceive:current mDefaultSubId=" + mDefaultSubId + " current mDefaultPhoneId=" + mDefaultPhoneId @@ -751,7 +753,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { return; } - int phoneId = SubscriptionManager.getPhoneId(subId); + int phoneId = getPhoneIdFromSubId(subId); synchronized (mRecords) { // register IBinder b = callback.asBinder(); @@ -786,9 +788,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { r.callback.onServiceStateChanged(rawSs); } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) { - r.callback.onServiceStateChanged(rawSs.sanitizeLocationInfo(false)); + r.callback.onServiceStateChanged( + rawSs.createLocationInfoSanitizedCopy(false)); } else { - r.callback.onServiceStateChanged(rawSs.sanitizeLocationInfo(true)); + r.callback.onServiceStateChanged( + rawSs.createLocationInfoSanitizedCopy(true)); } } catch (RemoteException ex) { remove(r.binder); @@ -1077,7 +1081,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { // Called only by Telecomm to communicate call state across different phone accounts. So // there is no need to add a valid subId or slotId. broadcastCallStateChanged(state, phoneNumber, - SubscriptionManager.INVALID_PHONE_INDEX, + SubscriptionManager.INVALID_SIM_SLOT_INDEX, SubscriptionManager.INVALID_SUBSCRIPTION_ID); } @@ -1144,9 +1148,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { stateToSend = new ServiceState(state); } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) { - stateToSend = state.sanitizeLocationInfo(false); + stateToSend = state.createLocationInfoSanitizedCopy(false); } else { - stateToSend = state.sanitizeLocationInfo(true); + stateToSend = state.createLocationInfoSanitizedCopy(true); } if (DBG) { log("notifyServiceStateForSubscriber: callback.onSSC r=" + r @@ -1301,7 +1305,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { synchronized (mRecords) { mCarrierNetworkChangeState = active; for (int subId : subIds) { - int phoneId = SubscriptionManager.getPhoneId(subId); + int phoneId = getPhoneIdFromSubId(subId); if (VDBG) { log("notifyCarrierNetworkChange: active=" + active + "subId: " + subId); @@ -1334,7 +1338,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("notifyCellInfoForSubscriber: subId=" + subId + " cellInfo=" + cellInfo); } - int phoneId = SubscriptionManager.getPhoneId(subId); + int phoneId = getPhoneIdFromSubId(subId); synchronized (mRecords) { if (validatePhoneId(phoneId)) { mCellInfo.set(phoneId, cellInfo); @@ -1425,7 +1429,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("notifyCallForwardingChangedForSubscriber: subId=" + subId + " cfi=" + cfi); } - int phoneId = SubscriptionManager.getPhoneId(subId); + int phoneId = getPhoneIdFromSubId(subId); synchronized (mRecords) { if (validatePhoneId(phoneId)) { mCallForwarding[phoneId] = cfi; @@ -1453,7 +1457,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (!checkNotifyPermission("notifyDataActivity()" )) { return; } - int phoneId = SubscriptionManager.getPhoneId(subId); + int phoneId = getPhoneIdFromSubId(subId); synchronized (mRecords) { if (validatePhoneId(phoneId)) { mDataActivity[phoneId] = state; @@ -1610,7 +1614,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { handleRemoveListLocked(); } - broadcastDataConnectionFailed(apnType, subId); } public void notifyCellLocation(Bundle cellLocation) { @@ -1627,7 +1630,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("notifyCellLocationForSubscriber: subId=" + subId + " cellLocation=" + cellLocation); } - int phoneId = SubscriptionManager.getPhoneId(subId); + int phoneId = getPhoneIdFromSubId(subId); synchronized (mRecords) { if (validatePhoneId(phoneId)) { mCellLocation[phoneId] = cellLocation; @@ -1736,7 +1739,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (!checkNotifyPermission("notifyImsCallDisconnectCause()")) { return; } - int phoneId = SubscriptionManager.getPhoneId(subId); + int phoneId = getPhoneIdFromSubId(subId); synchronized (mRecords) { if (validatePhoneId(phoneId)) { mImsReasonInfo.set(phoneId, imsReasonInfo); @@ -1803,7 +1806,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (VDBG) { log("notifySrvccStateChanged: subId=" + subId + " srvccState=" + state); } - int phoneId = SubscriptionManager.getPhoneId(subId); + int phoneId = getPhoneIdFromSubId(subId); synchronized (mRecords) { if (validatePhoneId(phoneId)) { mSrvccState[phoneId] = state; @@ -2209,7 +2212,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId); } // If the phoneId is invalid, the broadcast is for overall call state. - if (phoneId != SubscriptionManager.INVALID_PHONE_INDEX) { + if (phoneId != SubscriptionManager.INVALID_SIM_SLOT_INDEX) { intent.putExtra(PHONE_CONSTANTS_SLOT_KEY, phoneId); intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, phoneId); } @@ -2259,13 +2262,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } - private void broadcastDataConnectionFailed(String apnType, int subId) { - Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED); - intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType); - intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId); - mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); - } - private void enforceNotifyPermissionOrCarrierPrivilege(String method) { if (checkNotifyPermission()) { return; @@ -2653,8 +2649,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { return "TD_SCDMA"; case TelephonyManager.NETWORK_TYPE_IWLAN: return "IWLAN"; - case TelephonyManager.NETWORK_TYPE_LTE_CA: - return "LTE_CA"; + + //TODO: This network type is marked as hidden because it is not a + // true network type and we are looking to remove it completely from the available list + // of network types. Since this method is only used for logging, in the event that this + // network type is selected, the log will read as "Unknown." + //case TelephonyManager.NETWORK_TYPE_LTE_CA: + // return "LTE_CA"; + case TelephonyManager.NETWORK_TYPE_NR: return "NR"; default: @@ -2675,4 +2677,18 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private static CallQuality createCallQuality() { return new CallQuality(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } + + private int getPhoneIdFromSubId(int subId) { + SubscriptionManager subManager = (SubscriptionManager) + mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); + if (subManager == null) return INVALID_SIM_SLOT_INDEX; + + if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { + subId = SubscriptionManager.getDefaultSubscriptionId(); + } + + SubscriptionInfo info = subManager.getActiveSubscriptionInfo(subId); + if (info == null) return INVALID_SIM_SLOT_INDEX; + return info.getSimSlotIndex(); + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 1c4f1e3ed5bf..2af04ae3f800 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -5280,7 +5280,7 @@ public class ActivityManagerService extends IActivityManager.Stub storageManager.commitChanges(); } catch (Exception e) { PowerManager pm = (PowerManager) - mInjector.getContext().getSystemService(Context.POWER_SERVICE); + mContext.getSystemService(Context.POWER_SERVICE); pm.reboot("Checkpoint commit failed"); } diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index b406ce6c3ca8..972b10608056 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -2899,33 +2899,37 @@ final class ActivityManagerShellCommand extends ShellCommand { } ArraySet<Long> enabled = new ArraySet<>(); ArraySet<Long> disabled = new ArraySet<>(); - switch (toggleValue) { - case "enable": - enabled.add(changeId); - pw.println("Enabled change " + changeId + " for " + packageName + "."); - CompatibilityChangeConfig overrides = - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(enabled, disabled)); - platformCompat.setOverrides(overrides, packageName); - return 0; - case "disable": - disabled.add(changeId); - pw.println("Disabled change " + changeId + " for " + packageName + "."); - overrides = - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(enabled, disabled)); - platformCompat.setOverrides(overrides, packageName); - return 0; - case "reset": - if (platformCompat.clearOverride(changeId, packageName)) { - pw.println("Reset change " + changeId + " for " + packageName - + " to default value."); - } else { - pw.println("No override exists for changeId " + changeId + "."); - } - return 0; - default: - pw.println("Invalid toggle value: '" + toggleValue + "'."); + try { + switch (toggleValue) { + case "enable": + enabled.add(changeId); + CompatibilityChangeConfig overrides = + new CompatibilityChangeConfig( + new Compatibility.ChangeConfig(enabled, disabled)); + platformCompat.setOverrides(overrides, packageName); + pw.println("Enabled change " + changeId + " for " + packageName + "."); + return 0; + case "disable": + disabled.add(changeId); + overrides = + new CompatibilityChangeConfig( + new Compatibility.ChangeConfig(enabled, disabled)); + platformCompat.setOverrides(overrides, packageName); + pw.println("Disabled change " + changeId + " for " + packageName + "."); + return 0; + case "reset": + if (platformCompat.clearOverride(changeId, packageName)) { + pw.println("Reset change " + changeId + " for " + packageName + + " to default value."); + } else { + pw.println("No override exists for changeId " + changeId + "."); + } + return 0; + default: + pw.println("Invalid toggle value: '" + toggleValue + "'."); + } + } catch (SecurityException e) { + pw.println(e.getMessage()); } return -1; } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index f5579761c151..766fa3ba55c1 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -1363,7 +1363,7 @@ public final class ProcessList { final int procCount = procs.size(); for (int i = 0; i < procCount; i++) { final int procUid = procs.keyAt(i); - if (UserHandle.isApp(procUid) || !UserHandle.isSameUser(procUid, uid)) { + if (!UserHandle.isCore(procUid) || !UserHandle.isSameUser(procUid, uid)) { // Don't use an app process or different user process for system component. continue; } diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index cf83dd630a29..f15d999e1006 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -17,8 +17,10 @@ package com.android.server.compat; import android.compat.Compatibility.ChangeConfig; +import android.content.Context; import android.content.pm.ApplicationInfo; import android.os.Environment; +import android.os.RemoteException; import android.text.TextUtils; import android.util.LongArray; import android.util.LongSparseArray; @@ -26,8 +28,11 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.compat.AndroidBuildClassifier; import com.android.internal.compat.CompatibilityChangeConfig; import com.android.internal.compat.CompatibilityChangeInfo; +import com.android.internal.compat.IOverrideValidator; +import com.android.internal.compat.OverrideAllowedState; import com.android.server.compat.config.Change; import com.android.server.compat.config.XmlParser; @@ -54,22 +59,14 @@ final class CompatConfig { private static final String TAG = "CompatConfig"; - private static final CompatConfig sInstance = new CompatConfig().initConfigFromLib( - Environment.buildPath( - Environment.getRootDirectory(), "etc", "compatconfig")); - @GuardedBy("mChanges") private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>(); - @VisibleForTesting - CompatConfig() { - } + private IOverrideValidator mOverrideValidator; - /** - * @return The static instance of this class to be used within the system server. - */ - static CompatConfig get() { - return sInstance; + @VisibleForTesting + CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) { + mOverrideValidator = new OverrideValidatorImpl(androidBuildClassifier, context, this); } /** @@ -159,8 +156,12 @@ final class CompatConfig { * @param enabled If the change should be enabled or disabled. * @return {@code true} if the change existed before adding the override. */ - boolean addOverride(long changeId, String packageName, boolean enabled) { + boolean addOverride(long changeId, String packageName, boolean enabled) + throws RemoteException, SecurityException { boolean alreadyKnown = true; + OverrideAllowedState allowedState = + mOverrideValidator.getOverrideAllowedState(changeId, packageName); + allowedState.enforce(changeId, packageName); synchronized (mChanges) { CompatChange c = mChanges.get(changeId); if (c == null) { @@ -186,6 +187,20 @@ final class CompatConfig { } /** + * Returns the minimum sdk version for which this change should be enabled (or 0 if it is not + * target sdk gated). + */ + int minTargetSdkForChangeId(long changeId) { + synchronized (mChanges) { + CompatChange c = mChanges.get(changeId); + if (c == null) { + return 0; + } + return c.getEnableAfterTargetSdk(); + } + } + + /** * Removes an override previously added via {@link #addOverride(long, String, boolean)}. This * restores the default behaviour for the given change and app, once any app processes have been * restarted. @@ -194,34 +209,44 @@ final class CompatConfig { * @param packageName The app package name that was overridden. * @return {@code true} if an override existed; */ - boolean removeOverride(long changeId, String packageName) { + boolean removeOverride(long changeId, String packageName) + throws RemoteException, SecurityException { boolean overrideExists = false; synchronized (mChanges) { CompatChange c = mChanges.get(changeId); - if (c != null) { - overrideExists = true; - c.removePackageOverride(packageName); + try { + if (c != null) { + OverrideAllowedState allowedState = + mOverrideValidator.getOverrideAllowedState(changeId, packageName); + allowedState.enforce(changeId, packageName); + overrideExists = true; + c.removePackageOverride(packageName); + } + } catch (RemoteException e) { + // Should never occur, since validator is in the same process. + throw new RuntimeException("Unable to call override validator!", e); } } return overrideExists; } /** - * Overrides the enabled state for a given change and app. This method is intended to be used - * *only* for debugging purposes. + * Overrides the enabled state for a given change and app. * * <p>Note, package overrides are not persistent and will be lost on system or runtime restart. * * @param overrides list of overrides to default changes config. * @param packageName app for which the overrides will be applied. */ - void addOverrides(CompatibilityChangeConfig overrides, String packageName) { + void addOverrides(CompatibilityChangeConfig overrides, String packageName) + throws RemoteException, SecurityException { synchronized (mChanges) { for (Long changeId : overrides.enabledChanges()) { addOverride(changeId, packageName, true); } for (Long changeId : overrides.disabledChanges()) { addOverride(changeId, packageName, false); + } } } @@ -235,10 +260,22 @@ final class CompatConfig { * * @param packageName The package for which the overrides should be purged. */ - void removePackageOverrides(String packageName) { + void removePackageOverrides(String packageName) throws RemoteException, SecurityException { synchronized (mChanges) { for (int i = 0; i < mChanges.size(); ++i) { - mChanges.valueAt(i).removePackageOverride(packageName); + try { + CompatChange change = mChanges.valueAt(i); + OverrideAllowedState allowedState = + mOverrideValidator.getOverrideAllowedState(change.getId(), + packageName); + allowedState.enforce(change.getId(), packageName); + if (change != null) { + mChanges.valueAt(i).removePackageOverride(packageName); + } + } catch (RemoteException e) { + // Should never occur, since validator is in the same process. + throw new RuntimeException("Unable to call override validator!", e); + } } } } @@ -326,17 +363,23 @@ final class CompatConfig { } } - CompatConfig initConfigFromLib(File libraryDir) { + static CompatConfig create(AndroidBuildClassifier androidBuildClassifier, Context context) { + CompatConfig config = new CompatConfig(androidBuildClassifier, context); + config.initConfigFromLib(Environment.buildPath( + Environment.getRootDirectory(), "etc", "compatconfig")); + return config; + } + + void initConfigFromLib(File libraryDir) { if (!libraryDir.exists() || !libraryDir.isDirectory()) { Slog.e(TAG, "No directory " + libraryDir + ", skipping"); - return this; + return; } for (File f : libraryDir.listFiles()) { Slog.d(TAG, "Found a config file: " + f.getPath()); //TODO(b/138222363): Handle duplicate ids across config files. readConfig(f); } - return this; } private void readConfig(File configFile) { @@ -350,4 +393,7 @@ final class CompatConfig { } } + IOverrideValidator getOverrideValidator() { + return mOverrideValidator; + } } diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java new file mode 100644 index 000000000000..dfc00806992b --- /dev/null +++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.compat; + +import static com.android.internal.compat.OverrideAllowedState.ALLOWED; +import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK; +import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE; +import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH; +import static com.android.internal.compat.OverrideAllowedState.PACKAGE_DOES_NOT_EXIST; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.compat.AndroidBuildClassifier; +import com.android.internal.compat.IOverrideValidator; +import com.android.internal.compat.OverrideAllowedState; + +/** + * Implementation of the policy for allowing compat change overrides. + */ +public class OverrideValidatorImpl extends IOverrideValidator.Stub { + + private AndroidBuildClassifier mAndroidBuildClassifier; + private Context mContext; + private CompatConfig mCompatConfig; + + @VisibleForTesting + OverrideValidatorImpl(AndroidBuildClassifier androidBuildClassifier, + Context context, CompatConfig config) { + mAndroidBuildClassifier = androidBuildClassifier; + mContext = context; + mCompatConfig = config; + } + + @Override + public OverrideAllowedState getOverrideAllowedState(long changeId, String packageName) { + boolean debuggableBuild = false; + boolean finalBuild = false; + + debuggableBuild = mAndroidBuildClassifier.isDebuggableBuild(); + finalBuild = mAndroidBuildClassifier.isFinalBuild(); + + // Allow any override for userdebug or eng builds. + if (debuggableBuild) { + return new OverrideAllowedState(ALLOWED, -1, -1); + } + PackageManager packageManager = mContext.getPackageManager(); + if (packageManager == null) { + throw new IllegalStateException("No PackageManager!"); + } + ApplicationInfo applicationInfo; + try { + applicationInfo = packageManager.getApplicationInfo(packageName, 0); + } catch (NameNotFoundException e) { + return new OverrideAllowedState(PACKAGE_DOES_NOT_EXIST, -1, -1); + } + int appTargetSdk = applicationInfo.targetSdkVersion; + // Only allow overriding debuggable apps. + if ((applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) { + return new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1); + } + int minTargetSdk = mCompatConfig.minTargetSdkForChangeId(changeId); + // Do not allow overriding non-target sdk gated changes on user builds + if (minTargetSdk == -1) { + return new OverrideAllowedState(DISABLED_NON_TARGET_SDK, appTargetSdk, minTargetSdk); + } + // Allow overriding any change for debuggable apps on non-final builds. + if (!finalBuild) { + return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk); + } + // Only allow to opt-in for a targetSdk gated change. + if (applicationInfo.targetSdkVersion < minTargetSdk) { + return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk); + } + return new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, appTargetSdk, minTargetSdk); + } +} diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index 4a3d7d69d874..029b7bc32ef3 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -27,9 +27,12 @@ import android.os.UserHandle; import android.util.Slog; import android.util.StatsLog; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.compat.AndroidBuildClassifier; import com.android.internal.compat.ChangeReporter; import com.android.internal.compat.CompatibilityChangeConfig; import com.android.internal.compat.CompatibilityChangeInfo; +import com.android.internal.compat.IOverrideValidator; import com.android.internal.compat.IPlatformCompat; import com.android.internal.util.DumpUtils; @@ -45,11 +48,21 @@ public class PlatformCompat extends IPlatformCompat.Stub { private final Context mContext; private final ChangeReporter mChangeReporter; + private final CompatConfig mCompatConfig; public PlatformCompat(Context context) { mContext = context; mChangeReporter = new ChangeReporter( StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER); + mCompatConfig = CompatConfig.create(new AndroidBuildClassifier(), mContext); + } + + @VisibleForTesting + PlatformCompat(Context context, CompatConfig compatConfig) { + mContext = context; + mChangeReporter = new ChangeReporter( + StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER); + mCompatConfig = compatConfig; } @Override @@ -74,7 +87,7 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) { - if (CompatConfig.get().isChangeEnabled(changeId, appInfo)) { + if (mCompatConfig.isChangeEnabled(changeId, appInfo)) { reportChange(changeId, appInfo.uid, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED); return true; @@ -121,57 +134,59 @@ public class PlatformCompat extends IPlatformCompat.Stub { * otherwise. */ public boolean registerListener(long changeId, CompatChange.ChangeListener listener) { - return CompatConfig.get().registerListener(changeId, listener); + return mCompatConfig.registerListener(changeId, listener); } @Override - public void setOverrides(CompatibilityChangeConfig overrides, String packageName) { - CompatConfig.get().addOverrides(overrides, packageName); + public void setOverrides(CompatibilityChangeConfig overrides, String packageName) + throws RemoteException, SecurityException { + mCompatConfig.addOverrides(overrides, packageName); killPackage(packageName); } @Override - public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) { - CompatConfig.get().addOverrides(overrides, packageName); + public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) + throws RemoteException, SecurityException { + mCompatConfig.addOverrides(overrides, packageName); } @Override - public void clearOverrides(String packageName) { - CompatConfig config = CompatConfig.get(); - config.removePackageOverrides(packageName); + public void clearOverrides(String packageName) throws RemoteException, SecurityException { + mCompatConfig.removePackageOverrides(packageName); killPackage(packageName); } @Override - public void clearOverridesForTest(String packageName) { - CompatConfig config = CompatConfig.get(); - config.removePackageOverrides(packageName); + public void clearOverridesForTest(String packageName) + throws RemoteException, SecurityException { + mCompatConfig.removePackageOverrides(packageName); } @Override - public boolean clearOverride(long changeId, String packageName) { - boolean existed = CompatConfig.get().removeOverride(changeId, packageName); + public boolean clearOverride(long changeId, String packageName) + throws RemoteException, SecurityException { + boolean existed = mCompatConfig.removeOverride(changeId, packageName); killPackage(packageName); return existed; } @Override public CompatibilityChangeConfig getAppConfig(ApplicationInfo appInfo) { - return CompatConfig.get().getAppConfig(appInfo); + return mCompatConfig.getAppConfig(appInfo); } @Override public CompatibilityChangeInfo[] listAllChanges() { - return CompatConfig.get().dumpChanges(); + return mCompatConfig.dumpChanges(); } /** * Check whether the change is known to the compat config. - * @param changeId + * * @return {@code true} if the change is known. */ public boolean isKnownChangeId(long changeId) { - return CompatConfig.get().isKnownChangeId(changeId); + return mCompatConfig.isKnownChangeId(changeId); } @@ -181,11 +196,11 @@ public class PlatformCompat extends IPlatformCompat.Stub { * * @param appInfo The app in question * @return A sorted long array of change IDs. We use a primitive array to minimize memory - * footprint: Every app process will store this array statically so we aim to reduce - * overhead as much as possible. + * footprint: Every app process will store this array statically so we aim to reduce + * overhead as much as possible. */ public long[] getDisabledChanges(ApplicationInfo appInfo) { - return CompatConfig.get().getDisabledChanges(appInfo); + return mCompatConfig.getDisabledChanges(appInfo); } /** @@ -195,18 +210,24 @@ public class PlatformCompat extends IPlatformCompat.Stub { * @return The change ID, or {@code -1} if no change with that name exists. */ public long lookupChangeId(String name) { - return CompatConfig.get().lookupChangeId(name); + return mCompatConfig.lookupChangeId(name); } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return; - CompatConfig.get().dumpConfig(pw); + mCompatConfig.dumpConfig(pw); + } + + @Override + public IOverrideValidator getOverrideValidator() { + return mCompatConfig.getOverrideValidator(); } /** * Clears information stored about events reported on behalf of an app. * To be called once upon app start or end. A second call would be a no-op. + * * @param appInfo the app to reset */ public void resetReporting(ApplicationInfo appInfo) { diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index fa8c48bdc7f7..a89daff2d3bb 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -86,7 +86,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.WorkSource; import android.provider.Settings; -import android.text.format.Time; +import android.text.format.TimeMigrationUtils; import android.util.EventLog; import android.util.Log; import android.util.Pair; @@ -1987,9 +1987,7 @@ public class SyncManager { if (time == 0) { return "N/A"; } - Time tobj = new Time(); - tobj.set(time); - return tobj.format("%Y-%m-%d %H:%M:%S"); + return TimeMigrationUtils.formatMillisWithFixedFormat(time); } private final static Comparator<SyncOperation> sOpDumpComparator = (op1, op2) -> { @@ -2555,9 +2553,7 @@ public class SyncManager { accountKey = "Unknown"; } final long elapsedTime = item.elapsedTime; - final Time time = new Time(); final long eventTime = item.eventTime; - time.set(eventTime); final String key = authorityName + "/" + accountKey; final Long lastEventTime = lastTimeMap.get(key); @@ -2622,9 +2618,7 @@ public class SyncManager { authorityName = "Unknown"; accountKey = "Unknown"; } - final Time time = new Time(); final long eventTime = item.eventTime; - time.set(eventTime); pw.printf(" #%-3d: %s %8s ", i + 1, diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index eb5d472f7fbc..e655b35d930f 100644 --- a/services/core/java/com/android/server/job/controllers/JobStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -29,7 +29,7 @@ import android.net.Network; import android.net.Uri; import android.os.RemoteException; import android.os.UserHandle; -import android.text.format.Time; +import android.text.format.TimeMigrationUtils; import android.util.ArraySet; import android.util.Pair; import android.util.Slog; @@ -1643,17 +1643,13 @@ public final class JobStatus { if (numFailures != 0) { pw.print(prefix); pw.print("Num failures: "); pw.println(numFailures); } - final Time t = new Time(); - final String format = "%Y-%m-%d %H:%M:%S"; if (mLastSuccessfulRunTime != 0) { pw.print(prefix); pw.print("Last successful run: "); - t.set(mLastSuccessfulRunTime); - pw.println(t.format(format)); + pw.println(TimeMigrationUtils.formatMillisWithFixedFormat(mLastSuccessfulRunTime)); } if (mLastFailedRunTime != 0) { pw.print(prefix); pw.print("Last failed run: "); - t.set(mLastFailedRunTime); - pw.println(t.format(format)); + pw.println(TimeMigrationUtils.formatMillisWithFixedFormat(mLastFailedRunTime)); } } diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 2d8a2acd575f..ebba128b5f28 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -79,7 +79,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManagerInternal; import android.text.TextUtils; -import android.text.format.Time; +import android.text.format.TimeMigrationUtils; import android.util.ArraySet; import android.util.AtomicFile; import android.util.KeyValueListParser; @@ -3981,9 +3981,7 @@ public class ShortcutService extends IShortcutService.Stub { } static String formatTime(long time) { - Time tobj = new Time(); - tobj.set(time); - return tobj.format("%Y-%m-%d %H:%M:%S"); + return TimeMigrationUtils.formatMillisWithFixedFormat(time); } private void dumpCurrentTime(PrintWriter pw) { diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java index 172367a128cc..b7d63609cff9 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.timedetector.ITimeDetectorService; import android.app.timedetector.ManualTimeSuggestion; +import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.PhoneTimeSuggestion; import android.content.ContentResolver; import android.content.Context; @@ -105,6 +106,14 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { mHandler.post(() -> mTimeDetectorStrategy.suggestManualTime(timeSignal)); } + @Override + public void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSignal) { + enforceSuggestNetworkTimePermission(); + Objects.requireNonNull(timeSignal); + + mHandler.post(() -> mTimeDetectorStrategy.suggestNetworkTime(timeSignal)); + } + @VisibleForTesting public void handleAutoTimeDetectionToggle() { mHandler.post(mTimeDetectorStrategy::handleAutoTimeDetectionChanged); @@ -119,10 +128,20 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { } private void enforceSuggestPhoneTimePermission() { - mContext.enforceCallingPermission(android.Manifest.permission.SET_TIME, "set time"); + mContext.enforceCallingPermission( + android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE, + "suggest phone time and time zone"); } private void enforceSuggestManualTimePermission() { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SET_TIME, "set time"); + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE, + "suggest manual time and time zone"); + } + + private void enforceSuggestNetworkTimePermission() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.SET_TIME, + "set time"); } } diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java index 0a6c2e776072..b4f4eca5f188 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java @@ -19,6 +19,7 @@ package com.android.server.timedetector; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.timedetector.ManualTimeSuggestion; +import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.PhoneTimeSuggestion; import android.content.Intent; import android.util.TimestampedValue; @@ -86,6 +87,9 @@ public interface TimeDetectorStrategy { /** Process the suggested manually entered time. */ void suggestManualTime(@NonNull ManualTimeSuggestion timeSuggestion); + /** Process the suggested time from network sources. */ + void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSuggestion); + /** Handle the auto-time setting being toggled on or off. */ void handleAutoTimeDetectionChanged(); diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java index c50248d4b402..02656eaf5c6c 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; import android.app.timedetector.ManualTimeSuggestion; +import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.PhoneTimeSuggestion; import android.content.Intent; import android.telephony.TelephonyManager; @@ -32,6 +33,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.server.timezonedetector.ArrayMapWithHistory; +import com.android.server.timezonedetector.ReferenceWithHistory; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -56,11 +58,11 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { /** Each bucket is this size. All buckets are equally sized. */ @VisibleForTesting static final int PHONE_BUCKET_SIZE_MILLIS = 60 * 60 * 1000; - /** Phone suggestions older than this value are considered too old. */ + /** Phone and network suggestions older than this value are considered too old to be used. */ @VisibleForTesting - static final long PHONE_MAX_AGE_MILLIS = PHONE_BUCKET_COUNT * PHONE_BUCKET_SIZE_MILLIS; + static final long MAX_UTC_TIME_AGE_MILLIS = PHONE_BUCKET_COUNT * PHONE_BUCKET_SIZE_MILLIS; - @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL }) + @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL, ORIGIN_NETWORK }) @Retention(RetentionPolicy.SOURCE) public @interface Origin {} @@ -72,6 +74,10 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { @Origin private static final int ORIGIN_MANUAL = 2; + /** Used when a time value originated from a network signal. */ + @Origin + private static final int ORIGIN_NETWORK = 3; + /** * CLOCK_PARANOIA: The maximum difference allowed between the expected system clock time and the * actual system clock time before a warning is logged. Used to help identify situations where @@ -101,9 +107,13 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { * will have a small number of telephony devices and phoneIds are assumed to be stable. */ @GuardedBy("this") - private ArrayMapWithHistory<Integer, PhoneTimeSuggestion> mSuggestionByPhoneId = + private final ArrayMapWithHistory<Integer, PhoneTimeSuggestion> mSuggestionByPhoneId = new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE); + @GuardedBy("this") + private final ReferenceWithHistory<NetworkTimeSuggestion> mLastNetworkSuggestion = + new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE); + @Override public void initialize(@NonNull Callback callback) { mCallback = callback; @@ -122,6 +132,19 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } @Override + public synchronized void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSuggestion) { + if (!validateSuggestionTime(timeSuggestion.getUtcTime(), timeSuggestion)) { + return; + } + mLastNetworkSuggestion.set(timeSuggestion); + + // Now perform auto time detection. The new suggestion may be used to modify the system + // clock. + String reason = "New network time suggested. timeSuggestion=" + timeSuggestion; + doAutoTimeDetection(reason); + } + + @Override public synchronized void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) { // Empty time suggestion means that telephony network connectivity has been lost. // The passage of time is relentless, and we don't expect our users to use a time machine, @@ -167,6 +190,12 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { ipw.increaseIndent(); // level 1 ipw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet); + ipw.println("mCallback.isAutoTimeDetectionEnabled()=" + + mCallback.isAutoTimeDetectionEnabled()); + ipw.println("mCallback.elapsedRealtimeMillis()=" + mCallback.elapsedRealtimeMillis()); + ipw.println("mCallback.systemClockMillis()=" + mCallback.systemClockMillis()); + ipw.println("mCallback.systemClockUpdateThresholdMillis()=" + + mCallback.systemClockUpdateThresholdMillis()); ipw.println("Time change log:"); ipw.increaseIndent(); // level 2 @@ -178,6 +207,11 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { mSuggestionByPhoneId.dump(ipw); ipw.decreaseIndent(); // level 2 + ipw.println("Network suggestion history:"); + ipw.increaseIndent(); // level 2 + mLastNetworkSuggestion.dump(ipw); + ipw.decreaseIndent(); // level 2 + ipw.decreaseIndent(); // level 1 ipw.flush(); } @@ -247,23 +281,34 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { return; } + // Android devices currently prioritize any telephony over network signals. There are + // carrier compliance tests that would need to be changed before we could ignore NITZ or + // prefer NTP generally. This check is cheap on devices without phone hardware. PhoneTimeSuggestion bestPhoneSuggestion = findBestPhoneSuggestion(); + if (bestPhoneSuggestion != null) { + final TimestampedValue<Long> newUtcTime = bestPhoneSuggestion.getUtcTime(); + String cause = "Found good phone suggestion." + + ", bestPhoneSuggestion=" + bestPhoneSuggestion + + ", detectionReason=" + detectionReason; + setSystemClockIfRequired(ORIGIN_PHONE, newUtcTime, cause); + return; + } - // Work out what to do with the best suggestion. - if (bestPhoneSuggestion == null) { - // There is no good phone suggestion. - if (DBG) { - Slog.d(LOG_TAG, "Could not determine time: No best phone suggestion." - + " detectionReason=" + detectionReason); - } + // There is no good phone suggestion, try network. + NetworkTimeSuggestion networkSuggestion = findLatestValidNetworkSuggestion(); + if (networkSuggestion != null) { + final TimestampedValue<Long> newUtcTime = networkSuggestion.getUtcTime(); + String cause = "Found good network suggestion." + + ", networkSuggestion=" + networkSuggestion + + ", detectionReason=" + detectionReason; + setSystemClockIfRequired(ORIGIN_NETWORK, newUtcTime, cause); return; } - final TimestampedValue<Long> newUtcTime = bestPhoneSuggestion.getUtcTime(); - String cause = "Found good suggestion." - + ", bestPhoneSuggestion=" + bestPhoneSuggestion - + ", detectionReason=" + detectionReason; - setSystemClockIfRequired(ORIGIN_PHONE, newUtcTime, cause); + if (DBG) { + Slog.d(LOG_TAG, "Could not determine time: No best phone or network suggestion." + + " detectionReason=" + detectionReason); + } } @GuardedBy("this") @@ -342,37 +387,50 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { private static int scorePhoneSuggestion( long elapsedRealtimeMillis, @NonNull PhoneTimeSuggestion timeSuggestion) { - // The score is based on the age since receipt. Suggestions are bucketed so two - // suggestions in the same bucket from different phoneIds are scored the same. + + // Validate first. TimestampedValue<Long> utcTime = timeSuggestion.getUtcTime(); - long referenceTimeMillis = utcTime.getReferenceTimeMillis(); - if (referenceTimeMillis > elapsedRealtimeMillis) { - // Future times are ignored. They imply the reference time was wrong, or the elapsed - // realtime clock has gone backwards, neither of which are supportable situations. - Slog.w(LOG_TAG, "Existing suggestion found to be in the future. " + if (!validateSuggestionUtcTime(elapsedRealtimeMillis, utcTime)) { + Slog.w(LOG_TAG, "Existing suggestion found to be invalid " + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + ", timeSuggestion=" + timeSuggestion); return PHONE_INVALID_SCORE; } - long ageMillis = elapsedRealtimeMillis - referenceTimeMillis; + // The score is based on the age since receipt. Suggestions are bucketed so two + // suggestions in the same bucket from different phoneIds are scored the same. + long ageMillis = elapsedRealtimeMillis - utcTime.getReferenceTimeMillis(); - // Any suggestion > MAX_AGE_MILLIS is treated as too old. Although time is relentless and - // predictable, the accuracy of the reference time clock may be poor over long periods which - // would lead to errors creeping in. Also, in edge cases where a bad suggestion has been - // made and never replaced, it could also mean that the time detection code remains - // opinionated using a bad invalid suggestion. This caps that edge case at MAX_AGE_MILLIS. - if (ageMillis > PHONE_MAX_AGE_MILLIS) { + // Turn the age into a discrete value: 0 <= bucketIndex < PHONE_BUCKET_COUNT. + int bucketIndex = (int) (ageMillis / PHONE_BUCKET_SIZE_MILLIS); + if (bucketIndex >= PHONE_BUCKET_COUNT) { return PHONE_INVALID_SCORE; } - // Turn the age into a discrete value: 0 <= bucketIndex < MAX_AGE_HOURS. - int bucketIndex = (int) (ageMillis / PHONE_BUCKET_SIZE_MILLIS); - // We want the lowest bucket index to have the highest score. 0 > score >= BUCKET_COUNT. return PHONE_BUCKET_COUNT - bucketIndex; } + /** Returns the latest, valid, network suggestion. Returns {@code null} if there isn't one. */ + @GuardedBy("this") + @Nullable + private NetworkTimeSuggestion findLatestValidNetworkSuggestion() { + NetworkTimeSuggestion networkSuggestion = mLastNetworkSuggestion.get(); + if (networkSuggestion == null) { + // No network suggestions received. This is normal if there's no connectivity. + return null; + } + + TimestampedValue<Long> utcTime = networkSuggestion.getUtcTime(); + long elapsedRealTimeMillis = mCallback.elapsedRealtimeMillis(); + if (!validateSuggestionUtcTime(elapsedRealTimeMillis, utcTime)) { + // The latest suggestion is not valid, usually due to its age. + return null; + } + + return networkSuggestion; + } + @GuardedBy("this") private void setSystemClockIfRequired( @Origin int origin, @NonNull TimestampedValue<Long> time, @NonNull String cause) { @@ -409,7 +467,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } private static boolean isOriginAutomatic(@Origin int origin) { - return origin == ORIGIN_PHONE; + return origin != ORIGIN_MANUAL; } @GuardedBy("this") @@ -501,6 +559,16 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } /** + * Returns the latest valid network suggestion. Not intended for general use: it is used during + * tests to check strategy behavior. + */ + @VisibleForTesting + @Nullable + public NetworkTimeSuggestion findLatestValidNetworkSuggestionForTests() { + return findLatestValidNetworkSuggestion(); + } + + /** * A method used to inspect state during tests. Not intended for general use. */ @VisibleForTesting @@ -508,4 +576,32 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { public synchronized PhoneTimeSuggestion getLatestPhoneSuggestion(int phoneId) { return mSuggestionByPhoneId.get(phoneId); } + + /** + * A method used to inspect state during tests. Not intended for general use. + */ + @VisibleForTesting + @Nullable + public NetworkTimeSuggestion getLatestNetworkSuggestion() { + return mLastNetworkSuggestion.get(); + } + + private static boolean validateSuggestionUtcTime( + long elapsedRealtimeMillis, TimestampedValue<Long> utcTime) { + long referenceTimeMillis = utcTime.getReferenceTimeMillis(); + if (referenceTimeMillis > elapsedRealtimeMillis) { + // Future reference times are ignored. They imply the reference time was wrong, or the + // elapsed realtime clock used to derive it has gone backwards, neither of which are + // supportable situations. + return false; + } + + // Any suggestion > MAX_AGE_MILLIS is treated as too old. Although time is relentless and + // predictable, the accuracy of the reference time clock may be poor over long periods which + // would lead to errors creeping in. Also, in edge cases where a bad suggestion has been + // made and never replaced, it could also mean that the time detection code remains + // opinionated using a bad invalid suggestion. This caps that edge case at MAX_AGE_MILLIS. + long ageMillis = elapsedRealtimeMillis - referenceTimeMillis; + return ageMillis <= MAX_UTC_TIME_AGE_MILLIS; + } } diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 18ed51a6cd5e..5b58199aec4f 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -1814,8 +1814,8 @@ public final class TvInputManagerService extends SystemService { } @Override - public ParcelFileDescriptor openDvbDevice(DvbDeviceInfo info, int device) - throws RemoteException { + public ParcelFileDescriptor openDvbDevice(DvbDeviceInfo info, + @TvInputManager.DvbDeviceType int deviceType) throws RemoteException { if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires DVB_DEVICE permission"); @@ -1852,7 +1852,7 @@ public final class TvInputManagerService extends SystemService { final long identity = Binder.clearCallingIdentity(); try { String deviceFileName; - switch (device) { + switch (deviceType) { case TvInputManager.DVB_DEVICE_DEMUX: deviceFileName = String.format(dvbDeviceFound ? "/dev/dvb/adapter%d/demux%d" : "/dev/dvb%d.demux%d", @@ -1869,14 +1869,14 @@ public final class TvInputManagerService extends SystemService { info.getAdapterId(), info.getDeviceId()); break; default: - throw new IllegalArgumentException("Invalid DVB device: " + device); + throw new IllegalArgumentException("Invalid DVB device: " + deviceType); } try { // The DVB frontend device only needs to be opened in read/write mode, which // allows performing tuning operations. The DVB demux and DVR device are enough // to be opened in read only mode. return ParcelFileDescriptor.open(new File(deviceFileName), - TvInputManager.DVB_DEVICE_FRONTEND == device + TvInputManager.DVB_DEVICE_FRONTEND == deviceType ? ParcelFileDescriptor.MODE_READ_WRITE : ParcelFileDescriptor.MODE_READ_ONLY); } catch (FileNotFoundException e) { diff --git a/services/core/java/com/android/server/wallpaper/GLHelper.java b/services/core/java/com/android/server/wallpaper/GLHelper.java new file mode 100644 index 000000000000..1d733f53f055 --- /dev/null +++ b/services/core/java/com/android/server/wallpaper/GLHelper.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wallpaper; + +import static android.opengl.EGL14.EGL_ALPHA_SIZE; +import static android.opengl.EGL14.EGL_BLUE_SIZE; +import static android.opengl.EGL14.EGL_CONFIG_CAVEAT; +import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION; +import static android.opengl.EGL14.EGL_DEFAULT_DISPLAY; +import static android.opengl.EGL14.EGL_DEPTH_SIZE; +import static android.opengl.EGL14.EGL_GREEN_SIZE; +import static android.opengl.EGL14.EGL_HEIGHT; +import static android.opengl.EGL14.EGL_NONE; +import static android.opengl.EGL14.EGL_NO_CONTEXT; +import static android.opengl.EGL14.EGL_NO_DISPLAY; +import static android.opengl.EGL14.EGL_NO_SURFACE; +import static android.opengl.EGL14.EGL_OPENGL_ES2_BIT; +import static android.opengl.EGL14.EGL_RED_SIZE; +import static android.opengl.EGL14.EGL_RENDERABLE_TYPE; +import static android.opengl.EGL14.EGL_STENCIL_SIZE; +import static android.opengl.EGL14.EGL_WIDTH; +import static android.opengl.EGL14.eglChooseConfig; +import static android.opengl.EGL14.eglCreateContext; +import static android.opengl.EGL14.eglCreatePbufferSurface; +import static android.opengl.EGL14.eglDestroyContext; +import static android.opengl.EGL14.eglDestroySurface; +import static android.opengl.EGL14.eglGetDisplay; +import static android.opengl.EGL14.eglGetError; +import static android.opengl.EGL14.eglInitialize; +import static android.opengl.EGL14.eglMakeCurrent; +import static android.opengl.EGL14.eglTerminate; +import static android.opengl.GLES20.GL_MAX_TEXTURE_SIZE; +import static android.opengl.GLES20.glGetIntegerv; + +import android.opengl.EGLConfig; +import android.opengl.EGLContext; +import android.opengl.EGLDisplay; +import android.opengl.EGLSurface; +import android.opengl.GLUtils; +import android.os.SystemProperties; +import android.util.Log; + +class GLHelper { + private static final String TAG = GLHelper.class.getSimpleName(); + private static final int sMaxTextureSize; + + static { + int maxTextureSize = SystemProperties.getInt("sys.max_texture_size", 0); + sMaxTextureSize = maxTextureSize > 0 ? maxTextureSize : retrieveTextureSizeFromGL(); + } + + private static int retrieveTextureSizeFromGL() { + try { + String err; + + // Before we can retrieve info from GL, + // we have to create EGLContext, EGLConfig and EGLDisplay first. + // We will fail at querying info from GL once one of above failed. + // When this happens, we will use defValue instead. + EGLDisplay eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (eglDisplay == null || eglDisplay == EGL_NO_DISPLAY) { + err = "eglGetDisplay failed: " + GLUtils.getEGLErrorString(eglGetError()); + throw new RuntimeException(err); + } + + if (!eglInitialize(eglDisplay, null, 0 /* majorOffset */, null, 1 /* minorOffset */)) { + err = "eglInitialize failed: " + GLUtils.getEGLErrorString(eglGetError()); + throw new RuntimeException(err); + } + + EGLConfig eglConfig = null; + int[] configsCount = new int[1]; + EGLConfig[] configs = new EGLConfig[1]; + int[] configSpec = new int[] { + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 0, + EGL_DEPTH_SIZE, 0, + EGL_STENCIL_SIZE, 0, + EGL_CONFIG_CAVEAT, EGL_NONE, + EGL_NONE + }; + + if (!eglChooseConfig(eglDisplay, configSpec, 0 /* attrib_listOffset */, + configs, 0 /* configOffset */, 1 /* config_size */, + configsCount, 0 /* num_configOffset */)) { + err = "eglChooseConfig failed: " + GLUtils.getEGLErrorString(eglGetError()); + throw new RuntimeException(err); + } else if (configsCount[0] > 0) { + eglConfig = configs[0]; + } + + if (eglConfig == null) { + throw new RuntimeException("eglConfig not initialized!"); + } + + int[] attr_list = new int[] {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; + EGLContext eglContext = eglCreateContext( + eglDisplay, eglConfig, EGL_NO_CONTEXT, attr_list, 0 /* offset */); + + if (eglContext == null || eglContext == EGL_NO_CONTEXT) { + err = "eglCreateContext failed: " + GLUtils.getEGLErrorString(eglGetError()); + throw new RuntimeException(err); + } + + // We create a push buffer temporarily for querying info from GL. + int[] attrs = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE}; + EGLSurface eglSurface = + eglCreatePbufferSurface(eglDisplay, eglConfig, attrs, 0 /* offset */); + eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext); + + // Now, we are ready to query the info from GL. + int[] maxSize = new int[1]; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, maxSize, 0 /* offset */); + + // We have got the info we want, release all egl resources. + eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroySurface(eglDisplay, eglSurface); + eglDestroyContext(eglDisplay, eglContext); + eglTerminate(eglDisplay); + return maxSize[0]; + } catch (RuntimeException e) { + Log.w(TAG, "Retrieve from GL failed", e); + return Integer.MAX_VALUE; + } + } + + static int getMaxTextureSize() { + return sMaxTextureSize; + } +} + diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index b0f1e5d69be4..991c09a97bf5 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -134,6 +134,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub private static final boolean DEBUG = false; private static final boolean DEBUG_LIVE = true; + // This 100MB limitation is defined in RecordingCanvas. + private static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024; + public static class Lifecycle extends SystemService { private IWallpaperManagerService mService; @@ -572,7 +575,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // Only generate crop for default display. final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY); - Rect cropHint = new Rect(wallpaper.cropHint); + final Rect cropHint = new Rect(wallpaper.cropHint); + final DisplayInfo displayInfo = new DisplayInfo(); + mDisplayManager.getDisplay(DEFAULT_DISPLAY).getDisplayInfo(displayInfo); if (DEBUG) { Slog.v(TAG, "Generating crop for new wallpaper(s): 0x" @@ -618,12 +623,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } // scale if the crop height winds up not matching the recommended metrics - needScale = (wpData.mHeight != cropHint.height()); + needScale = wpData.mHeight != cropHint.height() + || cropHint.height() > GLHelper.getMaxTextureSize() + || cropHint.width() > GLHelper.getMaxTextureSize(); //make sure screen aspect ratio is preserved if width is scaled under screen size if (needScale) { - final DisplayInfo displayInfo = new DisplayInfo(); - mDisplayManager.getDisplay(DEFAULT_DISPLAY).getDisplayInfo(displayInfo); final float scaleByHeight = (float) wpData.mHeight / (float) cropHint.height(); final int newWidth = (int) (cropHint.width() * scaleByHeight); if (newWidth < displayInfo.logicalWidth) { @@ -644,14 +649,29 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (!needCrop && !needScale) { // Simple case: the nominal crop fits what we want, so we take // the whole thing and just copy the image file directly. - if (DEBUG) { - Slog.v(TAG, "Null crop of new wallpaper; copying"); + + // TODO: It is not accurate to estimate bitmap size without decoding it, + // may be we can try to remove this optimized way in the future, + // that means, we will always go into the 'else' block. + + // This is just a quick estimation, may be smaller than it is. + long estimateSize = options.outWidth * options.outHeight * 4; + + // A bitmap over than MAX_BITMAP_SIZE will make drawBitmap() fail. + // Please see: RecordingCanvas#throwIfCannotDraw. + if (estimateSize < MAX_BITMAP_SIZE) { + success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile); } - success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile); + if (!success) { wallpaper.cropFile.delete(); // TODO: fall back to default wallpaper in this case } + + if (DEBUG) { + Slog.v(TAG, "Null crop of new wallpaper, estimate size=" + + estimateSize + ", success=" + success); + } } else { // Fancy case: crop and scale. First, we decode and scale down if appropriate. FileOutputStream f = null; @@ -665,49 +685,78 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // We calculate the largest power-of-two under the actual ratio rather than // just let the decode take care of it because we also want to remap where the // cropHint rectangle lies in the decoded [super]rect. - final BitmapFactory.Options scaler; final int actualScale = cropHint.height() / wpData.mHeight; int scale = 1; - while (2*scale < actualScale) { + while (2 * scale <= actualScale) { scale *= 2; } - if (scale > 1) { - scaler = new BitmapFactory.Options(); - scaler.inSampleSize = scale; + options.inSampleSize = scale; + options.inJustDecodeBounds = false; + + final Rect estimateCrop = new Rect(cropHint); + estimateCrop.scale(1f / options.inSampleSize); + final float hRatio = (float) wpData.mHeight / estimateCrop.height(); + final int destHeight = (int) (estimateCrop.height() * hRatio); + final int destWidth = (int) (estimateCrop.width() * hRatio); + + // We estimated an invalid crop, try to adjust the cropHint to get a valid one. + if (destWidth > GLHelper.getMaxTextureSize()) { + int newHeight = (int) (wpData.mHeight / hRatio); + int newWidth = (int) (wpData.mWidth / hRatio); + if (DEBUG) { - Slog.v(TAG, "Downsampling cropped rect with scale " + scale); + Slog.v(TAG, "Invalid crop dimensions, trying to adjust."); } - } else { - scaler = null; + + estimateCrop.set(cropHint); + estimateCrop.left += (cropHint.width() - newWidth) / 2; + estimateCrop.top += (cropHint.height() - newHeight) / 2; + estimateCrop.right = estimateCrop.left + newWidth; + estimateCrop.bottom = estimateCrop.top + newHeight; + cropHint.set(estimateCrop); + estimateCrop.scale(1f / options.inSampleSize); + } + + // We've got the safe cropHint; now we want to scale it properly to + // the desired rectangle. + // That's a height-biased operation: make it fit the hinted height. + final int safeHeight = (int) (estimateCrop.height() * hRatio); + final int safeWidth = (int) (estimateCrop.width() * hRatio); + + if (DEBUG) { + Slog.v(TAG, "Decode parameters:"); + Slog.v(TAG, " cropHint=" + cropHint + ", estimateCrop=" + estimateCrop); + Slog.v(TAG, " down sampling=" + options.inSampleSize + + ", hRatio=" + hRatio); + Slog.v(TAG, " dest=" + destWidth + "x" + destHeight); + Slog.v(TAG, " safe=" + safeWidth + "x" + safeHeight); + Slog.v(TAG, " maxTextureSize=" + GLHelper.getMaxTextureSize()); } - Bitmap cropped = decoder.decodeRegion(cropHint, scaler); + + Bitmap cropped = decoder.decodeRegion(cropHint, options); decoder.recycle(); if (cropped == null) { Slog.e(TAG, "Could not decode new wallpaper"); } else { - // We've got the extracted crop; now we want to scale it properly to - // the desired rectangle. That's a height-biased operation: make it - // fit the hinted height, and accept whatever width we end up with. - cropHint.offsetTo(0, 0); - cropHint.right /= scale; // adjust by downsampling factor - cropHint.bottom /= scale; - final float heightR = - ((float) wpData.mHeight) / ((float) cropHint.height()); - if (DEBUG) { - Slog.v(TAG, "scale " + heightR + ", extracting " + cropHint); - } - final int destWidth = (int)(cropHint.width() * heightR); + // We are safe to create final crop with safe dimensions now. final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped, - destWidth, wpData.mHeight, true); + safeWidth, safeHeight, true); if (DEBUG) { Slog.v(TAG, "Final extract:"); Slog.v(TAG, " dims: w=" + wpData.mWidth + " h=" + wpData.mHeight); - Slog.v(TAG, " out: w=" + finalCrop.getWidth() + Slog.v(TAG, " out: w=" + finalCrop.getWidth() + " h=" + finalCrop.getHeight()); } + // A bitmap over than MAX_BITMAP_SIZE will make drawBitmap() fail. + // Please see: RecordingCanvas#throwIfCannotDraw. + if (finalCrop.getByteCount() > MAX_BITMAP_SIZE) { + throw new RuntimeException( + "Too large bitmap, limit=" + MAX_BITMAP_SIZE); + } + f = new FileOutputStream(wallpaper.cropFile); bos = new BufferedOutputStream(f, 32*1024); finalCrop.compress(Bitmap.CompressFormat.JPEG, 100, bos); @@ -1981,6 +2030,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (!isWallpaperSupported(callingPackage)) { return; } + + // Make sure both width and height are not larger than max texture size. + width = Math.min(width, GLHelper.getMaxTextureSize()); + height = Math.min(height, GLHelper.getMaxTextureSize()); + synchronized (mLock) { int userId = UserHandle.getCallingUserId(); WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM); diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 2b1c07ba40b1..3b66c72c1025 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -124,6 +124,7 @@ cc_defaults { "android.hardware.thermal@1.0", "android.hardware.tv.cec@1.0", "android.hardware.tv.input@1.0", + "android.hardware.vibrator-cpp", "android.hardware.vibrator@1.0", "android.hardware.vibrator@1.1", "android.hardware.vibrator@1.2", @@ -133,7 +134,6 @@ cc_defaults { "android.frameworks.sensorservice@1.0", "android.system.suspend@1.0", "suspend_control_aidl_interface-cpp", - "vintf-vibrator-cpp", ], static_libs: [ diff --git a/services/core/jni/com_android_server_net_NetworkStatsService.cpp b/services/core/jni/com_android_server_net_NetworkStatsService.cpp index 4d4a7b41643c..4696dd0bb88b 100644 --- a/services/core/jni/com_android_server_net_NetworkStatsService.cpp +++ b/services/core/jni/com_android_server_net_NetworkStatsService.cpp @@ -54,7 +54,7 @@ enum StatsType { TCP_TX_PACKETS = 5 }; -static uint64_t getStatsType(struct Stats* stats, StatsType type) { +static uint64_t getStatsType(Stats* stats, StatsType type) { switch (type) { case RX_BYTES: return stats->rxBytes; @@ -73,7 +73,7 @@ static uint64_t getStatsType(struct Stats* stats, StatsType type) { } } -static int parseIfaceStats(const char* iface, struct Stats* stats) { +static int parseIfaceStats(const char* iface, Stats* stats) { FILE *fp = fopen(QTAGUID_IFACE_STATS, "r"); if (fp == NULL) { return -1; @@ -117,7 +117,7 @@ static int parseIfaceStats(const char* iface, struct Stats* stats) { return 0; } -static int parseUidStats(const uint32_t uid, struct Stats* stats) { +static int parseUidStats(const uint32_t uid, Stats* stats) { FILE *fp = fopen(QTAGUID_UID_STATS, "r"); if (fp == NULL) { return -1; @@ -150,8 +150,7 @@ static int parseUidStats(const uint32_t uid, struct Stats* stats) { } static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type, jboolean useBpfStats) { - struct Stats stats; - memset(&stats, 0, sizeof(Stats)); + Stats stats = {}; if (useBpfStats) { if (bpfGetIfaceStats(NULL, &stats) == 0) { @@ -175,8 +174,7 @@ static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type, return UNKNOWN; } - struct Stats stats; - memset(&stats, 0, sizeof(Stats)); + Stats stats = {}; if (useBpfStats) { if (bpfGetIfaceStats(iface8.c_str(), &stats) == 0) { @@ -194,8 +192,7 @@ static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type, } static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type, jboolean useBpfStats) { - struct Stats stats; - memset(&stats, 0, sizeof(Stats)); + Stats stats = {}; if (useBpfStats) { if (bpfGetUidStats(uid, &stats) == 0) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 127b8e016d15..952a64cb2d7c 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -206,6 +206,7 @@ import android.provider.ContactsContract.QuickContact; import android.provider.ContactsInternal; import android.provider.Settings; import android.provider.Settings.Global; +import android.provider.Telephony; import android.security.IKeyChainAliasCallback; import android.security.IKeyChainService; import android.security.KeyChain; @@ -246,6 +247,7 @@ import com.android.internal.telephony.SmsApplication; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.FunctionalUtils.ThrowingRunnable; +import com.android.internal.util.FunctionalUtils.ThrowingSupplier; import com.android.internal.util.JournaledFile; import com.android.internal.util.Preconditions; import com.android.internal.util.StatLogger; @@ -2061,6 +2063,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Binder.withCleanCallingIdentity(action); } + final <T> T binderWithCleanCallingIdentity(@NonNull ThrowingSupplier<T> action) { + return Binder.withCleanCallingIdentity(action); + } + final int userHandleGetCallingUserId() { return UserHandle.getUserId(binderGetCallingUid()); } @@ -13936,23 +13942,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkNotNull(apnSetting, "ApnSetting is null in addOverrideApn"); enforceDeviceOwner(who); - int operatedId = -1; - Uri resultUri; - final long id = mInjector.binderClearCallingIdentity(); - try { - resultUri = mContext.getContentResolver().insert(DPC_URI, apnSetting.toContentValues()); - } finally { - mInjector.binderRestoreCallingIdentity(id); - } - if (resultUri != null) { - try { - operatedId = Integer.parseInt(resultUri.getLastPathSegment()); - } catch (NumberFormatException e) { - Slog.e(LOG_TAG, "Failed to parse inserted override APN id.", e); - } + TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); + if (tm != null) { + return mInjector.binderWithCleanCallingIdentity( + () -> tm.addDevicePolicyOverrideApn(mContext, apnSetting)); + } else { + Log.w(LOG_TAG, "TelephonyManager is null when trying to add override apn"); + return Telephony.Carriers.INVALID_APN_ID; } - - return operatedId; } @Override @@ -13968,13 +13965,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (apnId < 0) { return false; } - final long id = mInjector.binderClearCallingIdentity(); - try { - return mContext.getContentResolver().update( - Uri.withAppendedPath(DPC_URI, Integer.toString(apnId)), - apnSetting.toContentValues(), null, null) > 0; - } finally { - mInjector.binderRestoreCallingIdentity(id); + TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); + if (tm != null) { + return mInjector.binderWithCleanCallingIdentity( + () -> tm.modifyDevicePolicyOverrideApn(mContext, apnId, apnSetting)); + } else { + Log.w(LOG_TAG, "TelephonyManager is null when trying to modify override apn"); + return false; } } @@ -14016,28 +14013,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } private List<ApnSetting> getOverrideApnsUnchecked() { - final Cursor cursor; - final long id = mInjector.binderClearCallingIdentity(); - try { - cursor = mContext.getContentResolver().query(DPC_URI, null, null, null, null); - } finally { - mInjector.binderRestoreCallingIdentity(id); - } - - if (cursor == null) { - return Collections.emptyList(); - } - try { - List<ApnSetting> apnList = new ArrayList<ApnSetting>(); - cursor.moveToPosition(-1); - while (cursor.moveToNext()) { - ApnSetting apn = ApnSetting.makeApnSetting(cursor); - apnList.add(apn); - } - return apnList; - } finally { - cursor.close(); + TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); + if (tm != null) { + return mInjector.binderWithCleanCallingIdentity( + () -> tm.getDevicePolicyOverrideApns(mContext)); } + Log.w(LOG_TAG, "TelephonyManager is null when trying to get override apns"); + return Collections.emptyList(); } @Override diff --git a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java index f3c76b609c25..8871348d0027 100644 --- a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.when; import android.app.admin.DevicePolicyManagerInternal; import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetManagerInternal; import android.appwidget.AppWidgetProviderInfo; import android.appwidget.PendingHostUpdate; import android.content.BroadcastReceiver; @@ -80,6 +81,7 @@ public class AppWidgetServiceImplTest extends InstrumentationTestCase { super.setUp(); LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); LocalServices.removeServiceForTest(ShortcutServiceInternal.class); + LocalServices.removeServiceForTest(AppWidgetManagerInternal.class); mTestContext = new TestContext(); mPkgName = mTestContext.getOpPackageName(); diff --git a/services/tests/servicestests/src/com/android/server/compat/ApplicationInfoBuilder.java b/services/tests/servicestests/src/com/android/server/compat/ApplicationInfoBuilder.java new file mode 100644 index 000000000000..d0767ccb6f87 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/compat/ApplicationInfoBuilder.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.compat; + +import android.content.pm.ApplicationInfo; + +class ApplicationInfoBuilder { + private boolean mIsDebuggable; + private int mTargetSdk; + private String mPackageName; + + private ApplicationInfoBuilder() { + mTargetSdk = -1; + } + + static ApplicationInfoBuilder create() { + return new ApplicationInfoBuilder(); + } + + ApplicationInfoBuilder withTargetSdk(int targetSdk) { + mTargetSdk = targetSdk; + return this; + } + + ApplicationInfoBuilder debuggable() { + mIsDebuggable = true; + return this; + } + + ApplicationInfoBuilder withPackageName(String packageName) { + mPackageName = packageName; + return this; + } + + ApplicationInfo build() { + final ApplicationInfo applicationInfo = new ApplicationInfo(); + if (mIsDebuggable) { + applicationInfo.flags |= ApplicationInfo.FLAG_DEBUGGABLE; + } + applicationInfo.packageName = mPackageName; + applicationInfo.targetSdkVersion = mTargetSdk; + return applicationInfo; + } +} diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java new file mode 100644 index 000000000000..328c71dbc7db --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.compat; + +import android.content.Context; + +import com.android.internal.compat.AndroidBuildClassifier; + +import java.util.ArrayList; + +/** + * Helper class for creating a CompatConfig. + */ +class CompatConfigBuilder { + private ArrayList<CompatChange> mChanges; + private AndroidBuildClassifier mBuildClassifier; + private Context mContext; + + private CompatConfigBuilder(AndroidBuildClassifier buildClassifier, Context context) { + mChanges = new ArrayList<>(); + mBuildClassifier = buildClassifier; + mContext = context; + } + + static CompatConfigBuilder create(AndroidBuildClassifier buildClassifier, Context context) { + return new CompatConfigBuilder(buildClassifier, context); + } + + CompatConfigBuilder addTargetSdkChangeWithId(int sdk, long id) { + mChanges.add(new CompatChange(id, "", sdk, false, "")); + return this; + } + + CompatConfigBuilder addTargetSdkDisabledChangeWithId(int sdk, long id) { + mChanges.add(new CompatChange(id, "", sdk, true, "")); + return this; + } + + CompatConfigBuilder addTargetSdkChangeWithIdAndName(int sdk, long id, String name) { + mChanges.add(new CompatChange(id, name, sdk, false, "")); + return this; + } + + CompatConfigBuilder addTargetSdkChangeWithIdAndDescription(int sdk, long id, + String description) { + mChanges.add(new CompatChange(id, "", sdk, false, description)); + return this; + } + + CompatConfigBuilder addEnabledChangeWithId(long id) { + mChanges.add(new CompatChange(id, "", -1, false, "")); + return this; + } + + CompatConfigBuilder addEnabledChangeWithIdAndName(long id, String name) { + mChanges.add(new CompatChange(id, name, -1, false, "")); + return this; + } + CompatConfigBuilder addEnabledChangeWithIdAndDescription(long id, String description) { + mChanges.add(new CompatChange(id, "", -1, false, description)); + return this; + } + + CompatConfigBuilder addDisabledChangeWithId(long id) { + mChanges.add(new CompatChange(id, "", -1, true, "")); + return this; + } + + CompatConfigBuilder addDisabledChangeWithIdAndName(long id, String name) { + mChanges.add(new CompatChange(id, name, -1, true, "")); + return this; + } + + CompatConfigBuilder addDisabledChangeWithIdAndDescription(long id, String description) { + mChanges.add(new CompatChange(id, "", -1, true, description)); + return this; + } + + CompatConfig build() { + CompatConfig config = new CompatConfig(mBuildClassifier, mContext); + for (CompatChange change : mChanges) { + config.addChange(change); + } + return config; + } +} diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java index cb99c118a407..407f67e2fd8e 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java @@ -18,12 +18,25 @@ package com.android.server.compat; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertThrows; + +import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.compat.AndroidBuildClassifier; + +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import java.io.File; import java.io.FileOutputStream; @@ -34,12 +47,12 @@ import java.util.UUID; @RunWith(AndroidJUnit4.class) public class CompatConfigTest { - private ApplicationInfo makeAppInfo(String pName, int targetSdkVersion) { - ApplicationInfo ai = new ApplicationInfo(); - ai.packageName = pName; - ai.targetSdkVersion = targetSdkVersion; - return ai; - } + @Mock + private Context mContext; + @Mock + PackageManager mPackageManager; + @Mock + private AndroidBuildClassifier mBuildClassifier; private File createTempDir() { String base = System.getProperty("java.io.tmpdir"); @@ -54,112 +67,206 @@ public class CompatConfigTest { os.close(); } + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + // Assume userdebug/eng non-final build + when(mBuildClassifier.isDebuggableBuild()).thenReturn(true); + when(mBuildClassifier.isFinalBuild()).thenReturn(false); + } + + @Test + public void testUnknownChangeEnabled() throws Exception { + CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext); + assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create().build())) + .isTrue(); + } + @Test - public void testUnknownChangeEnabled() { - CompatConfig pc = new CompatConfig(); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isTrue(); + public void testDisabledChangeDisabled() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addDisabledChangeWithId(1234L) + .build(); + + assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create().build())) + .isFalse(); } @Test - public void testDisabledChangeDisabled() { - CompatConfig pc = new CompatConfig(); - pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true, "")); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse(); + public void testTargetSdkChangeDisabled() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addTargetSdkChangeWithId(2, 1234L) + .build(); + + assertThat(compatConfig.isChangeEnabled(1234L, + ApplicationInfoBuilder.create().withTargetSdk(2).build())) + .isFalse(); } @Test - public void testTargetSdkChangeDisabled() { - CompatConfig pc = new CompatConfig(); - pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, false, null)); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isFalse(); + public void testTargetSdkChangeEnabled() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addTargetSdkChangeWithId(2, 1234L) + .build(); + + assertThat(compatConfig.isChangeEnabled(1234L, + ApplicationInfoBuilder.create().withTargetSdk(3).build())).isTrue(); } @Test - public void testTargetSdkChangeEnabled() { - CompatConfig pc = new CompatConfig(); - pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, false, "")); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue(); + public void testDisabledOverrideTargetSdkChange() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addTargetSdkDisabledChangeWithId(2, 1234L) + .build(); + + assertThat(compatConfig.isChangeEnabled(1234L, + ApplicationInfoBuilder.create().withTargetSdk(3).build())).isFalse(); } @Test - public void testDisabledOverrideTargetSdkChange() { - CompatConfig pc = new CompatConfig(); - pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, true, null)); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isFalse(); + public void testGetDisabledChanges() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addDisabledChangeWithId(1234L) + .addEnabledChangeWithId(2345L) + .build(); + + assertThat(compatConfig.getDisabledChanges( + ApplicationInfoBuilder.create().build())).asList().containsExactly(1234L); } @Test - public void testGetDisabledChanges() { - CompatConfig pc = new CompatConfig(); - pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true, null)); - pc.addChange(new CompatChange(2345L, "OTHER_CHANGE", -1, false, null)); - assertThat(pc.getDisabledChanges( - makeAppInfo("com.some.package", 2))).asList().containsExactly(1234L); + public void testGetDisabledChangesSorted() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addDisabledChangeWithId(1234L) + .addDisabledChangeWithId(123L) + .addDisabledChangeWithId(12L) + .build(); + + assertThat(compatConfig.getDisabledChanges(ApplicationInfoBuilder.create().build())) + .asList().containsExactly(12L, 123L, 1234L); } @Test - public void testGetDisabledChangesSorted() { - CompatConfig pc = new CompatConfig(); - pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, true, null)); - pc.addChange(new CompatChange(123L, "OTHER_CHANGE", 2, true, null)); - pc.addChange(new CompatChange(12L, "THIRD_CHANGE", 2, true, null)); - assertThat(pc.getDisabledChanges( - makeAppInfo("com.some.package", 2))).asList().containsExactly(12L, 123L, 1234L); + public void testPackageOverrideEnabled() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addDisabledChangeWithId(1234L) + .build(); + + compatConfig.addOverride(1234L, "com.some.package", true); + + assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create() + .withPackageName("com.some.package").build())).isTrue(); + assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create() + .withPackageName("com.other.package").build())).isFalse(); } @Test - public void testPackageOverrideEnabled() { - CompatConfig pc = new CompatConfig(); - pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true, null)); // disabled - pc.addOverride(1234L, "com.some.package", true); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isTrue(); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.other.package", 2))).isFalse(); + public void testPackageOverrideDisabled() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addEnabledChangeWithId(1234L) + .build(); + + compatConfig.addOverride(1234L, "com.some.package", false); + + assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create() + .withPackageName("com.some.package").build())).isFalse(); + assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create() + .withPackageName("com.other.package").build())).isTrue(); } @Test - public void testPackageOverrideDisabled() { - CompatConfig pc = new CompatConfig(); - pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false, null)); - pc.addOverride(1234L, "com.some.package", false); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isFalse(); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.other.package", 2))).isTrue(); + public void testPackageOverrideUnknownPackage() throws Exception { + CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext); + + compatConfig.addOverride(1234L, "com.some.package", false); + + assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create() + .withPackageName("com.some.package").build())).isFalse(); + assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create() + .withPackageName("com.other.package").build())).isTrue(); } @Test - public void testPackageOverrideUnknownPackage() { - CompatConfig pc = new CompatConfig(); - pc.addOverride(1234L, "com.some.package", false); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isFalse(); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.other.package", 2))).isTrue(); + public void testPreventAddOverride() throws Exception { + final long changeId = 1234L; + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addDisabledChangeWithId(1234L) + .build(); + ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() + .withPackageName("com.some.package") + .build(); + PackageManager packageManager = mock(PackageManager.class); + when(mContext.getPackageManager()).thenReturn(packageManager); + when(packageManager.getApplicationInfo(eq("com.some.package"), anyInt())) + .thenReturn(applicationInfo); + + // Force the validator to prevent overriding the change by using a user build. + when(mBuildClassifier.isDebuggableBuild()).thenReturn(false); + when(mBuildClassifier.isFinalBuild()).thenReturn(true); + + assertThrows(SecurityException.class, + () -> compatConfig.addOverride(1234L, "com.some.package", true) + ); + assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse(); } @Test - public void testPackageOverrideUnknownChange() { - CompatConfig pc = new CompatConfig(); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isTrue(); + public void testPreventRemoveOverride() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addDisabledChangeWithId(1234L) + .build(); + ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() + .withPackageName("com.some.package") + .build(); + when(mPackageManager.getApplicationInfo(eq("com.some.package"), anyInt())) + .thenReturn(applicationInfo); + // Assume the override was allowed to be added. + compatConfig.addOverride(1234L, "com.some.package", true); + + // Validator allows turning on the change. + assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue(); + + // Reject all override attempts. + // Force the validator to prevent overriding the change by using a user build. + when(mBuildClassifier.isDebuggableBuild()).thenReturn(false); + when(mBuildClassifier.isFinalBuild()).thenReturn(true); + // Try to turn off change, but validator prevents it. + assertThrows(SecurityException.class, + () -> compatConfig.removeOverride(1234L, "com.some.package")); + assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue(); } @Test - public void testRemovePackageOverride() { - CompatConfig pc = new CompatConfig(); - pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false, null)); - pc.addOverride(1234L, "com.some.package", false); - pc.removeOverride(1234L, "com.some.package"); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isTrue(); + public void testRemovePackageOverride() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addEnabledChangeWithId(1234L) + .build(); + ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() + .withPackageName("com.some.package") + .build(); + + assertThat(compatConfig.addOverride(1234L, "com.some.package", false)).isTrue(); + assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse(); + + compatConfig.removeOverride(1234L, "com.some.package"); + assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue(); } @Test - public void testLookupChangeId() { - CompatConfig pc = new CompatConfig(); - pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false, null)); - pc.addChange(new CompatChange(2345L, "ANOTHER_CHANGE", -1, false, null)); - assertThat(pc.lookupChangeId("MY_CHANGE")).isEqualTo(1234L); + public void testLookupChangeId() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addEnabledChangeWithIdAndName(1234L, "MY_CHANGE") + .addEnabledChangeWithIdAndName(2345L, "MY_OTHER_CHANGE") + .build(); + + assertThat(compatConfig.lookupChangeId("MY_CHANGE")).isEqualTo(1234L); } @Test - public void testLookupChangeIdNotPresent() { - CompatConfig pc = new CompatConfig(); - assertThat(pc.lookupChangeId("MY_CHANGE")).isEqualTo(-1L); + public void testLookupChangeIdNotPresent() throws Exception { + CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext); + assertThat(compatConfig.lookupChangeId("MY_CHANGE")).isEqualTo(-1L); } @Test @@ -172,14 +279,17 @@ public class CompatConfigTest { File dir = createTempDir(); writeToFile(dir, "platform_compat_config.xml", configXml); - - CompatConfig pc = new CompatConfig(); - pc.initConfigFromLib(dir); - - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse(); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue(); - assertThat(pc.isChangeEnabled(1235L, makeAppInfo("com.some.package", 5))).isFalse(); - assertThat(pc.isChangeEnabled(1236L, makeAppInfo("com.some.package", 1))).isTrue(); + CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext); + compatConfig.initConfigFromLib(dir); + + assertThat(compatConfig.isChangeEnabled(1234L, + ApplicationInfoBuilder.create().withTargetSdk(1).build())).isFalse(); + assertThat(compatConfig.isChangeEnabled(1234L, + ApplicationInfoBuilder.create().withTargetSdk(3).build())).isTrue(); + assertThat(compatConfig.isChangeEnabled(1235L, + ApplicationInfoBuilder.create().withTargetSdk(5).build())).isFalse(); + assertThat(compatConfig.isChangeEnabled(1236L, + ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue(); } @Test @@ -195,15 +305,16 @@ public class CompatConfigTest { File dir = createTempDir(); writeToFile(dir, "libcore_platform_compat_config.xml", configXml1); writeToFile(dir, "frameworks_platform_compat_config.xml", configXml2); - - CompatConfig pc = new CompatConfig(); - pc.initConfigFromLib(dir); - - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse(); - assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue(); - assertThat(pc.isChangeEnabled(1235L, makeAppInfo("com.some.package", 5))).isFalse(); - assertThat(pc.isChangeEnabled(1236L, makeAppInfo("com.some.package", 1))).isTrue(); + CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext); + compatConfig.initConfigFromLib(dir); + + assertThat(compatConfig.isChangeEnabled(1234L, + ApplicationInfoBuilder.create().withTargetSdk(1).build())).isFalse(); + assertThat(compatConfig.isChangeEnabled(1234L, + ApplicationInfoBuilder.create().withTargetSdk(3).build())).isTrue(); + assertThat(compatConfig.isChangeEnabled(1235L, + ApplicationInfoBuilder.create().withTargetSdk(5).build())).isFalse(); + assertThat(compatConfig.isChangeEnabled(1236L, + ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue(); } } - - diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatibilityChangeConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatibilityChangeConfigBuilder.java new file mode 100644 index 000000000000..793296e88169 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/compat/CompatibilityChangeConfigBuilder.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.compat; + +import android.compat.Compatibility; + +import com.android.internal.compat.CompatibilityChangeConfig; + +import java.util.HashSet; +import java.util.Set; + +class CompatibilityChangeConfigBuilder { + private Set<Long> mEnabled; + private Set<Long> mDisabled; + + private CompatibilityChangeConfigBuilder() { + mEnabled = new HashSet<>(); + mDisabled = new HashSet<>(); + } + + static CompatibilityChangeConfigBuilder create() { + return new CompatibilityChangeConfigBuilder(); + } + + CompatibilityChangeConfigBuilder enable(Long id) { + mEnabled.add(id); + return this; + } + + CompatibilityChangeConfigBuilder disable(Long id) { + mDisabled.add(id); + return this; + } + + CompatibilityChangeConfig build() { + return new CompatibilityChangeConfig(new Compatibility.ChangeConfig(mEnabled, mDisabled)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java new file mode 100644 index 000000000000..ecd07bdc4544 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.compat; + +import static com.android.internal.compat.OverrideAllowedState.ALLOWED; +import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK; +import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE; +import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.pm.PackageManager; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.compat.AndroidBuildClassifier; +import com.android.internal.compat.IOverrideValidator; +import com.android.internal.compat.OverrideAllowedState; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +public class OverrideValidatorImplTest { + private static final String PACKAGE_NAME = "my.package"; + private static final int TARGET_SDK = 10; + private static final int TARGET_SDK_BEFORE = 9; + private static final int TARGET_SDK_AFTER = 11; + + @Mock + private PackageManager mPackageManager; + @Mock + Context mContext; + + private AndroidBuildClassifier debuggableBuild() { + AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class); + when(buildClassifier.isDebuggableBuild()).thenReturn(true); + return buildClassifier; + } + + private AndroidBuildClassifier betaBuild() { + AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class); + when(buildClassifier.isDebuggableBuild()).thenReturn(false); + when(buildClassifier.isFinalBuild()).thenReturn(false); + return buildClassifier; + } + + private AndroidBuildClassifier finalBuild() { + AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class); + when(buildClassifier.isDebuggableBuild()).thenReturn(false); + when(buildClassifier.isFinalBuild()).thenReturn(true); + return buildClassifier; + } + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + } + + @Test + public void getOverrideAllowedState_debugBuildAnyChangeDebugApp_allowOverride() + throws Exception { + CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext) + .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1) + .addTargetSdkChangeWithId(TARGET_SDK, 2) + .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3) + .addEnabledChangeWithId(4) + .addDisabledChangeWithId(5).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .debuggable() + .withTargetSdk(TARGET_SDK) + .withPackageName(PACKAGE_NAME).build()); + + OverrideAllowedState stateTargetSdkLessChange = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + OverrideAllowedState stateTargetSdkEqualChange = + overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME); + OverrideAllowedState stateTargetSdkAfterChange = + overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME); + OverrideAllowedState stateEnabledChange = + overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME); + OverrideAllowedState stateDisabledChange = + overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME); + + assertThat(stateTargetSdkLessChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); + assertThat(stateTargetSdkEqualChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); + assertThat(stateTargetSdkAfterChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); + assertThat(stateEnabledChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); + assertThat(stateDisabledChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); + } + + @Test + public void getOverrideAllowedState_debugBuildAnyChangeReleaseApp_allowOverride() + throws Exception { + CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext) + .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1) + .addTargetSdkChangeWithId(TARGET_SDK, 2) + .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3) + .addEnabledChangeWithId(4) + .addDisabledChangeWithId(5).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .withPackageName(PACKAGE_NAME) + .withTargetSdk(TARGET_SDK).build()); + + OverrideAllowedState stateTargetSdkLessChange = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + OverrideAllowedState stateTargetSdkEqualChange = + overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME); + OverrideAllowedState stateTargetSdkAfterChange = + overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME); + OverrideAllowedState stateEnabledChange = + overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME); + OverrideAllowedState stateDisabledChange = + overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME); + + assertThat(stateTargetSdkLessChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); + assertThat(stateTargetSdkEqualChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); + assertThat(stateTargetSdkAfterChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); + assertThat(stateEnabledChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); + assertThat(stateDisabledChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); + } + + @Test + public void getOverrideAllowedState_betaBuildTargetSdkChangeDebugApp_allowOverride() + throws Exception { + CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext) + .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1) + .addTargetSdkChangeWithId(TARGET_SDK, 2) + .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .debuggable() + .withTargetSdk(TARGET_SDK) + .withPackageName(PACKAGE_NAME).build()); + + OverrideAllowedState stateTargetSdkLessChange = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + OverrideAllowedState stateTargetSdkEqualChange = + overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME); + OverrideAllowedState stateTargetSdkAfterChange = + overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME); + + assertThat(stateTargetSdkLessChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK_BEFORE)); + assertThat(stateTargetSdkEqualChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK)); + assertThat(stateTargetSdkAfterChange) + .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK_AFTER)); + } + + @Test + public void getOverrideAllowedState_betaBuildEnabledChangeDebugApp_rejectOverride() + throws Exception { + CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext) + .addEnabledChangeWithId(1).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .withPackageName(PACKAGE_NAME) + .debuggable() + .build()); + + OverrideAllowedState allowedState = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + + assertThat(allowedState) + .isEqualTo(new OverrideAllowedState(DISABLED_NON_TARGET_SDK, -1, -1)); + } + + @Test + public void getOverrideAllowedState_betaBuildDisabledChangeDebugApp_rejectOverride() + throws Exception { + CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext) + .addDisabledChangeWithId(1).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .debuggable() + .withPackageName(PACKAGE_NAME).build()); + + OverrideAllowedState allowedState = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + + assertThat(allowedState) + .isEqualTo(new OverrideAllowedState(DISABLED_NON_TARGET_SDK, -1, -1)); + } + + @Test + public void getOverrideAllowedState_betaBuildAnyChangeReleaseApp_rejectOverride() + throws Exception { + CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext) + .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1) + .addTargetSdkChangeWithId(TARGET_SDK, 2) + .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3) + .addEnabledChangeWithId(4) + .addDisabledChangeWithId(5).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .withPackageName(PACKAGE_NAME) + .withTargetSdk(TARGET_SDK).build()); + + OverrideAllowedState stateTargetSdkLessChange = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + OverrideAllowedState stateTargetSdkEqualChange = + overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME); + OverrideAllowedState stateTargetSdkAfterChange = + overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME); + OverrideAllowedState stateEnabledChange = + overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME); + OverrideAllowedState stateDisabledChange = + overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME); + + assertThat(stateTargetSdkLessChange) + .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + assertThat(stateTargetSdkEqualChange) + .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + assertThat(stateTargetSdkAfterChange) + .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + assertThat(stateEnabledChange) + .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + assertThat(stateDisabledChange) + .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + } + + @Test + public void getOverrideAllowedState_finalBuildTargetSdkChangeDebugAppOptin_allowOverride() + throws Exception { + CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext) + .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 1).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .debuggable() + .withTargetSdk(TARGET_SDK) + .withPackageName(PACKAGE_NAME).build()); + + OverrideAllowedState allowedState = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + + assertThat(allowedState) + .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK_AFTER)); + } + + @Test + public void getOverrideAllowedState_finalBuildTargetSdkChangeDebugAppOptout_rejectOverride() + throws Exception { + CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext) + .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1) + .addTargetSdkChangeWithId(TARGET_SDK, 2).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .withPackageName(PACKAGE_NAME) + .withTargetSdk(TARGET_SDK) + .debuggable() + .build()); + + OverrideAllowedState stateTargetSdkLessChange = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + OverrideAllowedState stateTargetSdkEqualChange = + overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME); + + assertThat(stateTargetSdkLessChange).isEqualTo( + new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, TARGET_SDK, + TARGET_SDK_BEFORE)); + assertThat(stateTargetSdkEqualChange).isEqualTo( + new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, TARGET_SDK, TARGET_SDK)); + } + + @Test + public void getOverrideAllowedState_finalBuildEnabledChangeDebugApp_rejectOverride() + throws Exception { + CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext) + .addEnabledChangeWithId(1).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .withPackageName(PACKAGE_NAME) + .debuggable().build()); + + OverrideAllowedState allowedState = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + + assertThat(allowedState) + .isEqualTo(new OverrideAllowedState(DISABLED_NON_TARGET_SDK, -1, -1)); + } + + @Test + public void getOverrideAllowedState_finalBuildDisabledChangeDebugApp_rejectOverride() + throws Exception { + CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext) + .addDisabledChangeWithId(1).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .withPackageName(PACKAGE_NAME) + .debuggable().build()); + + OverrideAllowedState allowedState = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + + assertThat(allowedState) + .isEqualTo(new OverrideAllowedState(DISABLED_NON_TARGET_SDK, -1, -1)); + } + + @Test + public void getOverrideAllowedState_finalBuildAnyChangeReleaseApp_rejectOverride() + throws Exception { + CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext) + .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1) + .addTargetSdkChangeWithId(TARGET_SDK, 2) + .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3) + .addEnabledChangeWithId(4) + .addDisabledChangeWithId(5).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .withPackageName(PACKAGE_NAME) + .withTargetSdk(TARGET_SDK).build()); + + OverrideAllowedState stateTargetSdkLessChange = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + OverrideAllowedState stateTargetSdkEqualChange = + overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME); + OverrideAllowedState stateTargetSdkAfterChange = + overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME); + OverrideAllowedState stateEnabledChange = + overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME); + OverrideAllowedState stateDisabledChange = + overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME); + + assertThat(stateTargetSdkLessChange) + .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + assertThat(stateTargetSdkEqualChange) + .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + assertThat(stateTargetSdkAfterChange) + .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + assertThat(stateEnabledChange) + .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + assertThat(stateDisabledChange) + .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java index c406876c5cee..ce5d6d9be770 100644 --- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java @@ -26,21 +26,20 @@ import static org.mockito.Mockito.when; import static org.mockito.internal.verification.VerificationModeFactory.times; import static org.testng.Assert.assertThrows; -import android.compat.Compatibility; import android.content.Context; import android.content.pm.PackageManager; -import com.android.internal.compat.CompatibilityChangeConfig; +import androidx.test.runner.AndroidJUnit4; -import com.google.common.collect.ImmutableSet; +import com.android.internal.compat.AndroidBuildClassifier; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.MockitoAnnotations; -@RunWith(MockitoJUnitRunner.class) +@RunWith(AndroidJUnit4.class) public class PlatformCompatTest { private static final String PACKAGE_NAME = "my.package"; @@ -50,84 +49,77 @@ public class PlatformCompatTest { private PackageManager mPackageManager; @Mock CompatChange.ChangeListener mListener1, mListener2; - + PlatformCompat mPlatformCompat; + CompatConfig mCompatConfig; + @Mock + private AndroidBuildClassifier mBuildClassifier; @Before public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); when(mContext.getPackageManager()).thenReturn(mPackageManager); when(mPackageManager.getPackageUid(eq(PACKAGE_NAME), eq(0))).thenThrow( new PackageManager.NameNotFoundException()); - CompatConfig.get().clearChanges(); + mCompatConfig = new CompatConfig(mBuildClassifier, mContext); + mPlatformCompat = new PlatformCompat(mContext, mCompatConfig); + // Assume userdebug/eng non-final build + when(mBuildClassifier.isDebuggableBuild()).thenReturn(true); + when(mBuildClassifier.isFinalBuild()).thenReturn(false); } @Test - public void testRegisterListenerToSameIdThrows() { - PlatformCompat pc = new PlatformCompat(mContext); - + public void testRegisterListenerToSameIdThrows() throws Exception { // Registering a listener to change 1 is successful. - pc.registerListener(1, mListener1); + mPlatformCompat.registerListener(1, mListener1); // Registering a listener to change 2 is successful. - pc.registerListener(2, mListener1); + mPlatformCompat.registerListener(2, mListener1); // Trying to register another listener to change id 1 fails. - assertThrows(IllegalStateException.class, () -> pc.registerListener(1, mListener1)); + assertThrows(IllegalStateException.class, + () -> mPlatformCompat.registerListener(1, mListener1)); } @Test - public void testRegisterListenerReturn() { - PlatformCompat pc = new PlatformCompat(mContext); - - pc.setOverrides( - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of())), + public void testRegisterListenerReturn() throws Exception { + mPlatformCompat.setOverrides( + CompatibilityChangeConfigBuilder.create().enable(1L).build(), PACKAGE_NAME); // Change id 1 is known (added in setOverrides). - assertThat(pc.registerListener(1, mListener1)).isTrue(); + assertThat(mPlatformCompat.registerListener(1, mListener1)).isTrue(); // Change 2 is unknown. - assertThat(pc.registerListener(2, mListener1)).isFalse(); + assertThat(mPlatformCompat.registerListener(2, mListener1)).isFalse(); } @Test - public void testListenerCalledOnSetOverrides() { - PlatformCompat pc = new PlatformCompat(mContext); + public void testListenerCalledOnSetOverrides() throws Exception { + mPlatformCompat.registerListener(1, mListener1); + mPlatformCompat.registerListener(2, mListener1); - pc.registerListener(1, mListener1); - pc.registerListener(2, mListener1); - - pc.setOverrides( - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of(2L))), + mPlatformCompat.setOverrides( + CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(), PACKAGE_NAME); verify(mListener1, times(2)).onCompatChange(PACKAGE_NAME); } @Test - public void testListenerNotCalledOnWrongPackage() { - PlatformCompat pc = new PlatformCompat(mContext); - - pc.registerListener(1, mListener1); - pc.registerListener(2, mListener1); + public void testListenerNotCalledOnWrongPackage() throws Exception { + mPlatformCompat.registerListener(1, mListener1); + mPlatformCompat.registerListener(2, mListener1); - pc.setOverridesForTest( - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of(2L))), + mPlatformCompat.setOverrides( + CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(), PACKAGE_NAME); verify(mListener1, never()).onCompatChange("other.package"); } @Test - public void testListenerCalledOnSetOverridesTwoListeners() { - PlatformCompat pc = new PlatformCompat(mContext); - pc.registerListener(1, mListener1); + public void testListenerCalledOnSetOverridesTwoListeners() throws Exception { + mPlatformCompat.registerListener(1, mListener1); - final ImmutableSet<Long> enabled = ImmutableSet.of(1L); - final ImmutableSet<Long> disabled = ImmutableSet.of(2L); - - pc.setOverrides( - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(enabled, disabled)), + mPlatformCompat.setOverrides( + CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(), PACKAGE_NAME); verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME); @@ -136,11 +128,10 @@ public class PlatformCompatTest { reset(mListener1); reset(mListener2); - pc.registerListener(2, mListener2); + mPlatformCompat.registerListener(2, mListener2); - pc.setOverrides( - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(enabled, disabled)), + mPlatformCompat.setOverrides( + CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(), PACKAGE_NAME); verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME); @@ -148,31 +139,23 @@ public class PlatformCompatTest { } @Test - public void testListenerCalledOnSetOverridesForTest() { - PlatformCompat pc = new PlatformCompat(mContext); - - pc.registerListener(1, mListener1); - pc.registerListener(2, mListener1); + public void testListenerCalledOnSetOverridesForTest() throws Exception { + mPlatformCompat.registerListener(1, mListener1); + mPlatformCompat.registerListener(2, mListener1); - pc.setOverridesForTest( - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of(2L))), + mPlatformCompat.setOverrides( + CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(), PACKAGE_NAME); verify(mListener1, times(2)).onCompatChange(PACKAGE_NAME); } @Test - public void testListenerCalledOnSetOverridesTwoListenersForTest() { - PlatformCompat pc = new PlatformCompat(mContext); - pc.registerListener(1, mListener1); + public void testListenerCalledOnSetOverridesTwoListenersForTest() throws Exception { + mPlatformCompat.registerListener(1, mListener1); - final ImmutableSet<Long> enabled = ImmutableSet.of(1L); - final ImmutableSet<Long> disabled = ImmutableSet.of(2L); - - pc.setOverridesForTest( - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(enabled, disabled)), + mPlatformCompat.setOverrides( + CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(), PACKAGE_NAME); verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME); @@ -181,10 +164,10 @@ public class PlatformCompatTest { reset(mListener1); reset(mListener2); - pc.registerListener(2, mListener2); - pc.setOverridesForTest( - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(enabled, disabled)), + mPlatformCompat.registerListener(2, mListener2); + + mPlatformCompat.setOverrides( + CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(), PACKAGE_NAME); verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME); @@ -192,15 +175,12 @@ public class PlatformCompatTest { } @Test - public void testListenerCalledOnClearOverrides() { - PlatformCompat pc = new PlatformCompat(mContext); + public void testListenerCalledOnClearOverrides() throws Exception { + mPlatformCompat.registerListener(1, mListener1); + mPlatformCompat.registerListener(2, mListener2); - pc.registerListener(1, mListener1); - pc.registerListener(2, mListener2); - - pc.setOverrides( - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of())), + mPlatformCompat.setOverrides( + CompatibilityChangeConfigBuilder.create().enable(1L).build(), PACKAGE_NAME); verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME); verify(mListener2, never()).onCompatChange(PACKAGE_NAME); @@ -208,21 +188,18 @@ public class PlatformCompatTest { reset(mListener1); reset(mListener2); - pc.clearOverrides(PACKAGE_NAME); + mPlatformCompat.clearOverrides(PACKAGE_NAME); verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME); verify(mListener2, never()).onCompatChange(PACKAGE_NAME); } @Test - public void testListenerCalledOnClearOverridesMultipleOverrides() { - PlatformCompat pc = new PlatformCompat(mContext); - - pc.registerListener(1, mListener1); - pc.registerListener(2, mListener2); + public void testListenerCalledOnClearOverridesMultipleOverrides() throws Exception { + mPlatformCompat.registerListener(1, mListener1); + mPlatformCompat.registerListener(2, mListener2); - pc.setOverrides( - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of(2L))), + mPlatformCompat.setOverrides( + CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(), PACKAGE_NAME); verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME); verify(mListener2, times(1)).onCompatChange(PACKAGE_NAME); @@ -230,21 +207,18 @@ public class PlatformCompatTest { reset(mListener1); reset(mListener2); - pc.clearOverrides(PACKAGE_NAME); + mPlatformCompat.clearOverrides(PACKAGE_NAME); verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME); verify(mListener2, times(1)).onCompatChange(PACKAGE_NAME); } @Test - public void testListenerCalledOnClearOverrideExists() { - PlatformCompat pc = new PlatformCompat(mContext); + public void testListenerCalledOnClearOverrideExists() throws Exception { + mPlatformCompat.registerListener(1, mListener1); + mPlatformCompat.registerListener(2, mListener2); - pc.registerListener(1, mListener1); - pc.registerListener(2, mListener2); - - pc.setOverrides( - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(ImmutableSet.of(1L), ImmutableSet.of())), + mPlatformCompat.setOverrides( + CompatibilityChangeConfigBuilder.create().enable(1L).build(), PACKAGE_NAME); verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME); verify(mListener2, never()).onCompatChange(PACKAGE_NAME); @@ -252,21 +226,17 @@ public class PlatformCompatTest { reset(mListener1); reset(mListener2); - pc.clearOverride(1, PACKAGE_NAME); + mPlatformCompat.clearOverride(1, PACKAGE_NAME); verify(mListener1, times(1)).onCompatChange(PACKAGE_NAME); verify(mListener2, never()).onCompatChange(PACKAGE_NAME); } @Test - public void testListenerCalledOnClearOverrideDoesntExist() { - PlatformCompat pc = new PlatformCompat(mContext); - - pc.registerListener(1, mListener1); + public void testListenerCalledOnClearOverrideDoesntExist() throws Exception { + mPlatformCompat.registerListener(1, mListener1); - pc.clearOverride(1, PACKAGE_NAME); + mPlatformCompat.clearOverride(1, PACKAGE_NAME); // Listener not called when a non existing override is removed. verify(mListener1, never()).onCompatChange(PACKAGE_NAME); } - - } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java index 1a67576c218f..960f670904d6 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java @@ -217,6 +217,8 @@ public class DpmMockContext extends MockContext { return mMockSystemServices.wifiManager; case Context.ACCOUNT_SERVICE: return mMockSystemServices.accountManager; + case Context.TELEPHONY_SERVICE: + return mMockSystemServices.telephonyManager; } throw new UnsupportedOperationException(); } diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java index 72a7f508772b..71b568cc06c5 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java @@ -18,15 +18,18 @@ package com.android.server.timedetector; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.timedetector.ManualTimeSuggestion; +import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.PhoneTimeSuggestion; import android.content.Context; import android.content.pm.PackageManager; @@ -77,6 +80,22 @@ public class TimeDetectorServiceTest { mHandlerThread.join(); } + @Test(expected = SecurityException.class) + public void testSuggestPhoneTime_withoutPermission() { + doThrow(new SecurityException("Mock")) + .when(mMockContext).enforceCallingPermission(anyString(), any()); + PhoneTimeSuggestion phoneTimeSuggestion = createPhoneTimeSuggestion(); + + try { + mTimeDetectorService.suggestPhoneTime(phoneTimeSuggestion); + fail(); + } finally { + verify(mMockContext).enforceCallingPermission( + eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE), + anyString()); + } + } + @Test public void testSuggestPhoneTime() throws Exception { doNothing().when(mMockContext).enforceCallingPermission(anyString(), any()); @@ -86,13 +105,29 @@ public class TimeDetectorServiceTest { mTestHandler.assertTotalMessagesEnqueued(1); verify(mMockContext).enforceCallingPermission( - eq(android.Manifest.permission.SET_TIME), + eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE), anyString()); mTestHandler.waitForEmptyQueue(); mStubbedTimeDetectorStrategy.verifySuggestPhoneTimeCalled(phoneTimeSuggestion); } + @Test(expected = SecurityException.class) + public void testSuggestManualTime_withoutPermission() { + doThrow(new SecurityException("Mock")) + .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any()); + ManualTimeSuggestion manualTimeSuggestion = createManualTimeSuggestion(); + + try { + mTimeDetectorService.suggestManualTime(manualTimeSuggestion); + fail(); + } finally { + verify(mMockContext).enforceCallingOrSelfPermission( + eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE), + anyString()); + } + } + @Test public void testSuggestManualTime() throws Exception { doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any()); @@ -102,13 +137,43 @@ public class TimeDetectorServiceTest { mTestHandler.assertTotalMessagesEnqueued(1); verify(mMockContext).enforceCallingOrSelfPermission( - eq(android.Manifest.permission.SET_TIME), + eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE), anyString()); mTestHandler.waitForEmptyQueue(); mStubbedTimeDetectorStrategy.verifySuggestManualTimeCalled(manualTimeSuggestion); } + @Test(expected = SecurityException.class) + public void testSuggestNetworkTime_withoutPermission() { + doThrow(new SecurityException("Mock")) + .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any()); + NetworkTimeSuggestion NetworkTimeSuggestion = createNetworkTimeSuggestion(); + + try { + mTimeDetectorService.suggestNetworkTime(NetworkTimeSuggestion); + fail(); + } finally { + verify(mMockContext).enforceCallingOrSelfPermission( + eq(android.Manifest.permission.SET_TIME), anyString()); + } + } + + @Test + public void testSuggestNetworkTime() throws Exception { + doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any()); + + NetworkTimeSuggestion NetworkTimeSuggestion = createNetworkTimeSuggestion(); + mTimeDetectorService.suggestNetworkTime(NetworkTimeSuggestion); + mTestHandler.assertTotalMessagesEnqueued(1); + + verify(mMockContext).enforceCallingOrSelfPermission( + eq(android.Manifest.permission.SET_TIME), anyString()); + + mTestHandler.waitForEmptyQueue(); + mStubbedTimeDetectorStrategy.verifySuggestNetworkTimeCalled(NetworkTimeSuggestion); + } + @Test public void testDump() { when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)) @@ -146,11 +211,17 @@ public class TimeDetectorServiceTest { return new ManualTimeSuggestion(timeValue); } + private static NetworkTimeSuggestion createNetworkTimeSuggestion() { + TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L); + return new NetworkTimeSuggestion(timeValue); + } + private static class StubbedTimeDetectorStrategy implements TimeDetectorStrategy { // Call tracking. private PhoneTimeSuggestion mLastPhoneSuggestion; private ManualTimeSuggestion mLastManualSuggestion; + private NetworkTimeSuggestion mLastNetworkSuggestion; private boolean mLastAutoTimeDetectionToggleCalled; private boolean mDumpCalled; @@ -171,6 +242,12 @@ public class TimeDetectorServiceTest { } @Override + public void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion) { + resetCallTracking(); + mLastNetworkSuggestion = timeSuggestion; + } + + @Override public void handleAutoTimeDetectionChanged() { resetCallTracking(); mLastAutoTimeDetectionToggleCalled = true; @@ -185,6 +262,7 @@ public class TimeDetectorServiceTest { void resetCallTracking() { mLastPhoneSuggestion = null; mLastManualSuggestion = null; + mLastNetworkSuggestion = null; mLastAutoTimeDetectionToggleCalled = false; mDumpCalled = false; } @@ -197,6 +275,10 @@ public class TimeDetectorServiceTest { assertEquals(expectedSuggestion, mLastManualSuggestion); } + public void verifySuggestNetworkTimeCalled(NetworkTimeSuggestion expectedSuggestion) { + assertEquals(expectedSuggestion, mLastNetworkSuggestion); + } + void verifyHandleAutoTimeDetectionToggleCalled() { assertTrue(mLastAutoTimeDetectionToggleCalled); } diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java index 1aa3d8fe654b..ca6fd08092df 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.app.timedetector.ManualTimeSuggestion; +import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.PhoneTimeSuggestion; import android.content.Intent; import android.icu.util.Calendar; @@ -45,14 +46,16 @@ public class TimeDetectorStrategyImplTest { private static final TimestampedValue<Long> ARBITRARY_CLOCK_INITIALIZATION_INFO = new TimestampedValue<>( 123456789L /* realtimeClockMillis */, - createUtcTime(1977, 1, 1, 12, 0, 0)); + createUtcTime(2008, 5, 23, 12, 0, 0)); + /** + * An arbitrary time, very different from the {@link #ARBITRARY_CLOCK_INITIALIZATION_INFO} + * time. Can be used as the basis for time suggestions. + */ private static final long ARBITRARY_TEST_TIME_MILLIS = createUtcTime(2018, 1, 1, 12, 0, 0); private static final int ARBITRARY_PHONE_ID = 123456; - private static final long ONE_DAY_MILLIS = Duration.ofDays(1).toMillis(); - private Script mScript; @Before @@ -67,15 +70,16 @@ public class TimeDetectorStrategyImplTest { int phoneId = ARBITRARY_PHONE_ID; long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS; + PhoneTimeSuggestion timeSuggestion = mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis); - int clockIncrement = 1000; - long expectedSystemClockMillis = testTimeMillis + clockIncrement; + mScript.simulateTimePassing() + .simulatePhoneTimeSuggestion(timeSuggestion); - mScript.simulateTimePassing(clockIncrement) - .simulatePhoneTimeSuggestion(timeSuggestion) - .verifySystemClockWasSetAndResetCallTracking( - expectedSystemClockMillis, true /* expectNetworkBroadcast */) + long expectedSystemClockMillis = + mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime()); + mScript.verifySystemClockWasSetAndResetCallTracking( + expectedSystemClockMillis, true /* expectNetworkBroadcast */) .assertLatestPhoneSuggestion(phoneId, timeSuggestion); } @@ -94,26 +98,24 @@ public class TimeDetectorStrategyImplTest { @Test public void testSuggestPhoneTime_systemClockThreshold() { - int systemClockUpdateThresholdMillis = 1000; + final int systemClockUpdateThresholdMillis = 1000; + final int clockIncrementMillis = 100; mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) .pokeThresholds(systemClockUpdateThresholdMillis) .pokeAutoTimeDetectionEnabled(true); - final int clockIncrement = 100; int phoneId = ARBITRARY_PHONE_ID; // Send the first time signal. It should be used. { - long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS; PhoneTimeSuggestion timeSuggestion1 = - mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis); - TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime(); + mScript.generatePhoneTimeSuggestion(phoneId, ARBITRARY_TEST_TIME_MILLIS); // Increment the the device clocks to simulate the passage of time. - mScript.simulateTimePassing(clockIncrement); + mScript.simulateTimePassing(clockIncrementMillis); long expectedSystemClockMillis1 = - TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis()); + mScript.calculateTimeInMillisForNow(timeSuggestion1.getUtcTime()); mScript.simulatePhoneTimeSuggestion(timeSuggestion1) .verifySystemClockWasSetAndResetCallTracking( @@ -127,7 +129,7 @@ public class TimeDetectorStrategyImplTest { int underThresholdMillis = systemClockUpdateThresholdMillis - 1; PhoneTimeSuggestion timeSuggestion2 = mScript.generatePhoneTimeSuggestion( phoneId, mScript.peekSystemClockMillis() + underThresholdMillis); - mScript.simulateTimePassing(clockIncrement) + mScript.simulateTimePassing(clockIncrementMillis) .simulatePhoneTimeSuggestion(timeSuggestion2) .verifySystemClockWasNotSetAndResetCallTracking() .assertLatestPhoneSuggestion(phoneId, timeSuggestion2); @@ -138,11 +140,10 @@ public class TimeDetectorStrategyImplTest { PhoneTimeSuggestion timeSuggestion3 = mScript.generatePhoneTimeSuggestion( phoneId, mScript.peekSystemClockMillis() + systemClockUpdateThresholdMillis); - mScript.simulateTimePassing(clockIncrement); + mScript.simulateTimePassing(clockIncrementMillis); long expectedSystemClockMillis3 = - TimeDetectorStrategy.getTimeAt(timeSuggestion3.getUtcTime(), - mScript.peekElapsedRealtimeMillis()); + mScript.calculateTimeInMillisForNow(timeSuggestion3.getUtcTime()); mScript.simulatePhoneTimeSuggestion(timeSuggestion3) .verifySystemClockWasSetAndResetCallTracking( @@ -162,17 +163,16 @@ public class TimeDetectorStrategyImplTest { int phone1Id = ARBITRARY_PHONE_ID; int phone2Id = ARBITRARY_PHONE_ID + 1; long phone1TimeMillis = ARBITRARY_TEST_TIME_MILLIS; - long phone2TimeMillis = phone1TimeMillis + 60000; - - final int clockIncrement = 999; + long phone2TimeMillis = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(1).toMillis(); // Make a suggestion with phone2Id. { PhoneTimeSuggestion phone2TimeSuggestion = mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis); - mScript.simulateTimePassing(clockIncrement); + mScript.simulateTimePassing(); - long expectedSystemClockMillis = phone2TimeMillis + clockIncrement; + long expectedSystemClockMillis = + mScript.calculateTimeInMillisForNow(phone2TimeSuggestion.getUtcTime()); mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion) .verifySystemClockWasSetAndResetCallTracking( @@ -181,15 +181,16 @@ public class TimeDetectorStrategyImplTest { .assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion); } - mScript.simulateTimePassing(clockIncrement); + mScript.simulateTimePassing(); // Now make a different suggestion with phone1Id. { PhoneTimeSuggestion phone1TimeSuggestion = mScript.generatePhoneTimeSuggestion(phone1Id, phone1TimeMillis); - mScript.simulateTimePassing(clockIncrement); + mScript.simulateTimePassing(); - long expectedSystemClockMillis = phone1TimeMillis + clockIncrement; + long expectedSystemClockMillis = + mScript.calculateTimeInMillisForNow(phone1TimeSuggestion.getUtcTime()); mScript.simulatePhoneTimeSuggestion(phone1TimeSuggestion) .verifySystemClockWasSetAndResetCallTracking( @@ -198,14 +199,14 @@ public class TimeDetectorStrategyImplTest { } - mScript.simulateTimePassing(clockIncrement); + mScript.simulateTimePassing(); // Make another suggestion with phone2Id. It should be stored but not used because the // phone1Id suggestion will still "win". { PhoneTimeSuggestion phone2TimeSuggestion = mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis); - mScript.simulateTimePassing(clockIncrement); + mScript.simulateTimePassing(); mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion) .verifySystemClockWasNotSetAndResetCallTracking() @@ -220,9 +221,10 @@ public class TimeDetectorStrategyImplTest { { PhoneTimeSuggestion phone2TimeSuggestion = mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis); - mScript.simulateTimePassing(clockIncrement); + mScript.simulateTimePassing(); - long expectedSystemClockMillis = phone2TimeMillis + clockIncrement; + long expectedSystemClockMillis = + mScript.calculateTimeInMillisForNow(phone2TimeSuggestion.getUtcTime()); mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion) .verifySystemClockWasSetAndResetCallTracking( @@ -239,7 +241,7 @@ public class TimeDetectorStrategyImplTest { int phoneId = ARBITRARY_PHONE_ID; PhoneTimeSuggestion timeSuggestion = mScript.generatePhoneTimeSuggestion(phoneId, ARBITRARY_TEST_TIME_MILLIS); - mScript.simulateTimePassing(1000) + mScript.simulateTimePassing() .simulatePhoneTimeSuggestion(timeSuggestion) .verifySystemClockWasNotSetAndResetCallTracking() .assertLatestPhoneSuggestion(phoneId, timeSuggestion); @@ -260,9 +262,8 @@ public class TimeDetectorStrategyImplTest { TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime(); // Initialize the strategy / device with a time set from a phone suggestion. - mScript.simulateTimePassing(100); - long expectedSystemClockMillis1 = - TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis()); + mScript.simulateTimePassing(); + long expectedSystemClockMillis1 = mScript.calculateTimeInMillisForNow(utcTime1); mScript.simulatePhoneTimeSuggestion(timeSuggestion1) .verifySystemClockWasSetAndResetCallTracking( expectedSystemClockMillis1, true /* expectNetworkBroadcast */) @@ -299,8 +300,7 @@ public class TimeDetectorStrategyImplTest { long validReferenceTimeMillis = utcTime1.getReferenceTimeMillis() + 100; TimestampedValue<Long> utcTime4 = new TimestampedValue<>( validReferenceTimeMillis, validUtcTimeMillis); - long expectedSystemClockMillis4 = - TimeDetectorStrategy.getTimeAt(utcTime4, mScript.peekElapsedRealtimeMillis()); + long expectedSystemClockMillis4 = mScript.calculateTimeInMillisForNow(utcTime4); PhoneTimeSuggestion timeSuggestion4 = createPhoneTimeSuggestion(phoneId, utcTime4); mScript.simulatePhoneTimeSuggestion(timeSuggestion4) @@ -335,8 +335,7 @@ public class TimeDetectorStrategyImplTest { // Simulate more time passing. mScript.simulateTimePassing(clockIncrementMillis); - long expectedSystemClockMillis1 = - TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis()); + long expectedSystemClockMillis1 = mScript.calculateTimeInMillisForNow(utcTime1); // Turn on auto time detection. mScript.simulateAutoTimeDetectionToggle() @@ -357,8 +356,8 @@ public class TimeDetectorStrategyImplTest { // Simulate more time passing. mScript.simulateTimePassing(clockIncrementMillis); - long expectedSystemClockMillis2 = TimeDetectorStrategy.getTimeAt( - timeSuggestion2.getUtcTime(), mScript.peekElapsedRealtimeMillis()); + long expectedSystemClockMillis2 = + mScript.calculateTimeInMillisForNow(timeSuggestion2.getUtcTime()); // The new time, though valid, should not be set in the system clock because auto time is // disabled. @@ -382,19 +381,21 @@ public class TimeDetectorStrategyImplTest { long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS; PhoneTimeSuggestion phoneSuggestion = mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis); - int clockIncrementMillis = 1000; - mScript.simulateTimePassing(clockIncrementMillis) - .simulatePhoneTimeSuggestion(phoneSuggestion) + mScript.simulateTimePassing(); + + long expectedSystemClockMillis = + mScript.calculateTimeInMillisForNow(phoneSuggestion.getUtcTime()); + mScript.simulatePhoneTimeSuggestion(phoneSuggestion) .verifySystemClockWasSetAndResetCallTracking( - testTimeMillis + clockIncrementMillis, true /* expectedNetworkBroadcast */) + expectedSystemClockMillis, true /* expectedNetworkBroadcast */) .assertLatestPhoneSuggestion(phoneId, phoneSuggestion); // Look inside and check what the strategy considers the current best phone suggestion. assertEquals(phoneSuggestion, mScript.peekBestPhoneSuggestion()); // Simulate time passing, long enough that phoneSuggestion is now too old. - mScript.simulateTimePassing(TimeDetectorStrategyImpl.PHONE_MAX_AGE_MILLIS); + mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS); // Look inside and check what the strategy considers the current best phone suggestion. It // should still be the, it's just no longer used. @@ -407,13 +408,14 @@ public class TimeDetectorStrategyImplTest { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) .pokeAutoTimeDetectionEnabled(false); - long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS; - ManualTimeSuggestion timeSuggestion = mScript.generateManualTimeSuggestion(testTimeMillis); - final int clockIncrement = 1000; - long expectedSystemClockMillis = testTimeMillis + clockIncrement; + ManualTimeSuggestion timeSuggestion = + mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS); - mScript.simulateTimePassing(clockIncrement) - .simulateManualTimeSuggestion(timeSuggestion) + mScript.simulateTimePassing(); + + long expectedSystemClockMillis = + mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime()); + mScript.simulateManualTimeSuggestion(timeSuggestion) .verifySystemClockWasSetAndResetCallTracking( expectedSystemClockMillis, false /* expectNetworkBroadcast */); } @@ -430,21 +432,19 @@ public class TimeDetectorStrategyImplTest { long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS; PhoneTimeSuggestion phoneTimeSuggestion = mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis); - long expectedAutoClockMillis = phoneTimeSuggestion.getUtcTime().getValue(); - final int clockIncrement = 1000; // Simulate the passage of time. - mScript.simulateTimePassing(clockIncrement); - expectedAutoClockMillis += clockIncrement; + mScript.simulateTimePassing(); + long expectedAutoClockMillis = + mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime()); mScript.simulatePhoneTimeSuggestion(phoneTimeSuggestion) .verifySystemClockWasSetAndResetCallTracking( expectedAutoClockMillis, true /* expectNetworkBroadcast */) .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion); // Simulate the passage of time. - mScript.simulateTimePassing(clockIncrement); - expectedAutoClockMillis += clockIncrement; + mScript.simulateTimePassing(); // Switch to manual. mScript.simulateAutoTimeDetectionToggle() @@ -452,26 +452,29 @@ public class TimeDetectorStrategyImplTest { .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion); // Simulate the passage of time. - mScript.simulateTimePassing(clockIncrement); - expectedAutoClockMillis += clockIncrement; + mScript.simulateTimePassing(); // Simulate a manual suggestion 1 day different from the auto suggestion. - long manualTimeMillis = testTimeMillis + ONE_DAY_MILLIS; - long expectedManualClockMillis = manualTimeMillis; + long manualTimeMillis = testTimeMillis + Duration.ofDays(1).toMillis(); ManualTimeSuggestion manualTimeSuggestion = mScript.generateManualTimeSuggestion(manualTimeMillis); + mScript.simulateTimePassing(); + + long expectedManualClockMillis = + mScript.calculateTimeInMillisForNow(manualTimeSuggestion.getUtcTime()); mScript.simulateManualTimeSuggestion(manualTimeSuggestion) .verifySystemClockWasSetAndResetCallTracking( expectedManualClockMillis, false /* expectNetworkBroadcast */) .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion); // Simulate the passage of time. - mScript.simulateTimePassing(clockIncrement); - expectedAutoClockMillis += clockIncrement; + mScript.simulateTimePassing(); // Switch back to auto. mScript.simulateAutoTimeDetectionToggle(); + expectedAutoClockMillis = + mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime()); mScript.verifySystemClockWasSetAndResetCallTracking( expectedAutoClockMillis, true /* expectNetworkBroadcast */) .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion); @@ -492,13 +495,143 @@ public class TimeDetectorStrategyImplTest { ManualTimeSuggestion timeSuggestion = mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS); - final int clockIncrement = 1000; - mScript.simulateTimePassing(clockIncrement) + mScript.simulateTimePassing() .simulateManualTimeSuggestion(timeSuggestion) .verifySystemClockWasNotSetAndResetCallTracking(); } + @Test + public void testSuggestNetworkTime_autoTimeEnabled() { + mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoTimeDetectionEnabled(true); + + NetworkTimeSuggestion timeSuggestion = + mScript.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS); + + mScript.simulateTimePassing(); + + long expectedSystemClockMillis = + mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime()); + mScript.simulateNetworkTimeSuggestion(timeSuggestion) + .verifySystemClockWasSetAndResetCallTracking( + expectedSystemClockMillis, false /* expectNetworkBroadcast */); + } + + @Test + public void testSuggestNetworkTime_autoTimeDisabled() { + mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoTimeDetectionEnabled(false); + + NetworkTimeSuggestion timeSuggestion = + mScript.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS); + + mScript.simulateTimePassing() + .simulateNetworkTimeSuggestion(timeSuggestion) + .verifySystemClockWasNotSetAndResetCallTracking(); + } + + @Test + public void testSuggestNetworkTime_phoneSuggestionsBeatNetworkSuggestions() { + mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoTimeDetectionEnabled(true); + + // Three obviously different times that could not be mistaken for each other. + long networkTimeMillis1 = ARBITRARY_TEST_TIME_MILLIS; + long networkTimeMillis2 = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(30).toMillis(); + long phoneTimeMillis = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(60).toMillis(); + // A small increment used to simulate the passage of time, but not enough to interfere with + // macro-level time changes associated with suggestion age. + final long smallTimeIncrementMillis = 101; + + // A network suggestion is made. It should be used because there is no phone suggestion. + NetworkTimeSuggestion networkTimeSuggestion1 = + mScript.generateNetworkTimeSuggestion(networkTimeMillis1); + mScript.simulateTimePassing(smallTimeIncrementMillis) + .simulateNetworkTimeSuggestion(networkTimeSuggestion1) + .verifySystemClockWasSetAndResetCallTracking( + mScript.calculateTimeInMillisForNow(networkTimeSuggestion1.getUtcTime()), + false /* expectNetworkBroadcast */); + + // Check internal state. + mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, null) + .assertLatestNetworkSuggestion(networkTimeSuggestion1); + assertEquals(networkTimeSuggestion1, mScript.peekLatestValidNetworkSuggestion()); + assertNull(mScript.peekBestPhoneSuggestion()); + + // Simulate a little time passing. + mScript.simulateTimePassing(smallTimeIncrementMillis) + .verifySystemClockWasNotSetAndResetCallTracking(); + + // Now a phone suggestion is made. Phone suggestions are prioritized over network + // suggestions so it should "win". + PhoneTimeSuggestion phoneTimeSuggestion = + mScript.generatePhoneTimeSuggestion(ARBITRARY_PHONE_ID, phoneTimeMillis); + mScript.simulateTimePassing(smallTimeIncrementMillis) + .simulatePhoneTimeSuggestion(phoneTimeSuggestion) + .verifySystemClockWasSetAndResetCallTracking( + mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime()), + true /* expectNetworkBroadcast */); + + // Check internal state. + mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion) + .assertLatestNetworkSuggestion(networkTimeSuggestion1); + assertEquals(networkTimeSuggestion1, mScript.peekLatestValidNetworkSuggestion()); + assertEquals(phoneTimeSuggestion, mScript.peekBestPhoneSuggestion()); + + // Simulate some significant time passing: half the time allowed before a time signal + // becomes "too old to use". + mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2) + .verifySystemClockWasNotSetAndResetCallTracking(); + + // Now another network suggestion is made. Phone suggestions are prioritized over network + // suggestions so the latest phone suggestion should still "win". + NetworkTimeSuggestion networkTimeSuggestion2 = + mScript.generateNetworkTimeSuggestion(networkTimeMillis2); + mScript.simulateTimePassing(smallTimeIncrementMillis) + .simulateNetworkTimeSuggestion(networkTimeSuggestion2) + .verifySystemClockWasNotSetAndResetCallTracking(); + + // Check internal state. + mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion) + .assertLatestNetworkSuggestion(networkTimeSuggestion2); + assertEquals(networkTimeSuggestion2, mScript.peekLatestValidNetworkSuggestion()); + assertEquals(phoneTimeSuggestion, mScript.peekBestPhoneSuggestion()); + + // Simulate some significant time passing: half the time allowed before a time signal + // becomes "too old to use". This should mean that phoneTimeSuggestion is now too old to be + // used but networkTimeSuggestion2 is not. + mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2); + + // NOTE: The TimeDetectorStrategyImpl doesn't set an alarm for the point when the last + // suggestion it used becomes too old: it requires a new suggestion or an auto-time toggle + // to re-run the detection logic. This may change in future but until then we rely on a + // steady stream of suggestions to re-evaluate. + mScript.verifySystemClockWasNotSetAndResetCallTracking(); + + // Check internal state. + mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion) + .assertLatestNetworkSuggestion(networkTimeSuggestion2); + assertEquals(networkTimeSuggestion2, mScript.peekLatestValidNetworkSuggestion()); + assertNull(mScript.peekBestPhoneSuggestion()); + + // Toggle auto-time off and on to force the detection logic to run. + mScript.simulateAutoTimeDetectionToggle() + .simulateTimePassing(smallTimeIncrementMillis) + .simulateAutoTimeDetectionToggle(); + + // Verify the latest network time now wins. + mScript.verifySystemClockWasSetAndResetCallTracking( + mScript.calculateTimeInMillisForNow(networkTimeSuggestion2.getUtcTime()), + false /* expectNetworkTimeBroadcast */); + + // Check internal state. + mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion) + .assertLatestNetworkSuggestion(networkTimeSuggestion2); + assertEquals(networkTimeSuggestion2, mScript.peekLatestValidNetworkSuggestion()); + assertNull(mScript.peekBestPhoneSuggestion()); + } + /** * A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving * like the real thing should, it also asserts preconditions. @@ -674,6 +807,11 @@ public class TimeDetectorStrategyImplTest { return this; } + Script simulateNetworkTimeSuggestion(NetworkTimeSuggestion timeSuggestion) { + mTimeDetectorStrategy.suggestNetworkTime(timeSuggestion); + return this; + } + Script simulateAutoTimeDetectionToggle() { mFakeCallback.simulateAutoTimeZoneDetectionToggle(); mTimeDetectorStrategy.handleAutoTimeDetectionChanged(); @@ -685,6 +823,13 @@ public class TimeDetectorStrategyImplTest { return this; } + /** + * Simulates time passing by an arbitrary (but relatively small) amount. + */ + Script simulateTimePassing() { + return simulateTimePassing(999); + } + Script verifySystemClockWasNotSetAndResetCallTracking() { mFakeCallback.verifySystemClockNotSet(); mFakeCallback.verifyIntentWasNotBroadcast(); @@ -711,14 +856,30 @@ public class TimeDetectorStrategyImplTest { } /** + * White box test info: Asserts the latest network suggestion is as expected. + */ + Script assertLatestNetworkSuggestion(NetworkTimeSuggestion expected) { + assertEquals(expected, mTimeDetectorStrategy.getLatestNetworkSuggestion()); + return this; + } + + /** * White box test info: Returns the phone suggestion that would be used, if any, given the - * current elapsed real time clock. + * current elapsed real time clock and regardless of origin prioritization. */ PhoneTimeSuggestion peekBestPhoneSuggestion() { return mTimeDetectorStrategy.findBestPhoneSuggestionForTests(); } /** + * White box test info: Returns the network suggestion that would be used, if any, given the + * current elapsed real time clock and regardless of origin prioritization. + */ + NetworkTimeSuggestion peekLatestValidNetworkSuggestion() { + return mTimeDetectorStrategy.findLatestValidNetworkSuggestionForTests(); + } + + /** * Generates a ManualTimeSuggestion using the current elapsed realtime clock for the * reference time. */ @@ -739,6 +900,24 @@ public class TimeDetectorStrategyImplTest { } return createPhoneTimeSuggestion(phoneId, time); } + + /** + * Generates a NetworkTimeSuggestion using the current elapsed realtime clock for the + * reference time. + */ + NetworkTimeSuggestion generateNetworkTimeSuggestion(long timeMillis) { + TimestampedValue<Long> utcTime = + new TimestampedValue<>(mFakeCallback.peekElapsedRealtimeMillis(), timeMillis); + return new NetworkTimeSuggestion(utcTime); + } + + /** + * Calculates what the supplied time would be when adjusted for the movement of the fake + * elapsed realtime clock. + */ + long calculateTimeInMillisForNow(TimestampedValue<Long> utcTime) { + return TimeDetectorStrategy.getTimeAt(utcTime, peekElapsedRealtimeMillis()); + } } private static PhoneTimeSuggestion createPhoneTimeSuggestion(int phoneId, diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 0becaf237a73..8808339b1664 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -502,51 +502,116 @@ public abstract class Connection extends Conferenceable { //********************************************************************************************** /** - * Define IMS Audio Codec + * Indicates that the audio codec is currently not specified or is unknown. */ - // Current audio codec is NONE public static final int AUDIO_CODEC_NONE = ImsStreamMediaProfile.AUDIO_QUALITY_NONE; // 0 - // Current audio codec is AMR + /** + * Adaptive Multi-rate audio codec. + */ public static final int AUDIO_CODEC_AMR = ImsStreamMediaProfile.AUDIO_QUALITY_AMR; // 1 - // Current audio codec is AMR_WB + /** + * Adaptive Multi-rate wideband audio codec. + */ public static final int AUDIO_CODEC_AMR_WB = ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB; // 2 - // Current audio codec is QCELP13K + /** + * Qualcomm code-excited linear prediction 13 kilobit audio codec. + */ public static final int AUDIO_CODEC_QCELP13K = ImsStreamMediaProfile.AUDIO_QUALITY_QCELP13K; //3 - // Current audio codec is EVRC + /** + * Enhanced Variable Rate Codec. See 3GPP2 C.S0014-A. + */ public static final int AUDIO_CODEC_EVRC = ImsStreamMediaProfile.AUDIO_QUALITY_EVRC; // 4 - // Current audio codec is EVRC_B + /** + * Enhanced Variable Rate Codec B. Commonly used on CDMA networks. + */ public static final int AUDIO_CODEC_EVRC_B = ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_B; // 5 - // Current audio codec is EVRC_WB + /** + * Enhanced Variable Rate Wideband Codec. See RFC5188. + */ public static final int AUDIO_CODEC_EVRC_WB = ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB; // 6 - // Current audio codec is EVRC_NW + /** + * Enhanced Variable Rate Narrowband-Wideband Codec. + */ public static final int AUDIO_CODEC_EVRC_NW = ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_NW; // 7 - // Current audio codec is GSM_EFR + /** + * GSM Enhanced Full-Rate audio codec, also known as GSM-EFR, GSM 06.60, or simply EFR. + */ public static final int AUDIO_CODEC_GSM_EFR = ImsStreamMediaProfile.AUDIO_QUALITY_GSM_EFR; // 8 - // Current audio codec is GSM_FR + /** + * GSM Full-Rate audio codec, also known as GSM-FR, GSM 06.10, GSM, or simply FR. + */ public static final int AUDIO_CODEC_GSM_FR = ImsStreamMediaProfile.AUDIO_QUALITY_GSM_FR; // 9 - // Current audio codec is GSM_HR + /** + * GSM Half Rate audio codec. + */ public static final int AUDIO_CODEC_GSM_HR = ImsStreamMediaProfile.AUDIO_QUALITY_GSM_HR; // 10 - // Current audio codec is G711U + /** + * ITU-T G711U audio codec. + */ public static final int AUDIO_CODEC_G711U = ImsStreamMediaProfile.AUDIO_QUALITY_G711U; // 11 - // Current audio codec is G723 + /** + * ITU-T G723 audio codec. + */ public static final int AUDIO_CODEC_G723 = ImsStreamMediaProfile.AUDIO_QUALITY_G723; // 12 - // Current audio codec is G711A + /** + * ITU-T G711A audio codec. + */ public static final int AUDIO_CODEC_G711A = ImsStreamMediaProfile.AUDIO_QUALITY_G711A; // 13 - // Current audio codec is G722 + /** + * ITU-T G722 audio codec. + */ public static final int AUDIO_CODEC_G722 = ImsStreamMediaProfile.AUDIO_QUALITY_G722; // 14 - // Current audio codec is G711AB + /** + * ITU-T G711AB audio codec. + */ public static final int AUDIO_CODEC_G711AB = ImsStreamMediaProfile.AUDIO_QUALITY_G711AB; // 15 - // Current audio codec is G729 + /** + * ITU-T G729 audio codec. + */ public static final int AUDIO_CODEC_G729 = ImsStreamMediaProfile.AUDIO_QUALITY_G729; // 16 - // Current audio codec is EVS_NB + /** + * Enhanced Voice Services Narrowband audio codec. See 3GPP TS 26.441. + */ public static final int AUDIO_CODEC_EVS_NB = ImsStreamMediaProfile.AUDIO_QUALITY_EVS_NB; // 17 - // Current audio codec is EVS_WB + /** + * Enhanced Voice Services Wideband audio codec. See 3GPP TS 26.441. + */ public static final int AUDIO_CODEC_EVS_WB = ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB; // 18 - // Current audio codec is EVS_SWB + /** + * Enhanced Voice Services Super-Wideband audio codec. See 3GPP TS 26.441. + */ public static final int AUDIO_CODEC_EVS_SWB = ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB; // 19 - // Current audio codec is EVS_FB + /** + * Enhanced Voice Services Fullband audio codec. See 3GPP TS 26.441. + */ public static final int AUDIO_CODEC_EVS_FB = ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB; // 20 + /**@hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "AUDIO_CODEC_", value = { + AUDIO_CODEC_NONE, + AUDIO_CODEC_AMR, + AUDIO_CODEC_AMR_WB, + AUDIO_CODEC_QCELP13K, + AUDIO_CODEC_EVRC, + AUDIO_CODEC_EVRC_B, + AUDIO_CODEC_EVRC_WB, + AUDIO_CODEC_EVRC_NW, + AUDIO_CODEC_GSM_EFR, + AUDIO_CODEC_GSM_FR, + AUDIO_CODEC_GSM_HR, + AUDIO_CODEC_G711U, + AUDIO_CODEC_G723, + AUDIO_CODEC_G711A, + AUDIO_CODEC_G722, + AUDIO_CODEC_G711AB, + AUDIO_CODEC_G729, + AUDIO_CODEC_EVS_NB, + AUDIO_CODEC_EVS_SWB, + AUDIO_CODEC_EVS_FB + }) + public @interface AudioCodec {} + /** * Connection extra key used to store the last forwarded number associated with the current * connection. Used to communicate to the user interface that the connection was forwarded via @@ -640,10 +705,10 @@ public abstract class Connection extends Conferenceable { "android.telecom.extra.IS_RTT_AUDIO_PRESENT"; /** - * The audio codec in use for the current {@link Connection}, if known. Valid values include - * {@link #AUDIO_CODEC_AMR_WB} and {@link #AUDIO_CODEC_EVS_WB}. + * The audio codec in use for the current {@link Connection}, if known. Examples of valid + * values include {@link #AUDIO_CODEC_AMR_WB} and {@link #AUDIO_CODEC_EVS_WB}. */ - public static final String EXTRA_AUDIO_CODEC = + public static final @AudioCodec String EXTRA_AUDIO_CODEC = "android.telecom.extra.AUDIO_CODEC"; /** diff --git a/telephony/OWNERS b/telephony/OWNERS index 2a6e8deeb13e..58a7ea08da3f 100644 --- a/telephony/OWNERS +++ b/telephony/OWNERS @@ -14,4 +14,5 @@ shuoq@google.com refuhoo@google.com paulye@google.com nazaninb@google.com -sarahchin@google.com
\ No newline at end of file +sarahchin@google.com +dbright@google.com diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java index d4526a48fdb1..825ce2fc1a00 100644 --- a/telephony/java/android/telephony/LocationAccessPolicy.java +++ b/telephony/common/android/telephony/LocationAccessPolicy.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -11,7 +11,7 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License + * limitations under the License. */ package android.telephony; @@ -59,6 +59,7 @@ public final class LocationAccessPolicy { DENIED_HARD, } + /** Data structure for location permission query */ public static class LocationPermissionQuery { public final String callingPackage; public final int callingUid; @@ -80,6 +81,7 @@ public final class LocationAccessPolicy { this.method = method; } + /** Builder for LocationPermissionQuery */ public static class Builder { private String mCallingPackage; private int mCallingUid; @@ -149,6 +151,7 @@ public final class LocationAccessPolicy { return this; } + /** build LocationPermissionQuery */ public LocationPermissionQuery build() { return new LocationPermissionQuery(mCallingPackage, mCallingUid, mCallingPid, mMinSdkVersionForCoarse, mMinSdkVersionForFine, @@ -235,6 +238,7 @@ public final class LocationAccessPolicy { } } + /** Check if location permissions have been granted */ public static LocationPermissionResult checkLocationPermission( Context context, LocationPermissionQuery query) { // Always allow the phone process and system server to access location. This avoid @@ -341,4 +345,4 @@ public final class LocationAccessPolicy { } return false; } -}
\ No newline at end of file +} diff --git a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java index 0630454cbf53..b5d33699a7f6 100644 --- a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java +++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -11,7 +11,7 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License + * limitations under the License. */ package com.android.internal.telephony; @@ -73,7 +73,7 @@ public final class CarrierAppUtils { * system startup prior to any application running, as well as any time the set of carrier * privileged apps may have changed. */ - public synchronized static void disableCarrierAppsUntilPrivileged(String callingPackage, + public static synchronized void disableCarrierAppsUntilPrivileged(String callingPackage, IPackageManager packageManager, TelephonyManager telephonyManager, ContentResolver contentResolver, int userId) { if (DEBUG) { @@ -100,7 +100,7 @@ public final class CarrierAppUtils { * broadcasts. The app will continue to run (briefly) after being disabled, before the Package * Manager can kill it, and this can lead to crashes as the app is in an unexpected state. */ - public synchronized static void disableCarrierAppsUntilPrivileged(String callingPackage, + public static synchronized void disableCarrierAppsUntilPrivileged(String callingPackage, IPackageManager packageManager, ContentResolver contentResolver, int userId) { if (DEBUG) { Slog.d(TAG, "disableCarrierAppsUntilPrivileged"); @@ -117,7 +117,10 @@ public final class CarrierAppUtils { systemCarrierAppsDisabledUntilUsed, systemCarrierAssociatedAppsDisabledUntilUsed); } - // Must be public b/c framework unit tests can't access package-private methods. + /** + * Disable carrier apps until they are privileged + * Must be public b/c framework unit tests can't access package-private methods. + */ @VisibleForTesting public static void disableCarrierAppsUntilPrivileged(String callingPackage, IPackageManager packageManager, @Nullable TelephonyManager telephonyManager, @@ -166,10 +169,10 @@ public final class CarrierAppUtils { // Only update enabled state for the app on /system. Once it has been // updated we shouldn't touch it. if (!ai.isUpdatedSystemApp() - && (ai.enabledSetting == - PackageManager.COMPONENT_ENABLED_STATE_DEFAULT - || ai.enabledSetting == - PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED + && (ai.enabledSetting + == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT + || ai.enabledSetting + == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED || (ai.flags & ApplicationInfo.FLAG_INSTALLED) == 0)) { Slog.i(TAG, "Update state(" + packageName + "): ENABLED for user " + userId); @@ -188,10 +191,10 @@ public final class CarrierAppUtils { // Also enable any associated apps for this carrier app. if (associatedAppList != null) { for (ApplicationInfo associatedApp : associatedAppList) { - if (associatedApp.enabledSetting == - PackageManager.COMPONENT_ENABLED_STATE_DEFAULT - || associatedApp.enabledSetting == - PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED + if (associatedApp.enabledSetting + == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT + || associatedApp.enabledSetting + == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED || (associatedApp.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { Slog.i(TAG, "Update associated state(" + associatedApp.packageName @@ -216,8 +219,8 @@ public final class CarrierAppUtils { // Only update enabled state for the app on /system. Once it has been // updated we shouldn't touch it. if (!ai.isUpdatedSystemApp() - && ai.enabledSetting == - PackageManager.COMPONENT_ENABLED_STATE_DEFAULT + && ai.enabledSetting + == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) { Slog.i(TAG, "Update state(" + packageName + "): DISABLED_UNTIL_USED for user " + userId); @@ -291,8 +294,8 @@ public final class CarrierAppUtils { ApplicationInfo ai = candidates.get(i); String packageName = ai.packageName; boolean hasPrivileges = - telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) == - TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; + telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) + == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; if (!hasPrivileges) { candidates.remove(i); } diff --git a/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java b/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java index 922af126e73e..0b47547d3b0c 100644 --- a/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java +++ b/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java @@ -24,17 +24,17 @@ import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.os.Handler; +import android.os.HandlerThread; import android.os.Looper; import android.os.UserHandle; -import com.android.internal.os.BackgroundThread; - /** * Helper class for monitoring the state of packages: adding, removing, * updating, and disappearing and reappearing on the SD card. */ public abstract class PackageChangeReceiver extends BroadcastReceiver { static final IntentFilter sPackageIntentFilter = new IntentFilter(); + private static HandlerThread sHandlerThread; static { sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); @@ -43,28 +43,24 @@ public abstract class PackageChangeReceiver extends BroadcastReceiver { sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); sPackageIntentFilter.addDataScheme("package"); } + // Keep an instance of Context around as long as we still want the receiver: + // if the instance of Context gets garbage-collected, it'll unregister the receiver, so only + // unset when we want to unregister. Context mRegisteredContext; /** - * To register the intents that needed for monitoring the state of packages + * To register the intents that needed for monitoring the state of packages. Once this method + * has been called on an instance of {@link PackageChangeReceiver}, all subsequent calls must + * have the same {@code user} argument. */ public void register(@NonNull Context context, @Nullable Looper thread, @Nullable UserHandle user) { if (mRegisteredContext != null) { throw new IllegalStateException("Already registered"); } - Handler handler = (thread == null) ? BackgroundThread.getHandler() : new Handler(thread); - mRegisteredContext = context; - if (handler != null) { - if (user != null) { - context.registerReceiverAsUser(this, user, sPackageIntentFilter, null, handler); - } else { - context.registerReceiver(this, sPackageIntentFilter, - null, handler); - } - } else { - throw new NullPointerException(); - } + Handler handler = new Handler(thread == null ? getStaticLooper() : thread); + mRegisteredContext = user == null ? context : context.createContextAsUser(user, 0); + mRegisteredContext.registerReceiver(this, sPackageIntentFilter, null, handler); } /** @@ -78,6 +74,14 @@ public abstract class PackageChangeReceiver extends BroadcastReceiver { mRegisteredContext = null; } + private static synchronized Looper getStaticLooper() { + if (sHandlerThread == null) { + sHandlerThread = new HandlerThread(PackageChangeReceiver.class.getSimpleName()); + sHandlerThread.start(); + } + return sHandlerThread.getLooper(); + } + /** * This method is invoked when receive the Intent.ACTION_PACKAGE_ADDED */ diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java index df668ea2313d..53842cdeea5a 100644 --- a/telephony/common/com/android/internal/telephony/SmsApplication.java +++ b/telephony/common/com/android/internal/telephony/SmsApplication.java @@ -44,6 +44,7 @@ import android.telephony.Rlog; import android.telephony.TelephonyManager; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -57,7 +58,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; - +import java.util.stream.Collectors; /** @@ -67,10 +68,10 @@ import java.util.function.Consumer; */ public final class SmsApplication { static final String LOG_TAG = "SmsApplication"; - private static final String PHONE_PACKAGE_NAME = "com.android.phone"; - private static final String BLUETOOTH_PACKAGE_NAME = "com.android.bluetooth"; - private static final String MMS_SERVICE_PACKAGE_NAME = "com.android.mms.service"; - private static final String TELEPHONY_PROVIDER_PACKAGE_NAME = "com.android.providers.telephony"; + public static final String PHONE_PACKAGE_NAME = "com.android.phone"; + public static final String BLUETOOTH_PACKAGE_NAME = "com.android.bluetooth"; + public static final String MMS_SERVICE_PACKAGE_NAME = "com.android.mms.service"; + public static final String TELEPHONY_PROVIDER_PACKAGE_NAME = "com.android.providers.telephony"; private static final String SCHEME_SMS = "sms"; private static final String SCHEME_SMSTO = "smsto"; @@ -79,13 +80,13 @@ public final class SmsApplication { private static final boolean DEBUG = false; private static final boolean DEBUG_MULTIUSER = false; - private static final int[] DEFAULT_APP_EXCLUSIVE_APPOPS = { - AppOpsManager.OP_READ_SMS, - AppOpsManager.OP_WRITE_SMS, - AppOpsManager.OP_RECEIVE_SMS, - AppOpsManager.OP_RECEIVE_WAP_PUSH, - AppOpsManager.OP_SEND_SMS, - AppOpsManager.OP_READ_CELL_BROADCASTS + private static final String[] DEFAULT_APP_EXCLUSIVE_APPOPS = { + AppOpsManager.OPSTR_READ_SMS, + AppOpsManager.OPSTR_WRITE_SMS, + AppOpsManager.OPSTR_RECEIVE_SMS, + AppOpsManager.OPSTR_RECEIVE_WAP_PUSH, + AppOpsManager.OPSTR_SEND_SMS, + AppOpsManager.OPSTR_READ_CELL_BROADCASTS }; private static SmsPackageMonitor sSmsPackageMonitor = null; @@ -249,6 +250,7 @@ public final class SmsApplication { private static Collection<SmsApplicationData> getApplicationCollectionInternal( Context context, int userId) { PackageManager packageManager = context.getPackageManager(); + UserHandle userHandle = UserHandle.of(userId); // Get the list of apps registered for SMS Intent intent = new Intent(Intents.SMS_DELIVER_ACTION); @@ -257,7 +259,7 @@ public final class SmsApplication { } List<ResolveInfo> smsReceivers = packageManager.queryBroadcastReceiversAsUser(intent, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, - userId); + userHandle); HashMap<String, SmsApplicationData> receivers = new HashMap<String, SmsApplicationData>(); @@ -284,7 +286,7 @@ public final class SmsApplication { intent.setDataAndType(null, "application/vnd.wap.mms-message"); List<ResolveInfo> mmsReceivers = packageManager.queryBroadcastReceiversAsUser(intent, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, - userId); + userHandle); for (ResolveInfo resolveInfo : mmsReceivers) { final ActivityInfo activityInfo = resolveInfo.activityInfo; if (activityInfo == null) { @@ -326,7 +328,7 @@ public final class SmsApplication { Uri.fromParts(SCHEME_SMSTO, "", null)); List<ResolveInfo> sendToActivities = packageManager.queryIntentActivitiesAsUser(intent, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, - userId); + userHandle); for (ResolveInfo resolveInfo : sendToActivities) { final ActivityInfo activityInfo = resolveInfo.activityInfo; if (activityInfo == null) { @@ -344,7 +346,7 @@ public final class SmsApplication { List<ResolveInfo> smsAppChangedReceivers = packageManager.queryBroadcastReceiversAsUser(intent, PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle); if (DEBUG_MULTIUSER) { Log.i(LOG_TAG, "getApplicationCollectionInternal smsAppChangedActivities=" + smsAppChangedReceivers); @@ -371,7 +373,7 @@ public final class SmsApplication { List<ResolveInfo> providerChangedReceivers = packageManager.queryBroadcastReceiversAsUser(intent, PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle); if (DEBUG_MULTIUSER) { Log.i(LOG_TAG, "getApplicationCollectionInternal providerChangedActivities=" + providerChangedReceivers); @@ -398,7 +400,7 @@ public final class SmsApplication { List<ResolveInfo> simFullReceivers = packageManager.queryBroadcastReceiversAsUser(intent, PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle); if (DEBUG_MULTIUSER) { Log.i(LOG_TAG, "getApplicationCollectionInternal simFullReceivers=" + simFullReceivers); @@ -501,7 +503,7 @@ public final class SmsApplication { // If we found a package, make sure AppOps permissions are set up correctly if (applicationData != null) { - // We can only call checkOp if we are privileged (updateIfNeeded) or if the app we + // We can only call unsafeCheckOp if we are privileged (updateIfNeeded) or if the app we // are checking is for our current uid. Doing this check from the unprivileged current // SMS app allows us to tell the current SMS app that it is not in a good state and // needs to ask to be the current SMS app again to work properly. @@ -555,23 +557,23 @@ public final class SmsApplication { // apps, all of them should be able to write to telephony provider. // This is to allow the proxy package permission check in telephony provider // to pass. - for (int appop : DEFAULT_APP_EXCLUSIVE_APPOPS) { - appOps.setUidMode(appop, Process.PHONE_UID, AppOpsManager.MODE_ALLOWED); + for (String opStr : DEFAULT_APP_EXCLUSIVE_APPOPS) { + appOps.setUidMode(opStr, Process.PHONE_UID, AppOpsManager.MODE_ALLOWED); } } private static boolean tryFixExclusiveSmsAppops(Context context, SmsApplicationData applicationData, boolean updateIfNeeded) { AppOpsManager appOps = context.getSystemService(AppOpsManager.class); - for (int appOp : DEFAULT_APP_EXCLUSIVE_APPOPS) { - int mode = appOps.checkOp(appOp, applicationData.mUid, + for (String opStr : DEFAULT_APP_EXCLUSIVE_APPOPS) { + int mode = appOps.unsafeCheckOp(opStr, applicationData.mUid, applicationData.mPackageName); if (mode != AppOpsManager.MODE_ALLOWED) { Rlog.e(LOG_TAG, applicationData.mPackageName + " lost " - + AppOpsManager.modeToName(appOp) + ": " + + opStr + ": " + (updateIfNeeded ? " (fixing)" : " (no permission to fix)")); if (updateIfNeeded) { - appOps.setUidMode(appOp, applicationData.mUid, AppOpsManager.MODE_ALLOWED); + appOps.setUidMode(opStr, applicationData.mUid, AppOpsManager.MODE_ALLOWED); } else { return false; } @@ -630,7 +632,8 @@ public final class SmsApplication { } // We only make the change if the new package is valid - PackageManager packageManager = context.getPackageManager(); + PackageManager packageManager = + context.createContextAsUser(userHandle, 0).getPackageManager(); Collection<SmsApplicationData> applications = getApplicationCollectionInternal( context, userId); SmsApplicationData oldAppData = oldPackageName != null ? @@ -641,8 +644,7 @@ public final class SmsApplication { AppOpsManager appOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE); if (oldPackageName != null) { try { - int uid = packageManager.getPackageInfoAsUser( - oldPackageName, 0, userId).applicationInfo.uid; + int uid = packageManager.getPackageInfo(oldPackageName, 0).applicationInfo.uid; setExclusiveAppops(oldPackageName, appOps, uid, AppOpsManager.MODE_DEFAULT); } catch (NameNotFoundException e) { Rlog.w(LOG_TAG, "Old SMS package not found: " + oldPackageName); @@ -757,7 +759,7 @@ public final class SmsApplication { } try { PackageInfo info = packageManager.getPackageInfo(packageName, 0); - int mode = appOps.checkOp(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid, + int mode = appOps.unsafeCheckOp(AppOpsManager.OPSTR_WRITE_SMS, info.applicationInfo.uid, packageName); if (mode != AppOpsManager.MODE_ALLOWED) { Rlog.w(LOG_TAG, packageName + " does not have OP_WRITE_SMS: (fixing)"); @@ -773,8 +775,8 @@ public final class SmsApplication { private static void setExclusiveAppops(String pkg, AppOpsManager appOpsManager, int uid, int mode) { - for (int appop : DEFAULT_APP_EXCLUSIVE_APPOPS) { - appOpsManager.setUidMode(appop, uid, mode); + for (String opStr : DEFAULT_APP_EXCLUSIVE_APPOPS) { + appOpsManager.setUidMode(opStr, uid, mode); } } @@ -806,9 +808,16 @@ public final class SmsApplication { } private void onPackageChanged() { - PackageManager packageManager = mContext.getPackageManager(); + int userId; + try { + userId = getSendingUser().getIdentifier(); + } catch (NullPointerException e) { + // This should never happen in prod -- unit tests will put the receiver into a + // unusual state where the pending result is null, which produces a NPE when calling + // getSendingUserId. Just pretend like it's the system user for testing. + userId = UserHandle.USER_SYSTEM; + } Context userContext = mContext; - final int userId = getSendingUserId(); if (userId != UserHandle.USER_SYSTEM) { try { userContext = mContext.createPackageContextAsUser(mContext.getPackageName(), 0, @@ -819,10 +828,11 @@ public final class SmsApplication { } } } + PackageManager packageManager = userContext.getPackageManager(); // Ensure this component is still configured as the preferred activity ComponentName componentName = getDefaultSendToApplication(userContext, true); if (componentName != null) { - configurePreferredActivity(packageManager, componentName, userId); + configurePreferredActivity(packageManager, componentName); } } } @@ -834,41 +844,36 @@ public final class SmsApplication { @UnsupportedAppUsage private static void configurePreferredActivity(PackageManager packageManager, - ComponentName componentName, int userId) { + ComponentName componentName) { // Add the four activity preferences we want to direct to this app. - replacePreferredActivity(packageManager, componentName, userId, SCHEME_SMS); - replacePreferredActivity(packageManager, componentName, userId, SCHEME_SMSTO); - replacePreferredActivity(packageManager, componentName, userId, SCHEME_MMS); - replacePreferredActivity(packageManager, componentName, userId, SCHEME_MMSTO); + replacePreferredActivity(packageManager, componentName, SCHEME_SMS); + replacePreferredActivity(packageManager, componentName, SCHEME_SMSTO); + replacePreferredActivity(packageManager, componentName, SCHEME_MMS); + replacePreferredActivity(packageManager, componentName, SCHEME_MMSTO); } /** * Updates the ACTION_SENDTO activity to the specified component for the specified scheme. */ private static void replacePreferredActivity(PackageManager packageManager, - ComponentName componentName, int userId, String scheme) { + ComponentName componentName, String scheme) { // Build the set of existing activities that handle this scheme Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts(scheme, "", null)); - List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivitiesAsUser( - intent, PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_RESOLVED_FILTER, - userId); + List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities( + intent, PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_RESOLVED_FILTER); - // Build the set of ComponentNames for these activities - final int n = resolveInfoList.size(); - ComponentName[] set = new ComponentName[n]; - for (int i = 0; i < n; i++) { - ResolveInfo info = resolveInfoList.get(i); - set[i] = new ComponentName(info.activityInfo.packageName, info.activityInfo.name); - } + List<ComponentName> components = resolveInfoList.stream().map(info -> + new ComponentName(info.activityInfo.packageName, info.activityInfo.name)) + .collect(Collectors.toList()); // Update the preferred SENDTO activity for the specified scheme IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_SENDTO); intentFilter.addCategory(Intent.CATEGORY_DEFAULT); intentFilter.addDataScheme(scheme); - packageManager.replacePreferredActivityAsUser(intentFilter, + packageManager.replacePreferredActivity(intentFilter, IntentFilter.MATCH_CATEGORY_SCHEME + IntentFilter.MATCH_ADJUSTMENT_NORMAL, - set, componentName, userId); + components, componentName); } /** @@ -899,6 +904,7 @@ public final class SmsApplication { * @param userId target user ID. * @return component name of the app and class to deliver SMS messages to */ + @VisibleForTesting public static ComponentName getDefaultSmsApplicationAsUser(Context context, boolean updateIfNeeded, int userId) { final long token = Binder.clearCallingIdentity(); diff --git a/telephony/java/com/android/internal/telephony/util/ArrayUtils.java b/telephony/common/com/android/internal/telephony/util/ArrayUtils.java index 2905125c69cc..28401a6a7092 100644 --- a/telephony/java/com/android/internal/telephony/util/ArrayUtils.java +++ b/telephony/common/com/android/internal/telephony/util/ArrayUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,17 +29,30 @@ public final class ArrayUtils { /** * Adds value to given array if not already present, providing set-like behavior. + * + * @param kind The class of the array elements. + * @param array The array to append to. + * @param element The array element to append. + * @return The array containing the appended element. */ @SuppressWarnings("unchecked") - public static @NonNull <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element) { + @NonNull + public static <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element) { return appendElement(kind, array, element, false); } /** * Adds value to given array. + * + * @param kind The class of the array elements. + * @param array The array to append to. + * @param element The array element to append. + * @param allowDuplicates Whether to allow duplicated elements in array. + * @return The array containing the appended element. */ @SuppressWarnings("unchecked") - public static @NonNull <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element, + @NonNull + public static <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element, boolean allowDuplicates) { final T[] result; final int end; @@ -59,13 +72,14 @@ public final class ArrayUtils { /** * Combine multiple arrays into a single array. * - * @param kind The class of the array elements + * @param kind The class of the array elements * @param arrays The arrays to combine - * @param <T> The class of the array elements (inferred from kind). + * @param <T> The class of the array elements (inferred from kind). * @return A single array containing all the elements of the parameter arrays. */ @SuppressWarnings("unchecked") - public static @NonNull <T> T[] concatElements(Class<T> kind, @Nullable T[]... arrays) { + @NonNull + public static <T> T[] concatElements(Class<T> kind, @Nullable T[]... arrays) { if (arrays == null || arrays.length == 0) { return createEmptyArray(kind); } diff --git a/telephony/java/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java index 0498d7c31406..0498d7c31406 100644 --- a/telephony/java/com/android/internal/telephony/util/TelephonyUtils.java +++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java diff --git a/telephony/common/com/google/android/mms/pdu/PduPersister.java b/telephony/common/com/google/android/mms/pdu/PduPersister.java index b237705274da..8efca0ea3909 100755 --- a/telephony/common/com/google/android/mms/pdu/PduPersister.java +++ b/telephony/common/com/google/android/mms/pdu/PduPersister.java @@ -34,6 +34,7 @@ import android.provider.Telephony.MmsSms; import android.provider.Telephony.MmsSms.PendingMessages; import android.provider.Telephony.Threads; import android.telephony.PhoneNumberUtils; +import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -1448,9 +1449,9 @@ public class PduPersister { final Set<String> myPhoneNumbers = new HashSet<String>(); if (excludeMyNumber) { // Build a list of my phone numbers from the various sims. - for (int subid : subscriptionManager.getActiveSubscriptionIdList()) { + for (SubscriptionInfo subInfo : subscriptionManager.getActiveSubscriptionInfoList()) { final String myNumber = mContext.getSystemService(TelephonyManager.class). - createForSubscriptionId(subid).getLine1Number(); + createForSubscriptionId(subInfo.getSubscriptionId()).getLine1Number(); if (myNumber != null) { myPhoneNumbers.add(myNumber); } diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 1014571bc41a..a3044edd1631 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -64,6 +64,13 @@ public class ServiceState implements Parcelable { static final boolean DBG = false; static final boolean VDBG = false; // STOPSHIP if true + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "STATE_", + value = {STATE_IN_SERVICE, STATE_OUT_OF_SERVICE, STATE_EMERGENCY_ONLY, + STATE_POWER_OFF}) + public @interface RegState {} + /** * Normal operation condition, the phone is registered * with an operator either in home network or in roaming. @@ -82,6 +89,7 @@ public class ServiceState implements Parcelable { /** * The phone is registered and locked. Only emergency numbers are allowed. {@more} */ + //TODO: This state is not used anymore. It should be deprecated in a future release. public static final int STATE_EMERGENCY_ONLY = TelephonyProtoEnums.SERVICE_STATE_EMERGENCY_ONLY; // 2 @@ -530,13 +538,15 @@ public class ServiceState implements Parcelable { } /** - * Get current data service state + * Get current data registration state. * * @see #STATE_IN_SERVICE * @see #STATE_OUT_OF_SERVICE * @see #STATE_EMERGENCY_ONLY * @see #STATE_POWER_OFF * + * @return current data registration state + * * @hide */ @UnsupportedAppUsage @@ -545,6 +555,23 @@ public class ServiceState implements Parcelable { } /** + * Get current data registration state. + * + * @see #STATE_IN_SERVICE + * @see #STATE_OUT_OF_SERVICE + * @see #STATE_EMERGENCY_ONLY + * @see #STATE_POWER_OFF + * + * @return current data registration state + * + * @hide + */ + @SystemApi + public @RegState int getDataRegistrationState() { + return getDataRegState(); + } + + /** * Get the current duplex mode * * @see #DUPLEX_MODE_UNKNOWN @@ -1437,7 +1464,15 @@ public class ServiceState implements Parcelable { return getRilDataRadioTechnology(); } - /** @hide */ + /** + * Transform RIL radio technology {@link RilRadioTechnology} value to Network + * type {@link NetworkType}. + * + * @param rat The RIL radio technology {@link RilRadioTechnology}. + * @return The network type {@link NetworkType}. + * + * @hide + */ public static int rilRadioTechnologyToNetworkType(@RilRadioTechnology int rat) { switch(rat) { case RIL_RADIO_TECHNOLOGY_GPRS: @@ -1519,7 +1554,15 @@ public class ServiceState implements Parcelable { } } - /** @hide */ + /** + * Transform network type {@link NetworkType} value to RIL radio technology + * {@link RilRadioTechnology}. + * + * @param networkType The network type {@link NetworkType}. + * @return The RIL radio technology {@link RilRadioTechnology}. + * + * @hide + */ public static int networkTypeToRilRadioTechnology(int networkType) { switch(networkType) { case TelephonyManager.NETWORK_TYPE_GPRS: @@ -1720,7 +1763,14 @@ public class ServiceState implements Parcelable { return bearerBitmask; } - /** @hide */ + /** + * Convert network type bitmask to bearer bitmask. + * + * @param networkTypeBitmask The network type bitmask value + * @return The bearer bitmask value. + * + * @hide + */ public static int convertNetworkTypeBitmaskToBearerBitmask(int networkTypeBitmask) { if (networkTypeBitmask == 0) { return 0; @@ -1734,7 +1784,14 @@ public class ServiceState implements Parcelable { return bearerBitmask; } - /** @hide */ + /** + * Convert bearer bitmask to network type bitmask. + * + * @param bearerBitmask The bearer bitmask value. + * @return The network type bitmask value. + * + * @hide + */ public static int convertBearerBitmaskToNetworkTypeBitmask(int bearerBitmask) { if (bearerBitmask == 0) { return 0; @@ -1893,9 +1950,18 @@ public class ServiceState implements Parcelable { * Returns a copy of self with location-identifying information removed. * Always clears the NetworkRegistrationInfo's CellIdentity fields, but if removeCoarseLocation * is true, clears other info as well. + * + * @param removeCoarseLocation Whether to also remove coarse location information. + * if false, it only clears fine location information such as + * NetworkRegistrationInfo's CellIdentity fields; If true, it will + * also remove other location information such as operator's MCC + * and MNC. + * @return the copied ServiceState with location info sanitized. * @hide */ - public ServiceState sanitizeLocationInfo(boolean removeCoarseLocation) { + @SystemApi + @NonNull + public ServiceState createLocationInfoSanitizedCopy(boolean removeCoarseLocation) { ServiceState state = new ServiceState(this); synchronized (state.mNetworkRegistrationInfos) { List<NetworkRegistrationInfo> networkRegistrationInfos = diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java index 9aafc1be5831..aac56c08bdc4 100644 --- a/telephony/java/android/telephony/SignalStrength.java +++ b/telephony/java/android/telephony/SignalStrength.java @@ -332,17 +332,16 @@ public class SignalStrength implements Parcelable { /** * {@link Parcelable.Creator} * - * @hide */ - @UnsupportedAppUsage - public static final @android.annotation.NonNull Parcelable.Creator<SignalStrength> CREATOR = new Parcelable.Creator() { - public SignalStrength createFromParcel(Parcel in) { - return new SignalStrength(in); - } + public static final @android.annotation.NonNull Parcelable.Creator<SignalStrength> CREATOR = + new Parcelable.Creator<SignalStrength>() { + public SignalStrength createFromParcel(Parcel in) { + return new SignalStrength(in); + } - public SignalStrength[] newArray(int size) { - return new SignalStrength[size]; - } + public SignalStrength[] newArray(int size) { + return new SignalStrength[size]; + } }; /** diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index 6c2dd85076fc..34966865089b 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -31,8 +31,6 @@ import android.content.Context; import android.content.pm.PackageManager; import android.database.CursorWindow; import android.net.Uri; -import android.os.Binder; -import android.os.BaseBundle; import android.os.Build; import android.os.Bundle; import android.os.RemoteException; @@ -2420,22 +2418,31 @@ public final class SmsManager { public static final String MESSAGE_STATUS_READ = "read"; /** - * Get carrier-dependent configuration values. + * Get carrier-dependent MMS configuration values. * * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier - * applications or the Telephony framework and will never trigger an SMS disambiguation - * dialog. If this method is called on a device that has multiple active subscriptions, this - * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined - * default subscription is defined, the subscription ID associated with this message will be - * INVALID, which will result in the operation being completed on the subscription associated - * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the - * operation is performed on the correct subscription. + * applications or the Telephony framework and will never trigger an SMS disambiguation dialog. + * If this method is called on a device that has multiple active subscriptions, this {@link + * SmsManager} instance has been created with {@link #getDefault()}, and no user-defined default + * subscription is defined, the subscription ID associated with this message will be INVALID, + * which will result in the operation being completed on the subscription associated with + * logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the operation is + * performed on the correct subscription. * </p> * - * @return bundle key/values pairs of configuration values + * @return the bundle key/values pairs that contains MMS configuration values */ + @Nullable public Bundle getCarrierConfigValues() { - return MmsManager.getInstance().getCarrierConfigValues(getSubscriptionId()); + try { + ISms iSms = getISmsService(); + if (iSms != null) { + return iSms.getCarrierConfigValuesForSubscriber(getSubscriptionId()); + } + } catch (RemoteException ex) { + // ignore it + } + return null; } /** diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 376a5e00ae97..15c1807cd532 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -48,7 +48,6 @@ import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.Handler; -import android.os.HandlerExecutor; import android.os.Looper; import android.os.ParcelUuid; import android.os.Process; @@ -64,6 +63,7 @@ import android.util.Pair; import com.android.internal.telephony.ISetOpportunisticDataCallback; import com.android.internal.telephony.ISub; import com.android.internal.telephony.PhoneConstants; +import com.android.internal.telephony.util.HandlerExecutor; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; @@ -1197,12 +1197,17 @@ public class SubscriptionManager { } /** - * Get the active SubscriptionInfo associated with the iccId + * Gets an active SubscriptionInfo {@link SubscriptionInfo} associated with the Sim IccId. + * * @param iccId the IccId of SIM card * @return SubscriptionInfo, maybe null if its not active + * * @hide */ - public SubscriptionInfo getActiveSubscriptionInfoForIccIndex(String iccId) { + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @Nullable + @SystemApi + public SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String iccId) { if (VDBG) logd("[getActiveSubscriptionInfoForIccIndex]+ iccId=" + iccId); if (iccId == null) { logd("[getActiveSubscriptionInfoForIccIndex]- null iccid"); @@ -2645,8 +2650,7 @@ public class SubscriptionManager { /** * Checks whether the app with the given context is authorized to manage the given subscription - * according to its metadata. Only supported for embedded subscriptions (if - * {@code SubscriptionInfo#isEmbedded} returns true). + * according to its metadata. * * @param info The subscription to check. * @return whether the app is authorized to manage this subscription per its metadata. @@ -2659,16 +2663,16 @@ public class SubscriptionManager { * Checks whether the given app is authorized to manage the given subscription. An app can only * be authorized if it is included in the {@link android.telephony.UiccAccessRule} of the * {@link android.telephony.SubscriptionInfo} with the access status. - * Only supported for embedded subscriptions (if {@link SubscriptionInfo#isEmbedded} - * returns true). * * @param info The subscription to check. * @param packageName Package name of the app to check. * @return whether the app is authorized to manage this subscription per its access rules. * @hide */ - public boolean canManageSubscription(SubscriptionInfo info, String packageName) { - if (info == null || info.getAllAccessRules() == null) { + @SystemApi + public boolean canManageSubscription(@Nullable SubscriptionInfo info, + @Nullable String packageName) { + if (info == null || info.getAllAccessRules() == null || packageName == null) { return false; } PackageManager packageManager = mContext.getPackageManager(); @@ -3163,6 +3167,34 @@ public class SubscriptionManager { } /** + * Set uicc applications being enabled or disabled. + * The value will be remembered on the subscription and will be applied whenever it's present. + * If the subscription in currently present, it will also apply the setting to modem + * immediately. + * + * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required + * + * @param enabled whether uicc applications are enabled or disabled. + * @param subscriptionId which subscription to operate on. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setUiccApplicationsEnabled(boolean enabled, int subscriptionId) { + if (VDBG) { + logd("setUiccApplicationsEnabled subId= " + subscriptionId + " enable " + enabled); + } + try { + ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + if (iSub != null) { + iSub.setUiccApplicationsEnabled(enabled, subscriptionId); + } + } catch (RemoteException ex) { + // ignore it + } + } + + /** * Whether it's supported to disable / re-enable a subscription on a physical (non-euicc) SIM. * * Physical SIM refers non-euicc, or aka non-programmable SIM. diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 85f25811b959..b8ce32f79c34 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -17,6 +17,8 @@ package android.telephony; import static android.content.Context.TELECOM_SERVICE; +import static android.provider.Telephony.Carriers.DPC_URI; +import static android.provider.Telephony.Carriers.INVALID_APN_ID; import static com.android.internal.util.Preconditions.checkNotNull; @@ -44,6 +46,7 @@ import android.compat.annotation.EnabledAfter; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.database.Cursor; import android.net.ConnectivityManager; import android.net.NetworkStats; import android.net.Uri; @@ -75,6 +78,7 @@ import android.telephony.Annotation.NetworkType; import android.telephony.Annotation.RadioPowerState; import android.telephony.Annotation.SimActivationState; import android.telephony.VisualVoicemailService.VisualVoicemailTask; +import android.telephony.data.ApnSetting; import android.telephony.emergency.EmergencyNumber; import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories; import android.telephony.ims.ImsMmTelManager; @@ -765,30 +769,6 @@ public class TelephonyManager { public static final String EXTRA_PRECISE_DISCONNECT_CAUSE = "precise_disconnect_cause"; /** - * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast - * for an String containing the data APN type. - * - * <p class="note"> - * Retrieve with - * {@link android.content.Intent#getStringExtra(String name)}. - * - * @hide - */ - public static final String EXTRA_DATA_APN_TYPE = PhoneConstants.DATA_APN_TYPE_KEY; - - /** - * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast - * for an String containing the data APN. - * - * <p class="note"> - * Retrieve with - * {@link android.content.Intent#getStringExtra(String name)}. - * - * @hide - */ - public static final String EXTRA_DATA_APN = PhoneConstants.DATA_APN_KEY; - - /** * Broadcast intent action for letting the default dialer to know to show voicemail * notification. * @@ -7573,12 +7553,12 @@ public class TelephonyManager { /** * Check whether DUN APN is required for tethering. * <p> - * Requires Permission: READ_PRIVILEGED_PHONE_STATE. + * Requires Permission: MODIFY_PHONE_STATE. * * @return {@code true} if DUN APN is required for tethering. * @hide */ - @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @SystemApi public boolean isTetheringApnRequired() { return isTetheringApnRequired(getSubId(SubscriptionManager.getActiveDataSubscriptionId())); @@ -10396,7 +10376,8 @@ public class TelephonyManager { * * @hide */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isManualNetworkSelectionAllowed() { try { ITelephony telephony = getITelephony(); @@ -11671,6 +11652,88 @@ public class TelephonyManager { } /** + * Returns a list of APNs set as overrides by the device policy manager via + * {@link #addDevicePolicyOverrideApn}. + * This method must only be called from the system or phone processes. + * + * @param context Context to use. + * @return {@link List} of APNs that have been set as overrides. + * @throws {@link SecurityException} if the caller is not the system or phone process. + * @hide + */ + @SystemApi + @TestApi + // TODO: add new permission tag indicating that this is system-only. + public @NonNull List<ApnSetting> getDevicePolicyOverrideApns(@NonNull Context context) { + try (Cursor cursor = context.getContentResolver().query(DPC_URI, null, null, null, null)) { + if (cursor == null) { + return Collections.emptyList(); + } + List<ApnSetting> apnList = new ArrayList<ApnSetting>(); + cursor.moveToPosition(-1); + while (cursor.moveToNext()) { + ApnSetting apn = ApnSetting.makeApnSetting(cursor); + apnList.add(apn); + } + return apnList; + } + } + + /** + * Used by the device policy manager to add a new override APN. + * This method must only be called from the system or phone processes. + * + * @param context Context to use. + * @param apnSetting The {@link ApnSetting} describing the new APN. + * @return An integer, corresponding to a primary key in a database, that allows the caller to + * modify the APN in the future via {@link #modifyDevicePolicyOverrideApn}, or + * {@link android.provider.Telephony.Carriers.INVALID_APN_ID} if the override operation + * failed. + * @throws {@link SecurityException} if the caller is not the system or phone process. + * @hide + */ + @SystemApi + @TestApi + // TODO: add new permission tag indicating that this is system-only. + public int addDevicePolicyOverrideApn(@NonNull Context context, + @NonNull ApnSetting apnSetting) { + Uri resultUri = context.getContentResolver().insert(DPC_URI, apnSetting.toContentValues()); + + int resultId = INVALID_APN_ID; + if (resultUri != null) { + try { + resultId = Integer.parseInt(resultUri.getLastPathSegment()); + } catch (NumberFormatException e) { + Rlog.e(TAG, "Failed to parse inserted override APN id: " + + resultUri.getLastPathSegment()); + } + } + return resultId; + } + + /** + * Used by the device policy manager to modify an override APN. + * This method must only be called from the system or phone processes. + * + * @param context Context to use. + * @param apnId The integer key of the APN to modify, as returned by + * {@link #addDevicePolicyOverrideApn} + * @param apnSetting The {@link ApnSetting} describing the updated APN. + * @return {@code true} if successful, {@code false} otherwise. + * @throws {@link SecurityException} if the caller is not the system or phone process. + * @hide + */ + @SystemApi + @TestApi + // TODO: add new permission tag indicating that this is system-only. + public boolean modifyDevicePolicyOverrideApn(@NonNull Context context, int apnId, + @NonNull ApnSetting apnSetting) { + return context.getContentResolver().update( + Uri.withAppendedPath(DPC_URI, Integer.toString(apnId)), + apnSetting.toContentValues(), null, null) > 0; + } + + /** * Return whether data is enabled for certain APN type. This will tell if framework will accept * corresponding network requests on a subId. * diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index 034fc220cbbe..dbfb6a2a0f2e 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -1056,6 +1056,11 @@ public class ApnSetting implements Parcelable { } /** @hide */ + public boolean isEmergencyApn() { + return hasApnType(TYPE_EMERGENCY); + } + + /** @hide */ public boolean canHandleType(@ApnType int type) { if (!mCarrierEnabled) { return false; diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java index 4fc6a19d1f38..cfc803ca3639 100644 --- a/telephony/java/com/android/ims/ImsConfig.java +++ b/telephony/java/com/android/ims/ImsConfig.java @@ -17,7 +17,6 @@ package com.android.ims; import android.os.Handler; -import android.os.HandlerExecutor; import android.os.Looper; import android.os.RemoteException; import android.telephony.Rlog; @@ -26,6 +25,8 @@ import android.telephony.ims.ProvisioningManager; import android.telephony.ims.aidl.IImsConfig; import android.telephony.ims.aidl.IImsConfigCallback; +import com.android.internal.telephony.util.HandlerExecutor; + import java.util.concurrent.Executor; /** diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl index 45dd5811c657..2dc59e6a7c3f 100644 --- a/telephony/java/com/android/internal/telephony/ISms.aidl +++ b/telephony/java/com/android/internal/telephony/ISms.aidl @@ -1,18 +1,18 @@ /* -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ + * Copyright 2007, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.android.internal.telephony; @@ -22,7 +22,7 @@ import android.os.Bundle; import com.android.internal.telephony.SmsRawData; /** - * Interface for applications to access the ICC phone book. + * Service interface to handle SMS API requests * * See also SmsManager.java. */ @@ -542,6 +542,13 @@ interface ISms { in List<PendingIntent> deliveryIntents); /** + * Get carrier-dependent configuration values. + * + * @param subId the subscription Id + */ + Bundle getCarrierConfigValuesForSubscriber(int subId); + + /** * Create an app-only incoming SMS request for the calling package. * * If an incoming text contains the token returned by this method the provided diff --git a/telephony/java/com/android/internal/telephony/ISmsImplBase.java b/telephony/java/com/android/internal/telephony/ISmsImplBase.java index 020b92b410f1..98095bbc12ac 100644 --- a/telephony/java/com/android/internal/telephony/ISmsImplBase.java +++ b/telephony/java/com/android/internal/telephony/ISmsImplBase.java @@ -186,6 +186,11 @@ public class ISmsImplBase extends ISms.Stub { } @Override + public Bundle getCarrierConfigValuesForSubscriber(int subId) { + throw new UnsupportedOperationException(); + } + + @Override public String createAppSpecificSmsToken(int subId, String callingPkg, PendingIntent intent) { throw new UnsupportedOperationException(); } diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl index 92aab4b12330..5a26232c8892 100755 --- a/telephony/java/com/android/internal/telephony/ISub.aidl +++ b/telephony/java/com/android/internal/telephony/ISub.aidl @@ -285,4 +285,6 @@ interface ISub { int getActiveDataSubscriptionId(); boolean canDisablePhysicalSubscription(); + + int setUiccApplicationsEnabled(boolean enabled, int subscriptionId); } diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java index 8e1a78c56e0a..48fdca7d2b8e 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java +++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java @@ -172,24 +172,6 @@ public class TelephonyIntents { = "android.intent.action.ANY_DATA_STATE"; /** - * Broadcast Action: An attempt to establish a data connection has failed. - * The intent will have the following extra values:</p> - * <dl> - * <dt>phoneName</dt><dd>A string version of the phone name.</dd> - * <dt>state</dt><dd>One of {@code CONNECTED}, {@code CONNECTING}, or {code DISCONNECTED}.</dd> - * <dt>reason</dt><dd>A string indicating the reason for the failure, if available.</dd> - * </dl> - * - * <p class="note"> - * Requires the READ_PHONE_STATE permission. - * - * <p class="note">This is a protected intent that can only be sent - * by the system. - */ - public static final String ACTION_DATA_CONNECTION_FAILED - = "android.intent.action.DATA_CONNECTION_FAILED"; - - /** * Broadcast Action: The sim card state has changed. * The intent will have the following extra values:</p> * <dl> diff --git a/telephony/java/com/android/internal/telephony/util/HandlerExecutor.java b/telephony/java/com/android/internal/telephony/util/HandlerExecutor.java new file mode 100644 index 000000000000..8a2545772b5b --- /dev/null +++ b/telephony/java/com/android/internal/telephony/util/HandlerExecutor.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.util; + +import android.annotation.NonNull; +import android.os.Handler; + +import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; + +/** + * An adapter {@link Executor} that posts all executed tasks onto the given + * {@link Handler}. + * + * @hide + */ +public class HandlerExecutor implements Executor { + private final Handler mHandler; + + public HandlerExecutor(@NonNull Handler handler) { + if (handler == null) { + throw new NullPointerException(); + } + mHandler = handler; + } + + @Override + public void execute(Runnable command) { + if (!mHandler.post(command)) { + throw new RejectedExecutionException(mHandler + " is shutting down"); + } + } +} diff --git a/tests/TelephonyCommonTests/Android.bp b/tests/TelephonyCommonTests/Android.bp new file mode 100644 index 000000000000..61c3829e68a1 --- /dev/null +++ b/tests/TelephonyCommonTests/Android.bp @@ -0,0 +1,48 @@ +// +// 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. +// + +android_test { + name: "TelephonyCommonTests", + srcs: [ + ":framework-telephony-common-sources", + "**/*.java", + ], + static_libs: [ + "mockito-target-extended", + "androidx.test.rules", + "truth-prebuilt", + "platform-test-annotations", + "androidx.core_core", + "androidx.fragment_fragment", + "androidx.test.ext.junit" + ], + + jni_libs: ["libdexmakerjvmtiagent"], + + // We need to rename SmsApplication to the test package or else it'll get clobbered by the + // hidden api checker + jarjar_rules: "jarjar-rules.txt", + + // Uncomment this and comment out the jarjar rule if you want to attach a debugger and step + // through the renamed classes. + // platform_apis: true, + + libs: [ + "android.test.runner", + "android.test.mock", + "android.test.base", + ], +} diff --git a/tests/TelephonyCommonTests/AndroidManifest.xml b/tests/TelephonyCommonTests/AndroidManifest.xml new file mode 100644 index 000000000000..63f38c6af2cb --- /dev/null +++ b/tests/TelephonyCommonTests/AndroidManifest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.internal.telephony.tests" + android:debuggable="true"> + + <application android:label="TelephonyCommonTests" + android:debuggable="true"> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.internal.telephony.tests" + android:label="Telephony common tests" + android:debuggable="true"/> +</manifest> diff --git a/tests/TelephonyCommonTests/AndroidTest.xml b/tests/TelephonyCommonTests/AndroidTest.xml new file mode 100644 index 000000000000..e9fdabcaf136 --- /dev/null +++ b/tests/TelephonyCommonTests/AndroidTest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> +<configuration description="Runs Telephony Common Test Cases."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-instrumentation" /> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="TelephonyCommonTests.apk" /> + </target_preparer> + + <option name="test-tag" value="TelephonyCommonTests" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.internal.telephony.tests" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + </test> +</configuration> diff --git a/tests/TelephonyCommonTests/jarjar-rules.txt b/tests/TelephonyCommonTests/jarjar-rules.txt new file mode 100644 index 000000000000..4d1115f3c56c --- /dev/null +++ b/tests/TelephonyCommonTests/jarjar-rules.txt @@ -0,0 +1,3 @@ +rule com.android.internal.telephony.SmsApplication* com.android.internal.telephony.tests.SmsApplication@1 +rule android.telephony.PackageChangeReceiver* com.android.internal.telephony.tests.PackageChangeReceiver@1 +rule com.android.internal.os.BackgroundThread* com.android.internal.telephony.tests.BackgroundThread@1 diff --git a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java new file mode 100644 index 000000000000..83fd20803ed8 --- /dev/null +++ b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.tests; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNotNull; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.Manifest; +import android.app.AppOpsManager; +import android.app.role.RoleManager; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.net.Uri; +import android.os.Handler; +import android.os.UserHandle; +import android.provider.Telephony; +import android.telephony.TelephonyManager; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.android.internal.telephony.SmsApplication; + +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 java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Unit tests for the {@link SmsApplication} utility class + */ +@RunWith(AndroidJUnit4.class) +public class SmsApplicationTest { + private static final ComponentName TEST_COMPONENT_NAME = + ComponentName.unflattenFromString("com.android.test/.TestSmsApp"); + private static final String MMS_RECEIVER_NAME = "TestMmsReceiver"; + private static final String RESPOND_VIA_SMS_NAME = "TestRespondViaSmsHandler"; + private static final String SEND_TO_NAME = "TestSendTo"; + private static final int SMS_APP_UID = 10001; + + private static final int FAKE_PHONE_UID = 10002; + private static final int FAKE_MMS_UID = 10003; + private static final int FAKE_BT_UID = 10004; + private static final int FAKE_TELEPHONY_PROVIDER_UID = 10005; + + private static final String[] APP_OPS_TO_CHECK = { + AppOpsManager.OPSTR_READ_SMS, + AppOpsManager.OPSTR_WRITE_SMS, + AppOpsManager.OPSTR_RECEIVE_SMS, + AppOpsManager.OPSTR_RECEIVE_WAP_PUSH, + AppOpsManager.OPSTR_SEND_SMS, + AppOpsManager.OPSTR_READ_CELL_BROADCASTS + }; + + private static final Set<String> SCHEMES_FOR_PREFERRED_APP = Arrays.stream(new String[]{ + "mms", + "mmsto", + "sms", + "smsto" + }).collect(Collectors.toSet()); + + @Mock private Context mContext; + @Mock private TelephonyManager mTelephonyManager; + @Mock private RoleManager mRoleManager; + @Mock private PackageManager mPackageManager; + @Mock private AppOpsManager mAppOpsManager; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager); + when(mContext.getSystemService(Context.ROLE_SERVICE)).thenReturn(mRoleManager); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + when(mContext.getSystemService(RoleManager.class)).thenReturn(mRoleManager); + when(mContext.getSystemService(AppOpsManager.class)).thenReturn(mAppOpsManager); + when(mContext.createContextAsUser(isNotNull(), anyInt())).thenReturn(mContext); + + doAnswer(invocation -> getResolveInfosForIntent(invocation.getArgument(0))) + .when(mPackageManager) + .queryBroadcastReceiversAsUser(nullable(Intent.class), anyInt(), + nullable(UserHandle.class)); + doAnswer(invocation -> getResolveInfosForIntent(invocation.getArgument(0))) + .when(mPackageManager) + .queryIntentActivitiesAsUser(nullable(Intent.class), anyInt(), + nullable(UserHandle.class)); + doAnswer(invocation -> getResolveInfosForIntent(invocation.getArgument(0))) + .when(mPackageManager) + .queryIntentServicesAsUser(nullable(Intent.class), anyInt(), + nullable(UserHandle.class)); + + when(mTelephonyManager.isSmsCapable()).thenReturn(true); + when(mRoleManager.isRoleAvailable(RoleManager.ROLE_SMS)).thenReturn(true); + when(mRoleManager.getDefaultSmsPackage(anyInt())) + .thenReturn(TEST_COMPONENT_NAME.getPackageName()); + + for (String opStr : APP_OPS_TO_CHECK) { + when(mAppOpsManager.unsafeCheckOp( + opStr, SMS_APP_UID, TEST_COMPONENT_NAME.getPackageName())) + .thenReturn(AppOpsManager.MODE_ALLOWED); + } + } + + @Test + public void testGetDefaultSmsApplication() { + assertEquals(TEST_COMPONENT_NAME, + SmsApplication.getDefaultSmsApplicationAsUser(mContext, false, 0)); + } + + @Test + public void testGetDefaultSmsApplicationWithAppOpsFix() throws Exception { + when(mAppOpsManager.unsafeCheckOp(AppOpsManager.OPSTR_READ_SMS, SMS_APP_UID, + TEST_COMPONENT_NAME.getPackageName())) + .thenReturn(AppOpsManager.MODE_IGNORED); + setupPackageInfosForCoreApps(); + + assertEquals(TEST_COMPONENT_NAME, + SmsApplication.getDefaultSmsApplicationAsUser(mContext, true, 0)); + verify(mAppOpsManager, atLeastOnce()).setUidMode(AppOpsManager.OPSTR_READ_SMS, SMS_APP_UID, + AppOpsManager.MODE_ALLOWED); + } + + @Test + public void testPackageChanged() throws Exception { + setupPackageInfosForCoreApps(); + SmsApplication.initSmsPackageMonitor(mContext); + verify(mContext).createContextAsUser(eq(UserHandle.ALL), anyInt()); + ArgumentCaptor<BroadcastReceiver> captor = ArgumentCaptor.forClass(BroadcastReceiver.class); + verify(mContext).registerReceiver(captor.capture(), isNotNull(), + isNull(), nullable(Handler.class)); + BroadcastReceiver smsPackageMonitor = captor.getValue(); + + Intent packageChangedIntent = new Intent(Intent.ACTION_PACKAGE_CHANGED); + packageChangedIntent.setData( + Uri.fromParts("package", TEST_COMPONENT_NAME.getPackageName(), null)); + smsPackageMonitor.onReceive(mContext, packageChangedIntent); + + ArgumentCaptor<IntentFilter> intentFilterCaptor = + ArgumentCaptor.forClass(IntentFilter.class); + verify(mPackageManager, times(SCHEMES_FOR_PREFERRED_APP.size())) + .replacePreferredActivity(intentFilterCaptor.capture(), + eq(IntentFilter.MATCH_CATEGORY_SCHEME + | IntentFilter.MATCH_ADJUSTMENT_NORMAL), + isNotNull(List.class), + eq(new ComponentName(TEST_COMPONENT_NAME.getPackageName(), SEND_TO_NAME))); + + Set<String> capturedSchemes = intentFilterCaptor.getAllValues().stream() + .map(intentFilter -> intentFilter.getDataScheme(0)) + .collect(Collectors.toSet()); + assertEquals(SCHEMES_FOR_PREFERRED_APP.size(), capturedSchemes.size()); + assertTrue(SCHEMES_FOR_PREFERRED_APP.containsAll(capturedSchemes)); + } + + private void setupPackageInfosForCoreApps() throws Exception { + PackageInfo phonePackageInfo = new PackageInfo(); + ApplicationInfo phoneApplicationInfo = new ApplicationInfo(); + phoneApplicationInfo.uid = FAKE_PHONE_UID; + phonePackageInfo.applicationInfo = phoneApplicationInfo; + when(mPackageManager.getPackageInfo(eq(SmsApplication.PHONE_PACKAGE_NAME), anyInt())) + .thenReturn(phonePackageInfo); + + PackageInfo mmsPackageInfo = new PackageInfo(); + ApplicationInfo mmsApplicationInfo = new ApplicationInfo(); + mmsApplicationInfo.uid = FAKE_MMS_UID; + mmsPackageInfo.applicationInfo = mmsApplicationInfo; + when(mPackageManager.getPackageInfo(eq(SmsApplication.MMS_SERVICE_PACKAGE_NAME), anyInt())) + .thenReturn(mmsPackageInfo); + + PackageInfo bluetoothPackageInfo = new PackageInfo(); + ApplicationInfo bluetoothApplicationInfo = new ApplicationInfo(); + bluetoothApplicationInfo.uid = FAKE_BT_UID; + bluetoothPackageInfo.applicationInfo = bluetoothApplicationInfo; + when(mPackageManager.getPackageInfo(eq(SmsApplication.BLUETOOTH_PACKAGE_NAME), anyInt())) + .thenReturn(bluetoothPackageInfo); + + PackageInfo telephonyProviderPackageInfo = new PackageInfo(); + ApplicationInfo telephonyProviderApplicationInfo = new ApplicationInfo(); + telephonyProviderApplicationInfo.uid = FAKE_TELEPHONY_PROVIDER_UID; + telephonyProviderPackageInfo.applicationInfo = telephonyProviderApplicationInfo; + when(mPackageManager.getPackageInfo( + eq(SmsApplication.TELEPHONY_PROVIDER_PACKAGE_NAME), anyInt())) + .thenReturn(telephonyProviderPackageInfo); + } + + private List<ResolveInfo> getResolveInfosForIntent(Intent intent) { + switch (intent.getAction()) { + case Telephony.Sms.Intents.SMS_DELIVER_ACTION: + return Collections.singletonList(makeSmsDeliverResolveInfo()); + case Telephony.Sms.Intents.WAP_PUSH_DELIVER_ACTION: + return Collections.singletonList(makeWapPushResolveInfo()); + case TelephonyManager.ACTION_RESPOND_VIA_MESSAGE: + return Collections.singletonList(makeRespondViaMessageResolveInfo()); + case Intent.ACTION_SENDTO: + return Collections.singletonList(makeSendToResolveInfo()); + } + return Collections.emptyList(); + } + + private ApplicationInfo makeSmsApplicationInfo() { + ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.uid = SMS_APP_UID; + return applicationInfo; + } + + private ResolveInfo makeSmsDeliverResolveInfo() { + ResolveInfo info = new ResolveInfo(); + ActivityInfo activityInfo = new ActivityInfo(); + activityInfo.applicationInfo = makeSmsApplicationInfo(); + + activityInfo.permission = Manifest.permission.BROADCAST_SMS; + activityInfo.packageName = TEST_COMPONENT_NAME.getPackageName(); + activityInfo.name = TEST_COMPONENT_NAME.getClassName(); + + info.activityInfo = activityInfo; + return info; + } + + private ResolveInfo makeWapPushResolveInfo() { + ResolveInfo info = new ResolveInfo(); + ActivityInfo activityInfo = new ActivityInfo(); + + activityInfo.permission = Manifest.permission.BROADCAST_WAP_PUSH; + activityInfo.packageName = TEST_COMPONENT_NAME.getPackageName(); + activityInfo.name = MMS_RECEIVER_NAME; + + info.activityInfo = activityInfo; + return info; + } + + private ResolveInfo makeRespondViaMessageResolveInfo() { + ResolveInfo info = new ResolveInfo(); + ServiceInfo serviceInfo = new ServiceInfo(); + + serviceInfo.permission = Manifest.permission.SEND_RESPOND_VIA_MESSAGE; + serviceInfo.packageName = TEST_COMPONENT_NAME.getPackageName(); + serviceInfo.name = RESPOND_VIA_SMS_NAME; + + info.serviceInfo = serviceInfo; + return info; + } + + private ResolveInfo makeSendToResolveInfo() { + ResolveInfo info = new ResolveInfo(); + ActivityInfo activityInfo = new ActivityInfo(); + + activityInfo.packageName = TEST_COMPONENT_NAME.getPackageName(); + activityInfo.name = SEND_TO_NAME; + + info.activityInfo = activityInfo; + return info; + } +} diff --git a/tests/net/TEST_MAPPING b/tests/net/TEST_MAPPING new file mode 100644 index 000000000000..a7853b68c1c1 --- /dev/null +++ b/tests/net/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "postsubmit": [ + { + "name": "FrameworksNetIntegrationTests" + } + ] +}
\ No newline at end of file diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java index a7eef055a71c..a7328acb73b5 100644 --- a/tests/net/common/java/android/net/LinkPropertiesTest.java +++ b/tests/net/common/java/android/net/LinkPropertiesTest.java @@ -38,6 +38,7 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; +import java.net.Inet4Address; import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; @@ -65,6 +66,7 @@ public class LinkPropertiesTest { private static final InetAddress GATEWAY62 = address("fe80::6:22%lo"); private static final InetAddress TESTIPV4ADDR = address("192.168.47.42"); private static final InetAddress TESTIPV6ADDR = address("fe80::7:33%43"); + private static final Inet4Address DHCPSERVER = (Inet4Address) address("192.0.2.1"); private static final String NAME = "qmi0"; private static final String DOMAINS = "google.com"; private static final String PRIV_DNS_SERVER_NAME = "private.dns.com"; @@ -93,6 +95,7 @@ public class LinkPropertiesTest { assertNull(lp.getHttpProxy()); assertNull(lp.getTcpBufferSizes()); assertNull(lp.getNat64Prefix()); + assertNull(lp.getDhcpServerAddress()); assertFalse(lp.isProvisioned()); assertFalse(lp.isIpv4Provisioned()); assertFalse(lp.isIpv6Provisioned()); @@ -119,6 +122,7 @@ public class LinkPropertiesTest { lp.setMtu(MTU); lp.setTcpBufferSizes(TCP_BUFFER_SIZES); lp.setNat64Prefix(new IpPrefix("2001:db8:0:64::/96")); + lp.setDhcpServerAddress(DHCPSERVER); lp.setWakeOnLanSupported(true); return lp; } @@ -960,11 +964,13 @@ public class LinkPropertiesTest { source.setWakeOnLanSupported(true); + source.setDhcpServerAddress((Inet4Address) GATEWAY1); + final LinkProperties stacked = new LinkProperties(); stacked.setInterfaceName("test-stacked"); source.addStackedLink(stacked); - assertParcelSane(source, 15 /* fieldCount */); + assertParcelSane(source, 16 /* fieldCount */); } @Test @@ -1091,6 +1097,15 @@ public class LinkPropertiesTest { } @Test + public void testDhcpServerAddress() { + final LinkProperties lp = makeTestObject(); + assertEquals(DHCPSERVER, lp.getDhcpServerAddress()); + + lp.clear(); + assertNull(lp.getDhcpServerAddress()); + } + + @Test public void testWakeOnLanSupported() { final LinkProperties lp = makeTestObject(); assertTrue(lp.isWakeOnLanSupported()); diff --git a/tests/net/integration/Android.bp b/tests/net/integration/Android.bp index 7d9b7b756ae3..874bd4b97df1 100644 --- a/tests/net/integration/Android.bp +++ b/tests/net/integration/Android.bp @@ -36,6 +36,7 @@ android_test { "services.net", "testables", ], + test_suites: ["device-tests"], use_embedded_native_libs: true, jni_libs: [ // For mockito extended |