summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt4
-rwxr-xr-xapi/system-current.txt18
-rw-r--r--api/test-current.txt3
-rw-r--r--core/java/android/app/role/RoleManager.java3
-rw-r--r--core/java/android/app/timedetector/TimeDetector.java4
-rw-r--r--core/java/android/app/timezonedetector/TimeZoneDetector.java4
-rw-r--r--core/java/android/provider/CalendarContract.java8
-rw-r--r--core/java/android/provider/Telephony.java10
-rw-r--r--core/java/android/text/format/TimeFormatter.java69
-rw-r--r--core/java/android/text/format/TimeMigrationUtils.java40
-rw-r--r--core/java/com/android/internal/os/RuntimeInit.java31
-rw-r--r--core/res/AndroidManifest.xml26
-rw-r--r--core/tests/coretests/src/android/text/format/TimeMigrationUtilsTest.java120
-rw-r--r--data/etc/com.android.settings.xml2
-rw-r--r--data/etc/privapp-permissions-platform.xml2
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java4
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java2
-rw-r--r--keystore/java/android/security/keystore/ArrayUtils.java28
-rw-r--r--keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java285
-rw-r--r--media/java/android/media/tv/DvbDeviceInfo.java7
-rw-r--r--media/java/android/media/tv/TvInputManager.java28
-rw-r--r--media/jni/android_media_MediaCodec.cpp10
-rw-r--r--mms/java/android/telephony/MmsManager.java18
-rw-r--r--mms/java/com/android/internal/telephony/IMms.aidl7
-rw-r--r--services/TEST_MAPPING48
-rw-r--r--services/core/java/com/android/server/DropBoxManagerService.java6
-rw-r--r--services/core/java/com/android/server/MmsServiceBroker.java11
-rw-r--r--services/core/java/com/android/server/content/SyncManager.java10
-rw-r--r--services/core/java/com/android/server/job/controllers/JobStatus.java10
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java6
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorService.java8
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java6
-rwxr-xr-xservices/core/java/com/android/server/tv/TvInputManagerService.java10
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java70
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java38
-rw-r--r--telecomm/java/android/telecom/Connection.java115
-rw-r--r--telephony/common/com/android/internal/telephony/SmsApplication.java44
-rwxr-xr-xtelephony/common/com/google/android/mms/pdu/PduPersister.java5
-rw-r--r--telephony/java/android/telephony/SignalStrength.java17
-rw-r--r--telephony/java/android/telephony/SmsManager.java31
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java89
-rw-r--r--telephony/java/com/android/internal/telephony/ISms.aidl37
-rw-r--r--telephony/java/com/android/internal/telephony/ISmsImplBase.java5
-rw-r--r--tests/TelephonyCommonTests/Android.bp48
-rw-r--r--tests/TelephonyCommonTests/AndroidManifest.xml30
-rw-r--r--tests/TelephonyCommonTests/AndroidTest.xml30
-rw-r--r--tests/TelephonyCommonTests/jarjar-rules.txt1
-rw-r--r--tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java238
49 files changed, 1209 insertions, 439 deletions
diff --git a/api/current.txt b/api/current.txt
index 6d5d228d295e..b99f8de8cbce 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -42575,6 +42575,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 +45088,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 +45097,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..8a3dad7ec04c 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1162,6 +1162,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);
@@ -3972,6 +3973,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 +4096,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);
@@ -6735,7 +6747,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";
@@ -8931,6 +8945,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 +8972,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,6 +9007,7 @@ 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();
@@ -9000,6 +9017,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_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..80f7e4bff61c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3040,14 +3040,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/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/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java
index 48d5cd2d65a5..611b66bc0265 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);
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/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/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/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index fa823c4bf2f6..f14145b35db7 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -35,6 +35,7 @@ import com.android.internal.logging.AndroidConfig;
import com.android.server.NetworkManagementSocketTagger;
import dalvik.system.RuntimeHooks;
+import dalvik.system.ThreadPrioritySetter;
import dalvik.system.VMRuntime;
import libcore.content.type.MimeMap;
@@ -204,6 +205,7 @@ public class RuntimeInit {
*/
public static void preForkInit() {
if (DEBUG) Slog.d(TAG, "Entered preForkInit.");
+ RuntimeHooks.setThreadPrioritySetter(new RuntimeThreadPrioritySetter());
RuntimeInit.enableDdms();
// TODO(b/142019040#comment13): Decide whether to load the default instance eagerly, i.e.
// MimeMap.setDefault(DefaultMimeMapFactory.create());
@@ -216,6 +218,35 @@ public class RuntimeInit {
MimeMap.setDefaultSupplier(DefaultMimeMapFactory::create);
}
+ private static class RuntimeThreadPrioritySetter implements ThreadPrioritySetter {
+ // Should remain consistent with kNiceValues[] in system/libartpalette/palette_android.cc
+ private static final int[] NICE_VALUES = {
+ Process.THREAD_PRIORITY_LOWEST, // 1 (MIN_PRIORITY)
+ Process.THREAD_PRIORITY_BACKGROUND + 6,
+ Process.THREAD_PRIORITY_BACKGROUND + 3,
+ Process.THREAD_PRIORITY_BACKGROUND,
+ Process.THREAD_PRIORITY_DEFAULT, // 5 (NORM_PRIORITY)
+ Process.THREAD_PRIORITY_DEFAULT - 2,
+ Process.THREAD_PRIORITY_DEFAULT - 4,
+ Process.THREAD_PRIORITY_URGENT_DISPLAY + 3,
+ Process.THREAD_PRIORITY_URGENT_DISPLAY + 2,
+ Process.THREAD_PRIORITY_URGENT_DISPLAY // 10 (MAX_PRIORITY)
+ };
+
+ @Override
+ public void setPriority(int priority) {
+ // Check NICE_VALUES[] length first.
+ if (NICE_VALUES.length != (1 + Thread.MAX_PRIORITY - Thread.MIN_PRIORITY)) {
+ throw new AssertionError("Unexpected NICE_VALUES.length=" + NICE_VALUES.length);
+ }
+ // Priority should be in the range of MIN_PRIORITY (1) to MAX_PRIORITY (10).
+ if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) {
+ throw new IllegalArgumentException("Priority out of range: " + priority);
+ }
+ Process.setThreadPriority(NICE_VALUES[priority - Thread.MIN_PRIORITY]);
+ }
+ }
+
@UnsupportedAppUsage
protected static final void commonInit() {
if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 31ac2bc7407f..6d71c508bf1c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2524,17 +2524,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 +2542,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/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/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/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/services/TEST_MAPPING b/services/TEST_MAPPING
new file mode 100644
index 000000000000..5652ab1b88b9
--- /dev/null
+++ b/services/TEST_MAPPING
@@ -0,0 +1,48 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "FrameworksMockingCoreTests",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "WmTests",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ]
+}
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..fe154ed8d396 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -137,11 +137,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 +365,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(),
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..668630e06cf0 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -119,10 +119,14 @@ 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");
}
}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index c50248d4b402..e30ac8aa0b0c 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -167,6 +167,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
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/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/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..3456cc361eb4 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -18,10 +18,12 @@ 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;
@@ -77,6 +79,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 +104,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,7 +136,7 @@ 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();
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/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java
index df668ea2313d..07dfcf09b1c5 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;
@@ -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;
@@ -501,7 +502,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 +556,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;
}
@@ -757,7 +758,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 +774,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);
}
}
@@ -899,6 +900,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/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/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/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 85f25811b959..e57032ae4b95 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;
@@ -10396,7 +10400,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 +11676,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/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/tests/TelephonyCommonTests/Android.bp b/tests/TelephonyCommonTests/Android.bp
new file mode 100644
index 000000000000..16189c8e0ac8
--- /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..fe3471973c6e
--- /dev/null
+++ b/tests/TelephonyCommonTests/jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.android.internal.telephony.SmsApplication* com.android.internal.telephony.tests.SmsApplication@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..6d0ee1891ac9
--- /dev/null
+++ b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java
@@ -0,0 +1,238 @@
+/*
+ * 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.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
+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.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+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.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.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 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
+ };
+
+ @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);
+
+ doAnswer(invocation -> getResolveInfosForIntent(invocation.getArgument(0)))
+ .when(mPackageManager)
+ .queryBroadcastReceiversAsUser(nullable(Intent.class), anyInt(), anyInt());
+ doAnswer(invocation -> getResolveInfosForIntent(invocation.getArgument(0)))
+ .when(mPackageManager)
+ .queryIntentActivitiesAsUser(nullable(Intent.class), anyInt(), anyInt());
+ 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);
+ }
+
+ 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;
+ }
+}