Merge "Fix TextViewTest.testFallbackLineSpacing on small devices"
diff --git a/api/current.txt b/api/current.txt
index 3fff068..8375b4d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6368,6 +6368,7 @@
method public int getOrganizationColor(android.content.ComponentName);
method public java.lang.CharSequence getOrganizationName(android.content.ComponentName);
method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName);
+ method public java.lang.String getPasswordBlacklistName(android.content.ComponentName);
method public long getPasswordExpiration(android.content.ComponentName);
method public long getPasswordExpirationTimeout(android.content.ComponentName);
method public int getPasswordHistoryLength(android.content.ComponentName);
@@ -6468,6 +6469,7 @@
method public void setOrganizationColor(android.content.ComponentName, int);
method public void setOrganizationName(android.content.ComponentName, java.lang.CharSequence);
method public java.lang.String[] setPackagesSuspended(android.content.ComponentName, java.lang.String[], boolean);
+ method public boolean setPasswordBlacklist(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>);
method public void setPasswordExpirationTimeout(android.content.ComponentName, long);
method public void setPasswordHistoryLength(android.content.ComponentName, int);
method public void setPasswordMinimumLength(android.content.ComponentName, int);
@@ -11092,6 +11094,7 @@
field public static final java.lang.String FEATURE_TELEPHONY = "android.hardware.telephony";
field public static final java.lang.String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma";
field public static final java.lang.String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
+ field public static final java.lang.String FEATURE_TELEPHONY_MBMS = "android.hardware.telephony.mbms";
field public static final deprecated java.lang.String FEATURE_TELEVISION = "android.hardware.type.television";
field public static final java.lang.String FEATURE_TOUCHSCREEN = "android.hardware.touchscreen";
field public static final java.lang.String FEATURE_TOUCHSCREEN_MULTITOUCH = "android.hardware.touchscreen.multitouch";
@@ -11414,7 +11417,7 @@
method public android.graphics.drawable.Drawable getProfileSwitchingIcon(android.os.UserHandle);
method public java.lang.CharSequence getProfileSwitchingLabel(android.os.UserHandle);
method public java.util.List<android.os.UserHandle> getTargetUserProfiles();
- method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
+ method public void startMainActivity(android.content.ComponentName, android.os.UserHandle);
}
}
@@ -21357,6 +21360,8 @@
method public void clearTestProviderStatus(java.lang.String);
method public java.util.List<java.lang.String> getAllProviders();
method public java.lang.String getBestProvider(android.location.Criteria, boolean);
+ method public java.lang.String getGnssHardwareModelName();
+ method public int getGnssYearOfHardware();
method public deprecated android.location.GpsStatus getGpsStatus(android.location.GpsStatus);
method public android.location.Location getLastKnownLocation(java.lang.String);
method public android.location.LocationProvider getProvider(java.lang.String);
@@ -21392,6 +21397,7 @@
method public void unregisterGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback);
method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback);
method public void unregisterGnssStatusCallback(android.location.GnssStatus.Callback);
+ field public static final java.lang.String GNSS_HARDWARE_MODEL_NAME_UNKNOWN = "Model Name Unknown";
field public static final java.lang.String GPS_PROVIDER = "gps";
field public static final java.lang.String KEY_LOCATION_CHANGED = "location";
field public static final java.lang.String KEY_PROVIDER_ENABLED = "providerEnabled";
@@ -32272,6 +32278,7 @@
field public static final java.lang.String DISALLOW_ADD_MANAGED_PROFILE = "no_add_managed_profile";
field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user";
field public static final java.lang.String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
+ field public static final java.lang.String DISALLOW_AIRPLANE_MODE = "no_airplane_mode";
field public static final java.lang.String DISALLOW_APPS_CONTROL = "no_control_apps";
field public static final java.lang.String DISALLOW_AUTOFILL = "no_autofill";
field public static final java.lang.String DISALLOW_BLUETOOTH = "no_bluetooth";
@@ -32281,6 +32288,7 @@
field public static final java.lang.String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
field public static final java.lang.String DISALLOW_CONFIG_DATE_TIME = "no_config_date_time";
field public static final java.lang.String DISALLOW_CONFIG_LOCALE = "no_config_locale";
+ field public static final java.lang.String DISALLOW_CONFIG_LOCATION_MODE = "no_config_location_mode";
field public static final java.lang.String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn";
@@ -40684,19 +40692,19 @@
}
public class MbmsDownloadSession implements java.lang.AutoCloseable {
- method public void cancelDownload(android.telephony.mbms.DownloadRequest);
+ method public int cancelDownload(android.telephony.mbms.DownloadRequest);
method public void close();
method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, android.os.Handler);
method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, int, android.os.Handler);
- method public void download(android.telephony.mbms.DownloadRequest);
+ method public int download(android.telephony.mbms.DownloadRequest);
method public int getDownloadStatus(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo);
method public java.io.File getTempFileRootDirectory();
method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads();
- method public void registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback, android.os.Handler);
+ method public int registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback, android.os.Handler);
method public void requestUpdateFileServices(java.util.List<java.lang.String>);
method public void resetDownloadKnowledge(android.telephony.mbms.DownloadRequest);
method public void setTempFileRootDirectory(java.io.File);
- method public void unregisterStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback);
+ method public int unregisterStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback);
field public static final java.lang.String DEFAULT_TOP_LEVEL_TEMP_DIRECTORY = "androidMbmsTempFileRoot";
field public static final java.lang.String EXTRA_MBMS_COMPLETED_FILE_URI = "android.telephony.extra.MBMS_COMPLETED_FILE_URI";
field public static final java.lang.String EXTRA_MBMS_DOWNLOAD_REQUEST = "android.telephony.extra.MBMS_DOWNLOAD_REQUEST";
@@ -40744,6 +40752,34 @@
field public static final int UNKNOWN_RSSI = 99; // 0x63
}
+ public class NetworkScan {
+ method public void stop() throws android.os.RemoteException;
+ field public static final int ERROR_INTERRUPTED = 10002; // 0x2712
+ field public static final int ERROR_INVALID_SCAN = 2; // 0x2
+ field public static final int ERROR_INVALID_SCANID = 10001; // 0x2711
+ field public static final int ERROR_MODEM_ERROR = 1; // 0x1
+ field public static final int ERROR_MODEM_UNAVAILABLE = 3; // 0x3
+ field public static final int ERROR_RADIO_INTERFACE_ERROR = 10000; // 0x2710
+ field public static final int ERROR_UNSUPPORTED = 4; // 0x4
+ field public static final int SUCCESS = 0; // 0x0
+ }
+
+ public final class NetworkScanRequest implements android.os.Parcelable {
+ ctor public NetworkScanRequest(int, android.telephony.RadioAccessSpecifier[], int, int, boolean, int, java.util.ArrayList<java.lang.String>);
+ method public int describeContents();
+ method public boolean getIncrementalResults();
+ method public int getIncrementalResultsPeriodicity();
+ method public int getMaxSearchTime();
+ method public java.util.ArrayList<java.lang.String> getPlmns();
+ method public int getScanType();
+ method public int getSearchPeriodicity();
+ method public android.telephony.RadioAccessSpecifier[] getSpecifiers();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.NetworkScanRequest> CREATOR;
+ field public static final int SCAN_TYPE_ONE_SHOT = 0; // 0x0
+ field public static final int SCAN_TYPE_PERIODIC = 1; // 0x1
+ }
+
public class PhoneNumberFormattingTextWatcher implements android.text.TextWatcher {
ctor public PhoneNumberFormattingTextWatcher();
ctor public PhoneNumberFormattingTextWatcher(java.lang.String);
@@ -40836,6 +40872,121 @@
field public static final int LISTEN_SIGNAL_STRENGTHS = 256; // 0x100
}
+ public final class RadioAccessSpecifier implements android.os.Parcelable {
+ ctor public RadioAccessSpecifier(int, int[], int[]);
+ method public int describeContents();
+ method public int[] getBands();
+ method public int[] getChannels();
+ method public int getRadioAccessNetwork();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.RadioAccessSpecifier> CREATOR;
+ }
+
+ public final class RadioNetworkConstants {
+ ctor public RadioNetworkConstants();
+ }
+
+ public static final class RadioNetworkConstants.EutranBands {
+ ctor public RadioNetworkConstants.EutranBands();
+ field public static final int BAND_1 = 1; // 0x1
+ field public static final int BAND_10 = 10; // 0xa
+ field public static final int BAND_11 = 11; // 0xb
+ field public static final int BAND_12 = 12; // 0xc
+ field public static final int BAND_13 = 13; // 0xd
+ field public static final int BAND_14 = 14; // 0xe
+ field public static final int BAND_17 = 17; // 0x11
+ field public static final int BAND_18 = 18; // 0x12
+ field public static final int BAND_19 = 19; // 0x13
+ field public static final int BAND_2 = 2; // 0x2
+ field public static final int BAND_20 = 20; // 0x14
+ field public static final int BAND_21 = 21; // 0x15
+ field public static final int BAND_22 = 22; // 0x16
+ field public static final int BAND_23 = 23; // 0x17
+ field public static final int BAND_24 = 24; // 0x18
+ field public static final int BAND_25 = 25; // 0x19
+ field public static final int BAND_26 = 26; // 0x1a
+ field public static final int BAND_27 = 27; // 0x1b
+ field public static final int BAND_28 = 28; // 0x1c
+ field public static final int BAND_3 = 3; // 0x3
+ field public static final int BAND_30 = 30; // 0x1e
+ field public static final int BAND_31 = 31; // 0x1f
+ field public static final int BAND_33 = 33; // 0x21
+ field public static final int BAND_34 = 34; // 0x22
+ field public static final int BAND_35 = 35; // 0x23
+ field public static final int BAND_36 = 36; // 0x24
+ field public static final int BAND_37 = 37; // 0x25
+ field public static final int BAND_38 = 38; // 0x26
+ field public static final int BAND_39 = 39; // 0x27
+ field public static final int BAND_4 = 4; // 0x4
+ field public static final int BAND_40 = 40; // 0x28
+ field public static final int BAND_41 = 41; // 0x29
+ field public static final int BAND_42 = 42; // 0x2a
+ field public static final int BAND_43 = 43; // 0x2b
+ field public static final int BAND_44 = 44; // 0x2c
+ field public static final int BAND_45 = 45; // 0x2d
+ field public static final int BAND_46 = 46; // 0x2e
+ field public static final int BAND_47 = 47; // 0x2f
+ field public static final int BAND_48 = 48; // 0x30
+ field public static final int BAND_5 = 5; // 0x5
+ field public static final int BAND_6 = 6; // 0x6
+ field public static final int BAND_65 = 65; // 0x41
+ field public static final int BAND_66 = 66; // 0x42
+ field public static final int BAND_68 = 68; // 0x44
+ field public static final int BAND_7 = 7; // 0x7
+ field public static final int BAND_70 = 70; // 0x46
+ field public static final int BAND_8 = 8; // 0x8
+ field public static final int BAND_9 = 9; // 0x9
+ }
+
+ public static final class RadioNetworkConstants.GeranBands {
+ ctor public RadioNetworkConstants.GeranBands();
+ field public static final int BAND_450 = 3; // 0x3
+ field public static final int BAND_480 = 4; // 0x4
+ field public static final int BAND_710 = 5; // 0x5
+ field public static final int BAND_750 = 6; // 0x6
+ field public static final int BAND_850 = 8; // 0x8
+ field public static final int BAND_DCS1800 = 12; // 0xc
+ field public static final int BAND_E900 = 10; // 0xa
+ field public static final int BAND_ER900 = 14; // 0xe
+ field public static final int BAND_P900 = 9; // 0x9
+ field public static final int BAND_PCS1900 = 13; // 0xd
+ field public static final int BAND_R900 = 11; // 0xb
+ field public static final int BAND_T380 = 1; // 0x1
+ field public static final int BAND_T410 = 2; // 0x2
+ field public static final int BAND_T810 = 7; // 0x7
+ }
+
+ public static final class RadioNetworkConstants.RadioAccessNetworks {
+ ctor public RadioNetworkConstants.RadioAccessNetworks();
+ field public static final int EUTRAN = 3; // 0x3
+ field public static final int GERAN = 1; // 0x1
+ field public static final int UTRAN = 2; // 0x2
+ }
+
+ public static final class RadioNetworkConstants.UtranBands {
+ ctor public RadioNetworkConstants.UtranBands();
+ field public static final int BAND_1 = 1; // 0x1
+ field public static final int BAND_10 = 10; // 0xa
+ field public static final int BAND_11 = 11; // 0xb
+ field public static final int BAND_12 = 12; // 0xc
+ field public static final int BAND_13 = 13; // 0xd
+ field public static final int BAND_14 = 14; // 0xe
+ field public static final int BAND_19 = 19; // 0x13
+ field public static final int BAND_2 = 2; // 0x2
+ field public static final int BAND_20 = 20; // 0x14
+ field public static final int BAND_21 = 21; // 0x15
+ field public static final int BAND_22 = 22; // 0x16
+ field public static final int BAND_25 = 25; // 0x19
+ field public static final int BAND_26 = 26; // 0x1a
+ field public static final int BAND_3 = 3; // 0x3
+ field public static final int BAND_4 = 4; // 0x4
+ field public static final int BAND_5 = 5; // 0x5
+ field public static final int BAND_6 = 6; // 0x6
+ field public static final int BAND_7 = 7; // 0x7
+ field public static final int BAND_8 = 8; // 0x8
+ field public static final int BAND_9 = 9; // 0x9
+ }
+
public class ServiceState implements android.os.Parcelable {
ctor public ServiceState();
ctor public ServiceState(android.telephony.ServiceState);
@@ -41120,12 +41271,15 @@
method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
method public boolean isWorldPhone();
method public void listen(android.telephony.PhoneStateListener, int);
+ method public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback);
method public void sendDialerSpecialCode(java.lang.String);
method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
method public void sendVisualVoicemailSms(java.lang.String, int, java.lang.String, android.app.PendingIntent);
method public deprecated void setDataEnabled(boolean);
method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String);
+ method public void setNetworkSelectionModeAutomatic();
+ method public boolean setNetworkSelectionModeManual(java.lang.String, boolean);
method public boolean setOperatorBrandOverride(java.lang.String);
method public boolean setPreferredNetworkTypeToGlobal();
method public void setUserMobileDataEnabled(boolean);
@@ -41223,6 +41377,17 @@
method public void onReceiveUssdResponseFailed(android.telephony.TelephonyManager, java.lang.String, int);
}
+ public final class TelephonyScanManager {
+ ctor public TelephonyScanManager();
+ }
+
+ public static abstract class TelephonyScanManager.NetworkScanCallback {
+ ctor public TelephonyScanManager.NetworkScanCallback();
+ method public void onComplete();
+ method public void onError(int);
+ method public void onResults(java.util.List<android.telephony.CellInfo>);
+ }
+
public abstract class VisualVoicemailService extends android.app.Service {
ctor public VisualVoicemailService();
method public android.os.IBinder onBind(android.content.Intent);
diff --git a/api/test-current.txt b/api/test-current.txt
index d67e997..d56b0856 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -318,10 +318,6 @@
method public void setType(int);
}
- public class LocationManager {
- method public int getGnssYearOfHardware();
- }
-
}
package android.net {
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 4a21f5c..e61c5b7 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -36,6 +36,7 @@
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.UserHandle;
import android.transition.Transition;
import android.transition.TransitionListenerAdapter;
import android.transition.TransitionManager;
@@ -265,6 +266,8 @@
public static final int ANIM_CUSTOM_IN_PLACE = 10;
/** @hide */
public static final int ANIM_CLIP_REVEAL = 11;
+ /** @hide */
+ public static final int ANIM_OPEN_CROSS_PROFILE_APPS = 12;
private String mPackageName;
private Rect mLaunchBounds;
@@ -486,6 +489,19 @@
}
/**
+ * Creates an {@link ActivityOptions} object specifying an animation where the new activity
+ * is started in another user profile by calling {@link
+ * android.content.pm.crossprofile.CrossProfileApps#startMainActivity(ComponentName, UserHandle)
+ * }.
+ * @hide
+ */
+ public static ActivityOptions makeOpenCrossProfileAppsAnimation() {
+ ActivityOptions options = new ActivityOptions();
+ options.mAnimationType = ANIM_OPEN_CROSS_PROFILE_APPS;
+ return options;
+ }
+
+ /**
* Create an ActivityOptions specifying an animation where a thumbnail
* is scaled from a given position to the new activity window that is
* being started.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 70e1a96..7e80ac7 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2690,9 +2690,6 @@
* @see #getPasswordBlacklistName
* @see #isActivePasswordSufficient
* @see #resetPasswordWithToken
- *
- * TODO(63578054): unhide for P
- * @hide
*/
public boolean setPasswordBlacklist(@NonNull ComponentName admin, @Nullable String name,
@Nullable List<String> blacklist) {
@@ -2712,9 +2709,6 @@
* @return the name of the blacklist or {@code null} if no blacklist is set
*
* @see #setPasswordBlacklist
- *
- * TODO(63578054): unhide for P
- * @hide
*/
public @Nullable String getPasswordBlacklistName(@NonNull ComponentName admin) {
try {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ff02c40..2d72632 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2075,6 +2075,13 @@
public static final String FEATURE_TELEPHONY_EUICC = "android.hardware.telephony.euicc";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
+ * supports cell-broadcast reception using the MBMS APIs.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_TELEPHONY_MBMS = "android.hardware.telephony.mbms";
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports connecting to USB devices
* as the USB host.
diff --git a/core/java/android/content/pm/crossprofile/CrossProfileApps.java b/core/java/android/content/pm/crossprofile/CrossProfileApps.java
index c9f184a..414c138 100644
--- a/core/java/android/content/pm/crossprofile/CrossProfileApps.java
+++ b/core/java/android/content/pm/crossprofile/CrossProfileApps.java
@@ -16,7 +16,6 @@
package android.content.pm.crossprofile;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
@@ -61,15 +60,10 @@
* @param user The UserHandle of the profile, must be one of the users returned by
* {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
* be thrown.
- * @param sourceBounds The Rect containing the source bounds of the clicked icon, see
- * {@link android.content.Intent#setSourceBounds(Rect)}.
- * @param startActivityOptions Options to pass to startActivity
*/
- public void startMainActivity(@NonNull ComponentName component, @NonNull UserHandle user,
- @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions) {
+ public void startMainActivity(@NonNull ComponentName component, @NonNull UserHandle user) {
try {
- mService.startActivityAsUser(mContext.getPackageName(),
- component, sourceBounds, startActivityOptions, user);
+ mService.startActivityAsUser(mContext.getPackageName(), component, user);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl b/core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl
index dd8d04f..227f91f5 100644
--- a/core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl
+++ b/core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl
@@ -26,6 +26,7 @@
* @hide
*/
interface ICrossProfileApps {
- void startActivityAsUser(in String callingPackage, in ComponentName component, in Rect sourceBounds, in Bundle startActivityOptions, in UserHandle user);
+ void startActivityAsUser(in String callingPackage, in ComponentName component,
+ in UserHandle user);
List<UserHandle> getTargetUserProfiles(in String callingPackage);
}
\ No newline at end of file
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 75cbd57..dd9fd93 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -194,6 +194,21 @@
public static final String DISALLOW_SHARE_LOCATION = "no_share_location";
/**
+ * Specifies if airplane mode is disallowed on the device.
+ *
+ * <p> This restriction can only be set by the device owner and the profile owner on the
+ * primary user and it applies globally - i.e. it disables airplane mode on the entire device.
+ * <p>The default value is <code>false</code>.
+ *
+ * <p>Key for user restrictions.
+ * <p>Type: Boolean
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_AIRPLANE_MODE = "no_airplane_mode";
+
+ /**
* Specifies if a user is disallowed from enabling the
* "Unknown Sources" setting, that allows installation of apps from unknown sources.
* The default value is <code>false</code>.
@@ -335,6 +350,28 @@
public static final String DISALLOW_CONFIG_VPN = "no_config_vpn";
/**
+ * Specifies if a user is disallowed from configuring location mode. Device owner and profile
+ * owners can set this restriction and it only applies on the managed user.
+ *
+ * <p>In a managed profile, location sharing is forced off when it's off on primary user, so
+ * user can still turn off location sharing on managed profile when the restriction is set by
+ * profile owner on managed profile.
+ *
+ * <p>This user restriction is different from {@link #DISALLOW_SHARE_LOCATION},
+ * as the device owner or profile owner can still enable or disable location mode via
+ * {@link DevicePolicyManager#setSecureSetting} when this restriction is on.
+ *
+ * <p>The default value is <code>false</code>.
+ *
+ * <p>Key for user restrictions.
+ * <p>Type: Boolean
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_CONFIG_LOCATION_MODE = "no_config_location_mode";
+
+ /**
* Specifies if date, time and timezone configuring is disallowed.
*
* <p>When restriction is set by device owners, it applies globally - i.e., it disables date,
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2223263..c6deecc 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9571,6 +9571,31 @@
public static final String ANOMALY_DETECTION_CONSTANTS = "anomaly_detection_constants";
/**
+ * Battery tip specific settings
+ * This is encoded as a key=value list, separated by commas. Ex:
+ *
+ * "battery_tip_enabled=true,summary_enabled=true,high_usage_enabled=true,"
+ * "high_usage_app_count=3,reduced_battery_enabled=false,reduced_battery_percent=50"
+ *
+ * The following keys are supported:
+ *
+ * <pre>
+ * battery_tip_enabled (boolean)
+ * summary_enabled (boolean)
+ * battery_saver_tip_enabled (boolean)
+ * high_usage_enabled (boolean)
+ * high_usage_app_count (int)
+ * app_restriction_enabled (boolean)
+ * reduced_battery_enabled (boolean)
+ * reduced_battery_percent (int)
+ * low_battery_enabled (boolean)
+ * low_battery_hour (int)
+ * </pre>
+ * @hide
+ */
+ public static final String BATTERY_TIP_CONSTANTS = "battery_tip_constants";
+
+ /**
* Always on display(AOD) specific settings
* This is encoded as a key=value list, separated by commas. Ex:
*
diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java
index 63519a6..03ff0ca 100644
--- a/core/java/android/webkit/UserPackage.java
+++ b/core/java/android/webkit/UserPackage.java
@@ -34,7 +34,7 @@
private final UserInfo mUserInfo;
private final PackageInfo mPackageInfo;
- public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.O_MR1;
+ public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.P;
public UserPackage(UserInfo user, PackageInfo packageInfo) {
this.mUserInfo = user;
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index e3efad0..b3522ec 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -47,7 +47,7 @@
// visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote.
/** @hide */
private static final String CHROMIUM_WEBVIEW_FACTORY =
- "com.android.webview.chromium.WebViewChromiumFactoryProviderForOMR1";
+ "com.android.webview.chromium.WebViewChromiumFactoryProviderForP";
private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create";
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7f71446..8e391d3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1574,6 +1574,8 @@
<java-symbol type="anim" name="voice_activity_close_enter" />
<java-symbol type="anim" name="voice_activity_open_exit" />
<java-symbol type="anim" name="voice_activity_open_enter" />
+ <java-symbol type="anim" name="activity_open_exit" />
+ <java-symbol type="anim" name="activity_open_enter" />
<java-symbol type="array" name="config_autoRotationTiltTolerance" />
<java-symbol type="array" name="config_keyboardTapVibePattern" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 78d7785..eef9866 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -173,6 +173,7 @@
Settings.Global.DEVICE_DEMO_MODE,
Settings.Global.DEVICE_IDLE_CONSTANTS,
Settings.Global.BATTERY_SAVER_CONSTANTS,
+ Settings.Global.BATTERY_TIP_CONSTANTS,
Settings.Global.DEFAULT_SM_DP_PLUS,
Settings.Global.DEVICE_NAME,
Settings.Global.DEVICE_POLICY_CONSTANTS,
diff --git a/data/sounds/AudioPackageGo.mk b/data/sounds/AudioPackageGo.mk
index ae742df..0296219 100644
--- a/data/sounds/AudioPackageGo.mk
+++ b/data/sounds/AudioPackageGo.mk
@@ -35,6 +35,7 @@
$(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
$(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:system/media/audio/ringtones/Kuma.ogg \
$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
+ $(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
$(LOCAL_PATH)/alarms/ogg/Argon.ogg:system/media/audio/alarms/Argon.ogg \
$(LOCAL_PATH)/alarms/ogg/Platinum.ogg:system/media/audio/alarms/Platinum.ogg \
$(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 8f341a8..f9075cfd 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -71,6 +71,7 @@
void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener);
int getGnssYearOfHardware();
+ String getGnssHardwareModelName();
int getGnssBatchSize(String packageName);
boolean addGnssBatchingCallback(in IBatchedLocationCallback callback, String packageName);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index d15ab33..4802b23 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -19,6 +19,7 @@
import com.android.internal.location.ProviderProperties;
import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
@@ -225,6 +226,12 @@
public static final String HIGH_POWER_REQUEST_CHANGE_ACTION =
"android.location.HIGH_POWER_REQUEST_CHANGE";
+ /**
+ * The value returned by {@link LocationManager#getGnssHardwareModelName()} when the hardware
+ * does not support providing the actual value.
+ */
+ public static final String GNSS_HARDWARE_MODEL_NAME_UNKNOWN = "Model Name Unknown";
+
// Map from LocationListeners to their associated ListenerTransport objects
private HashMap<LocationListener,ListenerTransport> mListeners =
new HashMap<LocationListener,ListenerTransport>();
@@ -1969,11 +1976,10 @@
}
/**
- * Returns the system information of the GPS hardware.
- * May return 0 if GPS hardware is earlier than 2016.
- * @hide
+ * Returns the model year of the GNSS hardware and software build.
+ *
+ * May return 0 if the model year is less than 2016.
*/
- @TestApi
public int getGnssYearOfHardware() {
try {
return mService.getGnssYearOfHardware();
@@ -1983,6 +1989,22 @@
}
/**
+ * Returns the Model Name (including Vendor and Hardware/Software Version) of the GNSS hardware
+ * driver.
+ *
+ * Will return {@link LocationManager#GNSS_HARDWARE_MODEL_NAME_UNKNOWN} when the GNSS hardware
+ * abstraction layer does not support providing this value.
+ */
+ @NonNull
+ public String getGnssHardwareModelName() {
+ try {
+ return mService.getGnssHardwareModelName();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns the batch size (in number of Location objects) that are supported by the batching
* interface.
*
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index ad43ec2..6d4a525 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -65,7 +65,6 @@
import android.service.autofill.SaveRequest;
import android.service.autofill.UserData;
import android.service.autofill.ValueFinder;
-import android.service.autofill.EditDistanceScorer;
import android.service.autofill.FieldClassification;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -1560,7 +1559,7 @@
*
* <p>A new request will be started in 2 scenarios:
* <ol>
- * <li>If the user manually requested autofill after the view was already filled.
+ * <li>If the user manually requested autofill.
* <li>If the view is part of a new partition.
* </ol>
*
@@ -1568,14 +1567,10 @@
* @param viewState The view that is entered.
* @param flags The flag that was passed by the AutofillManager.
*/
- private void requestNewFillResponseIfNecessaryLocked(@NonNull AutofillId id,
+ private void requestNewFillResponseOnViewEnteredIfNecessaryLocked(@NonNull AutofillId id,
@NonNull ViewState viewState, int flags) {
- // First check if this is a manual request after view was autofilled.
- final int state = viewState.getState();
- final boolean restart = (state & STATE_AUTOFILLED) != 0
- && (flags & FLAG_MANUAL_REQUEST) != 0;
- if (restart) {
- if (sDebug) Slog.d(TAG, "Re-starting session on view " + id);
+ if ((flags & FLAG_MANUAL_REQUEST) != 0) {
+ if (sDebug) Slog.d(TAG, "Re-starting session on view " + id + " and flags " + flags);
viewState.setState(STATE_RESTARTED_SESSION);
requestNewFillResponseLocked(flags);
return;
@@ -1730,7 +1725,7 @@
if (sVerbose && virtualBounds != null) {
Slog.v(TAG, "entered on virtual child " + id + ": " + virtualBounds);
}
- requestNewFillResponseIfNecessaryLocked(id, viewState, flags);
+ requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags);
// Remove the UI if the ViewState has changed.
if (mCurrentViewId != viewState.id) {
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 0a86281..57c992f 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -1144,7 +1144,7 @@
}
/**
- * Returns the system information of the GNSS hardware.
+ * Returns the year of the GNSS hardware.
*/
@Override
public int getGnssYearOfHardware() {
@@ -1155,6 +1155,19 @@
}
}
+
+ /**
+ * Returns the model name of the GNSS hardware.
+ */
+ @Override
+ public String getGnssHardwareModelName() {
+ if (mGnssSystemInfoProvider != null) {
+ return mGnssSystemInfoProvider.getGnssHardwareModelName();
+ } else {
+ return LocationManager.GNSS_HARDWARE_MODEL_NAME_UNKNOWN;
+ }
+ }
+
/**
* Runs some checks for GNSS (FINE) level permissions, used by several methods which directly
* (try to) access GNSS information at this layer.
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7dfde56..bc25a32 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -19900,16 +19900,14 @@
userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
ALLOW_NON_FULL, "broadcast", callerPackage);
- // Make sure that the user who is receiving this broadcast is running.
- // If not, we will just skip it. Make an exception for shutdown broadcasts
- // and upgrade steps.
-
- if (userId != UserHandle.USER_ALL && !mUserController.isUserRunning(userId, 0)) {
+ // Make sure that the user who is receiving this broadcast or its parent is running.
+ // If not, we will just skip it. Make an exception for shutdown broadcasts, upgrade steps.
+ if (userId != UserHandle.USER_ALL && !mUserController.isUserOrItsParentRunning(userId)) {
if ((callingUid != SYSTEM_UID
|| (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0)
&& !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {
Slog.w(TAG, "Skipping broadcast of " + intent
- + ": user " + userId + " is stopped");
+ + ": user " + userId + " and its parent (if any) are stopped");
return ActivityManager.BROADCAST_FAILED_USER_STOPPED;
}
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 69f6d5e..8eb5197 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -23,6 +23,7 @@
import static android.app.ActivityOptions.ANIM_CUSTOM;
import static android.app.ActivityOptions.ANIM_SCALE_UP;
import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
+import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
@@ -1476,6 +1477,9 @@
}
}
break;
+ case ANIM_OPEN_CROSS_PROFILE_APPS:
+ service.mWindowManager.overridePendingAppTransitionStartCrossProfileApps();
+ break;
default:
Slog.e(TAG, "applyOptionsLocked: Unknown animationType=" + animationType);
break;
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 14260c5..34621e0 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1737,6 +1737,19 @@
}
}
+ boolean isUserOrItsParentRunning(int userId) {
+ synchronized (mLock) {
+ if (isUserRunning(userId, 0)) {
+ return true;
+ }
+ final int parentUserId = mUserProfileGroupIds.get(userId, UserInfo.NO_PROFILE_GROUP_ID);
+ if (parentUserId == UserInfo.NO_PROFILE_GROUP_ID) {
+ return false;
+ }
+ return isUserRunning(parentUserId, 0);
+ }
+ }
+
boolean isCurrentProfile(int userId) {
synchronized (mLock) {
return ArrayUtils.contains(mCurrentProfileIds, userId);
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 3bd6446..e6de07d 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -414,16 +414,16 @@
private WorkSource mClientSource = new WorkSource();
private GeofenceHardwareImpl mGeofenceHardwareImpl;
- private int mYearOfHardware = 0;
+
+ // Volatile for simple inter-thread sync on these values.
+ private volatile int mHardwareYear = 0;
+ private volatile String mHardwareModelName = LocationManager.GNSS_HARDWARE_MODEL_NAME_UNKNOWN;
// Set lower than the current ITAR limit of 600m/s to allow this to trigger even if GPS HAL
// stops output right at 600m/s, depriving this of the information of a device that reaches
// greater than 600m/s, and higher than the speed of sound to avoid impacting most use cases.
private static final float ITAR_SPEED_LIMIT_METERS_PER_SECOND = 400.0F;
- // TODO: improve comment
- // Volatile to ensure that potentially near-concurrent outputs from HAL
- // react to this value change promptly
private volatile boolean mItarSpeedLimitExceeded = false;
// GNSS Metrics
@@ -1833,33 +1833,53 @@
/**
* called from native code to inform us what the GPS engine capabilities are
*/
- private void setEngineCapabilities(int capabilities) {
- mEngineCapabilities = capabilities;
+ private void setEngineCapabilities(final int capabilities) {
+ // send to handler thread for fast native return, and in-order handling
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mEngineCapabilities = capabilities;
- if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) {
- mOnDemandTimeInjection = true;
- requestUtcTime();
- }
+ if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) {
+ mOnDemandTimeInjection = true;
+ requestUtcTime();
+ }
- mGnssMeasurementsProvider.onCapabilitiesUpdated(
- (capabilities & GPS_CAPABILITY_MEASUREMENTS) == GPS_CAPABILITY_MEASUREMENTS);
- mGnssNavigationMessageProvider.onCapabilitiesUpdated(
- (capabilities & GPS_CAPABILITY_NAV_MESSAGES) == GPS_CAPABILITY_NAV_MESSAGES);
+ mGnssMeasurementsProvider.onCapabilitiesUpdated(hasCapability(
+ GPS_CAPABILITY_MEASUREMENTS));
+ mGnssNavigationMessageProvider.onCapabilitiesUpdated(hasCapability(
+ GPS_CAPABILITY_NAV_MESSAGES));
+ }
+ });
+ }
+
+ /**
+ * Called from native code to inform us the hardware year.
+ */
+ private void setGnssYearOfHardware(final int yearOfHardware) {
+ // mHardwareYear is simply set here, to be read elsewhere, and is volatile for safe sync
+ if (DEBUG) Log.d(TAG, "setGnssYearOfHardware called with " + yearOfHardware);
+ mHardwareYear = yearOfHardware;
}
/**
- * Called from native code to inform us the hardware information.
+ * Called from native code to inform us the hardware model name.
*/
- private void setGnssYearOfHardware(int yearOfHardware) {
- if (DEBUG) Log.d(TAG, "setGnssYearOfHardware called with " + yearOfHardware);
- mYearOfHardware = yearOfHardware;
+ private void setGnssHardwareModelName(final String modelName) {
+ // mHardwareModelName is simply set here, to be read elsewhere, and volatile for safe sync
+ if (DEBUG) Log.d(TAG, "setGnssModelName called with " + modelName);
+ mHardwareModelName = modelName;
}
public interface GnssSystemInfoProvider {
/**
- * Returns the year of GPS hardware.
+ * Returns the year of underlying GPS hardware.
*/
int getGnssYearOfHardware();
+ /**
+ * Returns the model name of underlying GPS hardware.
+ */
+ String getGnssHardwareModelName();
}
/**
@@ -1869,7 +1889,11 @@
return new GnssSystemInfoProvider() {
@Override
public int getGnssYearOfHardware() {
- return mYearOfHardware;
+ return mHardwareYear;
+ }
+ @Override
+ public String getGnssHardwareModelName() {
+ return mHardwareModelName;
}
};
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
index b390fe8..4597fad 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
@@ -54,6 +54,8 @@
"V1 encrypted_application_key".getBytes(StandardCharsets.UTF_8);
private static final byte[] RECOVERY_CLAIM_HEADER =
"V1 KF_claim".getBytes(StandardCharsets.UTF_8);
+ private static final byte[] RECOVERY_RESPONSE_HEADER =
+ "V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
private static final byte[] THM_KF_HASH_PREFIX = "THM_KF_hash".getBytes(StandardCharsets.UTF_8);
@@ -204,6 +206,28 @@
}
/**
+ * Decrypts response from recovery claim, returning the locally encrypted key.
+ *
+ * @param keyClaimant The key claimant, used by the remote service to encrypt the response.
+ * @param vaultParams Vault params associated with the claim.
+ * @param encryptedResponse The encrypted response.
+ * @return The locally encrypted recovery key.
+ * @throws NoSuchAlgorithmException if any SecureBox algorithm is not present.
+ * @throws InvalidKeyException if the {@code keyClaimant} could not be used to decrypt.
+ * @throws AEADBadTagException if the message has been tampered with or was encrypted with a
+ * different key.
+ */
+ public static byte[] decryptRecoveryClaimResponse(
+ byte[] keyClaimant, byte[] vaultParams, byte[] encryptedResponse)
+ throws NoSuchAlgorithmException, InvalidKeyException, AEADBadTagException {
+ return SecureBox.decrypt(
+ /*ourPrivateKey=*/ null,
+ /*sharedSecret=*/ keyClaimant,
+ /*header=*/ concat(RECOVERY_RESPONSE_HEADER, vaultParams),
+ /*encryptedPayload=*/ encryptedResponse);
+ }
+
+ /**
* Decrypts a recovery key, after having retrieved it from a remote server.
*
* @param lskfHash The lock screen hash associated with the key.
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index c089b40..bb9099a 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -35,6 +35,7 @@
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage;
+import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
@@ -42,10 +43,13 @@
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import javax.crypto.AEADBadTagException;
+
/**
* Class with {@link RecoverableKeyStoreLoader} API implementation and internal methods to interact
* with {@code LockSettingsService}.
@@ -60,6 +64,7 @@
private final RecoverableKeyStoreDb mDatabase;
private final RecoverySessionStorage mRecoverySessionStorage;
private final ExecutorService mExecutorService;
+ private final ListenersStorage mListenersStorage;
/**
* Returns a new or existing instance.
@@ -73,7 +78,8 @@
mContext.getApplicationContext(),
db,
new RecoverySessionStorage(),
- Executors.newSingleThreadExecutor());
+ Executors.newSingleThreadExecutor(),
+ ListenersStorage.getInstance());
}
return mInstance;
}
@@ -83,11 +89,13 @@
Context context,
RecoverableKeyStoreDb recoverableKeyStoreDb,
RecoverySessionStorage recoverySessionStorage,
- ExecutorService executorService) {
+ ExecutorService executorService,
+ ListenersStorage listenersStorage) {
mContext = context;
mDatabase = recoverableKeyStoreDb;
mRecoverySessionStorage = recoverySessionStorage;
mExecutorService = executorService;
+ mListenersStorage = listenersStorage;
}
public int initRecoveryService(
@@ -130,7 +138,8 @@
public void setSnapshotCreatedPendingIntent(@Nullable PendingIntent intent, int userId)
throws RemoteException {
checkRecoverKeyStorePermission();
- throw new UnsupportedOperationException();
+ final int recoveryAgentUid = Binder.getCallingUid();
+ mListenersStorage.setSnapshotListener(recoveryAgentUid, intent);
}
/**
@@ -150,18 +159,37 @@
throw new UnsupportedOperationException();
}
+ /**
+ * Updates recovery status for the application given its {@code packageName}.
+ *
+ * @param packageName which recoverable key statuses will be returned
+ * @param aliases - KeyStore aliases or {@code null} for all aliases of the app
+ * @param status - new status
+ */
public void setRecoveryStatus(
@NonNull String packageName, @Nullable String[] aliases, int status, int userId)
throws RemoteException {
checkRecoverKeyStorePermission();
- throw new UnsupportedOperationException();
+ int uid = Binder.getCallingUid();
+ if (packageName != null) {
+ // TODO: get uid for package name, when many apps are supported.
+ }
+ if (aliases == null) {
+ // Get all keys for the app.
+ Map<String, Integer> allKeys = mDatabase.getStatusForAllKeys(uid);
+ aliases = new String[allKeys.size()];
+ allKeys.keySet().toArray(aliases);
+ }
+ for (String alias: aliases) {
+ mDatabase.setRecoveryStatus(uid, alias, status);
+ }
}
/**
- * Gets recovery status for keys {@code packageName}.
+ * Gets recovery status for caller or other application {@code packageName}.
+ * @param packageName which recoverable keys statuses will be returned.
*
- * @param packageName which recoverable keys statuses will be returned
- * @return Map from KeyStore alias to recovery status
+ * @return {@code Map} from KeyStore alias to recovery status.
*/
public @NonNull Map<String, Integer> getRecoveryStatus(@Nullable String packageName, int userId)
throws RemoteException {
@@ -169,7 +197,7 @@
// If caller is a recovery agent it can check statuses for other packages, but
// only for recoverable keys it manages.
checkRecoverKeyStorePermission();
- throw new UnsupportedOperationException();
+ return mDatabase.getStatusForAllKeys(Binder.getCallingUid());
}
/**
@@ -225,7 +253,7 @@
* @param verifierPublicKey X509-encoded public key.
* @param vaultParams Additional params associated with vault.
* @param vaultChallenge Challenge issued by vault service.
- * @param secrets Lock-screen hashes. Should have a single element. TODO: why is this a list?
+ * @param secrets Lock-screen hashes. For now only a single secret is supported.
* @return Encrypted bytes of recovery claim. This can then be issued to the vault service.
*
* @hide
@@ -248,7 +276,8 @@
byte[] keyClaimant = KeySyncUtils.generateKeyClaimant();
byte[] kfHash = secrets.get(0).getSecret();
mRecoverySessionStorage.add(
- userId, new RecoverySessionStorage.Entry(sessionId, kfHash, keyClaimant));
+ userId,
+ new RecoverySessionStorage.Entry(sessionId, kfHash, keyClaimant, vaultParams));
try {
byte[] thmKfHash = KeySyncUtils.calculateThmKfHash(kfHash);
@@ -275,14 +304,101 @@
}
}
+ /**
+ * Invoked by a recovery agent after a successful recovery claim is sent to the remote vault
+ * service.
+ *
+ * <p>TODO: should also load into AndroidKeyStore.
+ *
+ * @param sessionId The session ID used to generate the claim. See
+ * {@link #startRecoverySession(String, byte[], byte[], byte[], List, int)}.
+ * @param encryptedRecoveryKey The encrypted recovery key blob returned by the remote vault
+ * service.
+ * @param applicationKeys The encrypted key blobs returned by the remote vault service. These
+ * were wrapped with the recovery key.
+ * @param uid The uid of the recovery agent.
+ * @throws RemoteException if an error occurred recovering the keys.
+ */
public void recoverKeys(
@NonNull String sessionId,
- @NonNull byte[] recoveryKeyBlob,
+ @NonNull byte[] encryptedRecoveryKey,
@NonNull List<KeyEntryRecoveryData> applicationKeys,
- int userId)
+ int uid)
throws RemoteException {
checkRecoverKeyStorePermission();
- throw new UnsupportedOperationException();
+
+ RecoverySessionStorage.Entry sessionEntry = mRecoverySessionStorage.get(uid, sessionId);
+ if (sessionEntry == null) {
+ throw new RemoteException(String.format(Locale.US,
+ "User %d does not have pending session '%s'", uid, sessionId));
+ }
+
+ try {
+ byte[] recoveryKey = decryptRecoveryKey(sessionEntry, encryptedRecoveryKey);
+ recoverApplicationKeys(recoveryKey, applicationKeys);
+ } finally {
+ sessionEntry.destroy();
+ mRecoverySessionStorage.remove(uid);
+ }
+ }
+
+ private byte[] decryptRecoveryKey(
+ RecoverySessionStorage.Entry sessionEntry, byte[] encryptedClaimResponse)
+ throws RemoteException {
+ try {
+ byte[] locallyEncryptedKey = KeySyncUtils.decryptRecoveryClaimResponse(
+ sessionEntry.getKeyClaimant(),
+ sessionEntry.getVaultParams(),
+ encryptedClaimResponse);
+ return KeySyncUtils.decryptRecoveryKey(sessionEntry.getLskfHash(), locallyEncryptedKey);
+ } catch (InvalidKeyException | AEADBadTagException e) {
+ throw new RemoteException(
+ "Failed to decrypt recovery key",
+ e,
+ /*enableSuppression=*/ true,
+ /*writeableStackTrace=*/ true);
+ } catch (NoSuchAlgorithmException e) {
+ // Should never happen: all the algorithms used are required by AOSP implementations
+ throw new RemoteException(
+ "Missing required algorithm",
+ e,
+ /*enableSuppression=*/ true,
+ /*writeableStackTrace=*/ true);
+ }
+ }
+
+ /**
+ * Uses {@code recoveryKey} to decrypt {@code applicationKeys}.
+ *
+ * <p>TODO: and load them into store?
+ *
+ * @throws RemoteException if an error occurred decrypting the keys.
+ */
+ private void recoverApplicationKeys(
+ @NonNull byte[] recoveryKey,
+ @NonNull List<KeyEntryRecoveryData> applicationKeys) throws RemoteException {
+ for (KeyEntryRecoveryData applicationKey : applicationKeys) {
+ String alias = new String(applicationKey.getAlias(), StandardCharsets.UTF_8);
+ byte[] encryptedKeyMaterial = applicationKey.getEncryptedKeyMaterial();
+
+ try {
+ // TODO: put decrypted key material in appropriate AndroidKeyStore
+ KeySyncUtils.decryptApplicationKey(recoveryKey, encryptedKeyMaterial);
+ } catch (NoSuchAlgorithmException e) {
+ // Should never happen: all the algorithms used are required by AOSP implementations
+ throw new RemoteException(
+ "Missing required algorithm",
+ e,
+ /*enableSuppression=*/ true,
+ /*writeableStackTrace=*/ true);
+ } catch (InvalidKeyException | AEADBadTagException e) {
+ throw new RemoteException(
+ "Failed to recover key with alias '" + alias + "'",
+ e,
+ /*enableSuppression=*/ true,
+ /*writeableStackTrace=*/ true);
+ }
+ }
}
/**
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java
index d8a2d31..801d4de 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java
@@ -230,7 +230,7 @@
* @throws NoSuchAlgorithmException if any underlying crypto algorithm is not supported
* @throws InvalidKeyException if the provided key is invalid for underlying crypto algorithms
* @throws AEADBadTagException if the authentication tag contained in {@code encryptedPayload}
- * cannot be validated
+ * cannot be validated, or if the payload is not a valid SecureBox V2 payload.
* @hide
*/
public static byte[] decrypt(
@@ -244,12 +244,14 @@
throw new IllegalArgumentException("Both the private key and shared secret are empty");
}
header = emptyByteArrayIfNull(header);
- encryptedPayload = emptyByteArrayIfNull(encryptedPayload);
+ if (encryptedPayload == null) {
+ throw new NullPointerException("Encrypted payload must not be null.");
+ }
ByteBuffer ciphertextBuffer = ByteBuffer.wrap(encryptedPayload);
byte[] version = readEncryptedPayload(ciphertextBuffer, VERSION.length);
if (!Arrays.equals(version, VERSION)) {
- throw new IllegalArgumentException("The payload was not encrypted by SecureBox v2");
+ throw new AEADBadTagException("The payload was not encrypted by SecureBox v2");
}
byte[] senderPublicKeyBytes;
@@ -271,12 +273,13 @@
return aesGcmDecrypt(decryptionKey, randNonce, ciphertext, header);
}
- private static byte[] readEncryptedPayload(ByteBuffer buffer, int length) {
+ private static byte[] readEncryptedPayload(ByteBuffer buffer, int length)
+ throws AEADBadTagException {
byte[] output = new byte[length];
try {
buffer.get(output);
} catch (BufferUnderflowException ex) {
- throw new IllegalArgumentException("The encrypted payload is too short");
+ throw new AEADBadTagException("The encrypted payload is too short");
}
return output;
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
index dfa173c..54aa9f0 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
@@ -17,6 +17,7 @@
package com.android.server.locksettings.recoverablekeystore;
import android.util.Log;
+import android.security.recoverablekeystore.RecoverableKeyStoreLoader;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
@@ -45,6 +46,7 @@
private static final int GCM_TAG_LENGTH_BITS = 128;
private final int mPlatformKeyGenerationId;
+ private final int mRecoveryStatus;
private final byte[] mNonce;
private final byte[] mKeyMaterial;
@@ -94,7 +96,25 @@
return new WrappedKey(
/*nonce=*/ cipher.getIV(),
/*keyMaterial=*/ encryptedKeyMaterial,
- /*platformKeyGenerationId=*/ wrappingKey.getGenerationId());
+ /*platformKeyGenerationId=*/ wrappingKey.getGenerationId(),
+ RecoverableKeyStoreLoader.RECOVERY_STATUS_SYNC_IN_PROGRESS);
+ }
+
+ /**
+ * A new instance with default recovery status.
+ *
+ * @param nonce The nonce with which the key material was encrypted.
+ * @param keyMaterial The encrypted bytes of the key material.
+ * @param platformKeyGenerationId The generation ID of the key used to wrap this key.
+ *
+ * @see RecoverableKeyStoreLoader.RECOVERY_STATUS_SYNC_IN_PROGRESS
+ * @hide
+ */
+ public WrappedKey(byte[] nonce, byte[] keyMaterial, int platformKeyGenerationId) {
+ mNonce = nonce;
+ mKeyMaterial = keyMaterial;
+ mPlatformKeyGenerationId = platformKeyGenerationId;
+ mRecoveryStatus = RecoverableKeyStoreLoader.RECOVERY_STATUS_SYNC_IN_PROGRESS;
}
/**
@@ -103,13 +123,16 @@
* @param nonce The nonce with which the key material was encrypted.
* @param keyMaterial The encrypted bytes of the key material.
* @param platformKeyGenerationId The generation ID of the key used to wrap this key.
+ * @param recoveryStatus recovery status of the key.
*
* @hide
*/
- public WrappedKey(byte[] nonce, byte[] keyMaterial, int platformKeyGenerationId) {
+ public WrappedKey(byte[] nonce, byte[] keyMaterial, int platformKeyGenerationId,
+ int recoveryStatus) {
mNonce = nonce;
mKeyMaterial = keyMaterial;
mPlatformKeyGenerationId = platformKeyGenerationId;
+ mRecoveryStatus = recoveryStatus;
}
/**
@@ -130,7 +153,6 @@
return mKeyMaterial;
}
-
/**
* Returns the generation ID of the platform key, with which this key was wrapped.
*
@@ -141,6 +163,15 @@
}
/**
+ * Returns recovery status of the key.
+ *
+ * @hide
+ */
+ public int getRecoveryStatus() {
+ return mRecoveryStatus;
+ }
+
+ /**
* Unwraps the {@code wrappedKeys} with the {@code platformKey}.
*
* @return The unwrapped keys, indexed by alias.
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
index ed570c3..56804e0 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
@@ -16,11 +16,13 @@
package com.android.server.locksettings.recoverablekeystore.storage;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
+import android.security.recoverablekeystore.RecoverableKeyStoreLoader;
import android.util.Log;
import com.android.server.locksettings.recoverablekeystore.WrappedKey;
@@ -80,6 +82,7 @@
values.put(KeysEntry.COLUMN_NAME_WRAPPED_KEY, wrappedKey.getKeyMaterial());
values.put(KeysEntry.COLUMN_NAME_LAST_SYNCED_AT, LAST_SYNCED_AT_UNSYNCED);
values.put(KeysEntry.COLUMN_NAME_GENERATION_ID, wrappedKey.getPlatformKeyGenerationId());
+ values.put(KeysEntry.COLUMN_NAME_RECOVERY_STATUS, wrappedKey.getRecoveryStatus());
return db.replace(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values);
}
@@ -94,7 +97,8 @@
KeysEntry._ID,
KeysEntry.COLUMN_NAME_NONCE,
KeysEntry.COLUMN_NAME_WRAPPED_KEY,
- KeysEntry.COLUMN_NAME_GENERATION_ID};
+ KeysEntry.COLUMN_NAME_GENERATION_ID,
+ KeysEntry.COLUMN_NAME_RECOVERY_STATUS};
String selection =
KeysEntry.COLUMN_NAME_UID + " = ? AND "
+ KeysEntry.COLUMN_NAME_ALIAS + " = ?";
@@ -128,11 +132,73 @@
cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_WRAPPED_KEY));
int generationId = cursor.getInt(
cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_GENERATION_ID));
- return new WrappedKey(nonce, keyMaterial, generationId);
+ int recoveryStatus = cursor.getInt(
+ cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_RECOVERY_STATUS));
+ return new WrappedKey(nonce, keyMaterial, generationId, recoveryStatus);
}
}
/**
+ * Returns all statuses for keys {@code uid} and {@code platformKeyGenerationId}.
+ *
+ * @param uid of the application
+ *
+ * @return Map from Aliases to status.
+ *
+ * @hide
+ */
+ public @NonNull Map<String, Integer> getStatusForAllKeys(int uid) {
+ SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();
+ String[] projection = {
+ KeysEntry._ID,
+ KeysEntry.COLUMN_NAME_ALIAS,
+ KeysEntry.COLUMN_NAME_RECOVERY_STATUS};
+ String selection =
+ KeysEntry.COLUMN_NAME_UID + " = ?";
+ String[] selectionArguments = {Integer.toString(uid)};
+
+ try (
+ Cursor cursor = db.query(
+ KeysEntry.TABLE_NAME,
+ projection,
+ selection,
+ selectionArguments,
+ /*groupBy=*/ null,
+ /*having=*/ null,
+ /*orderBy=*/ null)
+ ) {
+ HashMap<String, Integer> statuses = new HashMap<>();
+ while (cursor.moveToNext()) {
+ String alias = cursor.getString(
+ cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_ALIAS));
+ int recoveryStatus = cursor.getInt(
+ cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_RECOVERY_STATUS));
+ statuses.put(alias, recoveryStatus);
+ }
+ return statuses;
+ }
+ }
+
+ /**
+ * Updates status for given key.
+ * @param uid of the application
+ * @param alias of the key
+ * @param status - new status
+ * @return number of updated entries.
+ * @hide
+ **/
+ public int setRecoveryStatus(int uid, String alias, int status) {
+ SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put(KeysEntry.COLUMN_NAME_RECOVERY_STATUS, status);
+ String selection =
+ KeysEntry.COLUMN_NAME_UID + " = ? AND "
+ + KeysEntry.COLUMN_NAME_ALIAS + " = ?";
+ return db.update(KeysEntry.TABLE_NAME, values, selection,
+ new String[] {String.valueOf(uid), alias});
+ }
+
+ /**
* Returns all keys for the given {@code userId} and {@code platformKeyGenerationId}.
*
* @param userId User id of the profile to which all the keys are associated.
@@ -148,7 +214,8 @@
KeysEntry._ID,
KeysEntry.COLUMN_NAME_NONCE,
KeysEntry.COLUMN_NAME_WRAPPED_KEY,
- KeysEntry.COLUMN_NAME_ALIAS};
+ KeysEntry.COLUMN_NAME_ALIAS,
+ KeysEntry.COLUMN_NAME_RECOVERY_STATUS};
String selection =
KeysEntry.COLUMN_NAME_USER_ID + " = ? AND "
+ KeysEntry.COLUMN_NAME_GENERATION_ID + " = ?";
@@ -173,7 +240,10 @@
cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_WRAPPED_KEY));
String alias = cursor.getString(
cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_ALIAS));
- keys.put(alias, new WrappedKey(nonce, keyMaterial, platformKeyGenerationId));
+ int recoveryStatus = cursor.getInt(
+ cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_RECOVERY_STATUS));
+ keys.put(alias, new WrappedKey(nonce, keyMaterial, platformKeyGenerationId,
+ recoveryStatus));
}
return keys;
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
index dcd1827..82e5650 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
@@ -62,6 +62,11 @@
* Timestamp of when this key was last synced with remote storage, or -1 if never synced.
*/
static final String COLUMN_NAME_LAST_SYNCED_AT = "last_synced_at";
+
+ /**
+ * Status of the key sync {@code RecoverableKeyStoreLoader#setRecoveryStatus}
+ */
+ static final String COLUMN_NAME_RECOVERY_STATUS = "recovery_status";
}
/**
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
index b017fbb..46846e1 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
@@ -24,6 +24,7 @@
+ KeysEntry.COLUMN_NAME_WRAPPED_KEY + " BLOB,"
+ KeysEntry.COLUMN_NAME_GENERATION_ID + " INTEGER,"
+ KeysEntry.COLUMN_NAME_LAST_SYNCED_AT + " INTEGER,"
+ + KeysEntry.COLUMN_NAME_RECOVERY_STATUS + " INTEGER,"
+ "UNIQUE(" + KeysEntry.COLUMN_NAME_UID + ","
+ KeysEntry.COLUMN_NAME_ALIAS + "))";
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorage.java
index bc56ae1..f7633e4 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorage.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorage.java
@@ -129,15 +129,17 @@
public static class Entry implements Destroyable {
private final byte[] mLskfHash;
private final byte[] mKeyClaimant;
+ private final byte[] mVaultParams;
private final String mSessionId;
/**
* @hide
*/
- public Entry(String sessionId, byte[] lskfHash, byte[] keyClaimant) {
- this.mLskfHash = lskfHash;
- this.mSessionId = sessionId;
- this.mKeyClaimant = keyClaimant;
+ public Entry(String sessionId, byte[] lskfHash, byte[] keyClaimant, byte[] vaultParams) {
+ mLskfHash = lskfHash;
+ mSessionId = sessionId;
+ mKeyClaimant = keyClaimant;
+ mVaultParams = vaultParams;
}
/**
@@ -160,6 +162,15 @@
}
/**
+ * Returns the vault params associated with the session.
+ *
+ * @hide
+ */
+ public byte[] getVaultParams() {
+ return mVaultParams;
+ }
+
+ /**
* Overwrites the memory for the lskf hash and key claimant.
*
* @hide
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 4564988..9240843 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -115,6 +115,8 @@
UserManager.DISALLOW_AUTOFILL,
UserManager.DISALLOW_USER_SWITCH,
UserManager.DISALLOW_UNIFIED_PASSWORD,
+ UserManager.DISALLOW_CONFIG_LOCATION_MODE,
+ UserManager.DISALLOW_AIRPLANE_MODE
});
/**
@@ -142,7 +144,8 @@
UserManager.DISALLOW_FUN,
UserManager.DISALLOW_SAFE_BOOT,
UserManager.DISALLOW_CREATE_WINDOWS,
- UserManager.DISALLOW_DATA_ROAMING
+ UserManager.DISALLOW_DATA_ROAMING,
+ UserManager.DISALLOW_AIRPLANE_MODE
);
/**
@@ -197,7 +200,8 @@
* Special user restrictions that are always applied to all users no matter who sets them.
*/
private static final Set<String> PROFILE_GLOBAL_RESTRICTIONS = Sets.newArraySet(
- UserManager.ENSURE_VERIFY_APPS
+ UserManager.ENSURE_VERIFY_APPS,
+ UserManager.DISALLOW_AIRPLANE_MODE
);
/**
diff --git a/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java
index 854b704..d6281c5 100644
--- a/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java
@@ -19,6 +19,7 @@
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import android.annotation.UserIdInt;
+import android.app.ActivityOptions;
import android.app.AppOpsManager;
import android.content.ComponentName;
import android.content.Context;
@@ -74,8 +75,6 @@
public void startActivityAsUser(
String callingPackage,
ComponentName component,
- Rect sourceBounds,
- Bundle startActivityOptions,
UserHandle user) throws RemoteException {
Preconditions.checkNotNull(callingPackage);
Preconditions.checkNotNull(component);
@@ -103,7 +102,6 @@
// CATEGORY_LAUNCHER as calling startActivityAsUser ignore them if component is present.
final Intent launchIntent = new Intent(Intent.ACTION_MAIN);
launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- launchIntent.setSourceBounds(sourceBounds);
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
// Only package name is set here, as opposed to component name, because intent action and
@@ -114,7 +112,8 @@
final long ident = mInjector.clearCallingIdentity();
try {
launchIntent.setComponent(component);
- mContext.startActivityAsUser(launchIntent, startActivityOptions, user);
+ mContext.startActivityAsUser(launchIntent,
+ ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle(), user);
} finally {
mInjector.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index c2cbced..4a74e29 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -44,17 +44,17 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
-import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import static com.android.server.wm.proto.AppTransitionProto.APP_TRANSITION_STATE;
import static com.android.server.wm.proto.AppTransitionProto.LAST_USED_APP_TRANSITION;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.Bitmap;
import android.graphics.GraphicBuffer;
import android.graphics.Path;
import android.graphics.Rect;
@@ -64,6 +64,7 @@
import android.os.IRemoteCallback;
import android.os.RemoteException;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
@@ -199,6 +200,13 @@
private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN = 6;
private static final int NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE = 7;
private static final int NEXT_TRANSIT_TYPE_CLIP_REVEAL = 8;
+
+ /**
+ * Refers to the transition to activity started by using {@link
+ * android.content.pm.crossprofile.CrossProfileApps#startMainActivity(ComponentName, UserHandle)
+ * }.
+ */
+ private static final int NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS = 9;
private int mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
// These are the possible states for the enter/exit activities during a thumbnail transition
@@ -1605,6 +1613,17 @@
+ " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
+ " Callers=" + Debug.getCallers(3));
}
+ } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS
+ && (transit == TRANSIT_ACTIVITY_OPEN
+ || transit == TRANSIT_TASK_OPEN
+ || transit == TRANSIT_TASK_TO_FRONT)) {
+ a = loadAnimationRes("android", enter
+ ? com.android.internal.R.anim.activity_open_enter
+ : com.android.internal.R.anim.activity_open_exit);
+ Slog.v(TAG,
+ "applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS:"
+ + " anim=" + a + " transit=" + appTransitionToString(transit)
+ + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
} else {
int animAttr = 0;
switch (transit) {
@@ -1833,6 +1852,17 @@
}
/**
+ * @see {@link #NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS}
+ */
+ void overridePendingAppTransitionStartCrossProfileApps() {
+ if (isTransitionSet()) {
+ clear();
+ mNextAppTransitionType = NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS;
+ postAnimationCallback();
+ }
+ }
+
+ /**
* If a future is set for the app transition specs, fetch it in another thread.
*/
private void fetchAppTransitionSpecsFromFuture() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9fc9f3c..58a5ca4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2706,6 +2706,12 @@
}
}
+ public void overridePendingAppTransitionStartCrossProfileApps() {
+ synchronized (mWindowMap) {
+ mAppTransition.overridePendingAppTransitionStartCrossProfileApps();
+ }
+ }
+
void prolongAnimationsFromSpecs(@NonNull AppTransitionAnimationSpec[] specs, boolean scaleUp) {
// This is used by freeform <-> recents windows transition. We need to synchronize
// the animation with the appearance of the content of recents, so we will make
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 246bd42..67bad0f 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -48,6 +48,7 @@
static jmethodID method_reportNmea;
static jmethodID method_setEngineCapabilities;
static jmethodID method_setGnssYearOfHardware;
+static jmethodID method_setGnssHardwareModelName;
static jmethodID method_xtraDownloadRequest;
static jmethodID method_reportNiNotification;
static jmethodID method_requestRefLocation;
@@ -373,12 +374,11 @@
Return<void> GnssCallback::gnssNameCb(const android::hardware::hidl_string& name) {
ALOGD("%s: name=%s\n", __func__, name.c_str());
- // TODO(b/38003769): build Java code to connect to below code
- /*
JNIEnv* env = getJniEnv();
- env->CallVoidMethod(mCallbacksObj, method_setGnssHardwareName, name);
+ jstring jstringName = env->NewStringUTF(name.c_str());
+ env->CallVoidMethod(mCallbacksObj, method_setGnssHardwareModelName, jstringName);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
- */
+
return Void();
}
@@ -1031,6 +1031,8 @@
method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V");
method_setEngineCapabilities = env->GetMethodID(clazz, "setEngineCapabilities", "(I)V");
method_setGnssYearOfHardware = env->GetMethodID(clazz, "setGnssYearOfHardware", "(I)V");
+ method_setGnssHardwareModelName = env->GetMethodID(clazz, "setGnssHardwareModelName",
+ "(Ljava/lang/String;)V");
method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V");
method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification",
"(IIIIILjava/lang/String;Ljava/lang/String;II)V");
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
index c328dda..6254d52 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
@@ -55,6 +55,8 @@
utf8Bytes("snQzsbvclkSsG6PwasAp1oFLzbq3KtFe");
private static final byte[] RECOVERY_CLAIM_HEADER =
"V1 KF_claim".getBytes(StandardCharsets.UTF_8);
+ private static final byte[] RECOVERY_RESPONSE_HEADER =
+ "V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
@Test
public void calculateThmKfHash_isShaOfLockScreenHashWithPrefix() throws Exception {
@@ -173,6 +175,42 @@
}
@Test
+ public void decryptRecoveryClaimResponse_decryptsAValidResponse() throws Exception {
+ byte[] keyClaimant = KeySyncUtils.generateKeyClaimant();
+ byte[] vaultParams = randomBytes(100);
+ byte[] recoveryKey = randomBytes(32);
+ byte[] encryptedPayload = SecureBox.encrypt(
+ /*theirPublicKey=*/ null,
+ /*sharedSecret=*/ keyClaimant,
+ /*header=*/ KeySyncUtils.concat(RECOVERY_RESPONSE_HEADER, vaultParams),
+ /*payload=*/ recoveryKey);
+
+ byte[] decrypted = KeySyncUtils.decryptRecoveryClaimResponse(
+ keyClaimant, vaultParams, encryptedPayload);
+
+ assertArrayEquals(recoveryKey, decrypted);
+ }
+
+ @Test
+ public void decryptRecoveryClaimResponse_throwsIfCannotDecrypt() throws Exception {
+ byte[] vaultParams = randomBytes(100);
+ byte[] recoveryKey = randomBytes(32);
+ byte[] encryptedPayload = SecureBox.encrypt(
+ /*theirPublicKey=*/ null,
+ /*sharedSecret=*/ KeySyncUtils.generateKeyClaimant(),
+ /*header=*/ KeySyncUtils.concat(RECOVERY_RESPONSE_HEADER, vaultParams),
+ /*payload=*/ recoveryKey);
+
+ try {
+ KeySyncUtils.decryptRecoveryClaimResponse(
+ KeySyncUtils.generateKeyClaimant(), vaultParams, encryptedPayload);
+ fail("Did not throw decrypting with bad keyClaimant");
+ } catch (AEADBadTagException error) {
+ // expected
+ }
+ }
+
+ @Test
public void encryptRecoveryClaim_encryptsLockScreenAndKeyClaimant() throws Exception {
KeyPair keyPair = SecureBox.genKeyPair();
byte[] keyClaimant = KeySyncUtils.generateKeyClaimant();
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 56a23de..953ea62 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -19,16 +19,23 @@
import static android.security.recoverablekeystore.KeyStoreRecoveryMetadata.TYPE_LOCKSCREEN;
import static android.security.recoverablekeystore.KeyStoreRecoveryMetadata.TYPE_PASSWORD;
+import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.security.recoverablekeystore.KeyDerivationParameters;
+import android.security.recoverablekeystore.KeyEntryRecoveryData;
import android.security.recoverablekeystore.KeyStoreRecoveryMetadata;
import android.security.recoverablekeystore.RecoverableKeyStoreLoader;
import android.support.test.InstrumentationRegistry;
@@ -39,6 +46,7 @@
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import org.junit.After;
import org.junit.Before;
@@ -50,6 +58,11 @@
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.Executors;
+import java.util.Map;
+import java.util.Random;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -77,8 +90,16 @@
private static final byte[] TEST_VAULT_PARAMS = getUtf8Bytes("vault_params");
private static final int TEST_USER_ID = 10009;
private static final int KEY_CLAIMANT_LENGTH_BYTES = 16;
+ private static final byte[] RECOVERY_RESPONSE_HEADER =
+ "V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
+ private static final String TEST_ALIAS = "nick";
+
+ private static final int GENERATION_ID = 1;
+ private static final byte[] NONCE = getUtf8Bytes("nonce");
+ private static final byte[] KEY_MATERIAL = getUtf8Bytes("keymaterial");
@Mock private Context mMockContext;
+ @Mock private ListenersStorage mMockListenersStorage;
private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
private File mDatabaseFile;
@@ -97,7 +118,8 @@
mMockContext,
mRecoverableKeyStoreDb,
mRecoverySessionStorage,
- Executors.newSingleThreadExecutor());
+ Executors.newSingleThreadExecutor(),
+ mMockListenersStorage);
}
@After
@@ -113,16 +135,17 @@
TEST_PUBLIC_KEY,
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(new KeyStoreRecoveryMetadata(
- TYPE_LOCKSCREEN,
- TYPE_PASSWORD,
- KeyDerivationParameters.createSHA256Parameters(TEST_SALT),
- TEST_SECRET)),
+ ImmutableList.of(
+ new KeyStoreRecoveryMetadata(
+ TYPE_LOCKSCREEN,
+ TYPE_PASSWORD,
+ KeyDerivationParameters.createSHA256Parameters(TEST_SALT),
+ TEST_SECRET)),
TEST_USER_ID);
- verify(mMockContext, times(1)).enforceCallingOrSelfPermission(
- eq(RecoverableKeyStoreLoader.PERMISSION_RECOVER_KEYSTORE),
- any());
+ verify(mMockContext, times(1))
+ .enforceCallingOrSelfPermission(
+ eq(RecoverableKeyStoreLoader.PERMISSION_RECOVER_KEYSTORE), any());
}
@Test
@@ -132,16 +155,17 @@
TEST_PUBLIC_KEY,
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(new KeyStoreRecoveryMetadata(
- TYPE_LOCKSCREEN,
- TYPE_PASSWORD,
- KeyDerivationParameters.createSHA256Parameters(TEST_SALT),
- TEST_SECRET)),
+ ImmutableList.of(
+ new KeyStoreRecoveryMetadata(
+ TYPE_LOCKSCREEN,
+ TYPE_PASSWORD,
+ KeyDerivationParameters.createSHA256Parameters(TEST_SALT),
+ TEST_SECRET)),
TEST_USER_ID);
assertEquals(1, mRecoverySessionStorage.size());
- RecoverySessionStorage.Entry entry = mRecoverySessionStorage.get(
- TEST_USER_ID, TEST_SESSION_ID);
+ RecoverySessionStorage.Entry entry =
+ mRecoverySessionStorage.get(TEST_USER_ID, TEST_SESSION_ID);
assertArrayEquals(TEST_SECRET, entry.getLskfHash());
assertEquals(KEY_CLAIMANT_LENGTH_BYTES, entry.getKeyClaimant().length);
}
@@ -156,9 +180,9 @@
TEST_VAULT_CHALLENGE,
ImmutableList.of(),
TEST_USER_ID);
+ fail("should have thrown");
} catch (RemoteException e) {
- assertEquals("Only a single KeyStoreRecoveryMetadata is supported",
- e.getMessage());
+ assertEquals("Only a single KeyStoreRecoveryMetadata is supported", e.getMessage());
}
}
@@ -170,19 +194,230 @@
getUtf8Bytes("0"),
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
- ImmutableList.of(new KeyStoreRecoveryMetadata(
- TYPE_LOCKSCREEN,
- TYPE_PASSWORD,
- KeyDerivationParameters.createSHA256Parameters(TEST_SALT),
- TEST_SECRET)),
+ ImmutableList.of(
+ new KeyStoreRecoveryMetadata(
+ TYPE_LOCKSCREEN,
+ TYPE_PASSWORD,
+ KeyDerivationParameters.createSHA256Parameters(TEST_SALT),
+ TEST_SECRET)),
TEST_USER_ID);
+ fail("should have thrown");
} catch (RemoteException e) {
- assertEquals("Not a valid X509 key",
+ assertEquals("Not a valid X509 key", e.getMessage());
+ }
+ }
+
+ @Test
+ public void recoverKeys_throwsIfNoSessionIsPresent() throws Exception {
+ try {
+ mRecoverableKeyStoreManager.recoverKeys(
+ TEST_SESSION_ID,
+ /*recoveryKeyBlob=*/ randomBytes(32),
+ /*applicationKeys=*/ ImmutableList.of(
+ new KeyEntryRecoveryData(getUtf8Bytes("alias"), randomBytes(32))
+ ),
+ TEST_USER_ID);
+ fail("should have thrown");
+ } catch (RemoteException e) {
+ assertEquals("User 10009 does not have pending session 'karlin'",
e.getMessage());
}
}
+ @Test
+ public void recoverKeys_throwsIfRecoveryClaimCannotBeDecrypted() throws Exception {
+ mRecoverableKeyStoreManager.startRecoverySession(
+ TEST_SESSION_ID,
+ TEST_PUBLIC_KEY,
+ TEST_VAULT_PARAMS,
+ TEST_VAULT_CHALLENGE,
+ ImmutableList.of(new KeyStoreRecoveryMetadata(
+ TYPE_LOCKSCREEN,
+ TYPE_PASSWORD,
+ KeyDerivationParameters.createSHA256Parameters(TEST_SALT),
+ TEST_SECRET)),
+ TEST_USER_ID);
+
+ try {
+ mRecoverableKeyStoreManager.recoverKeys(
+ TEST_SESSION_ID,
+ /*encryptedRecoveryKey=*/ randomBytes(60),
+ /*applicationKeys=*/ ImmutableList.of(),
+ /*uid=*/ TEST_USER_ID);
+ fail("should have thrown");
+ } catch (RemoteException e) {
+ assertEquals("Failed to decrypt recovery key", e.getMessage());
+ }
+ }
+
+ @Test
+ public void recoverKeys_throwsIfFailedToDecryptAnApplicationKey() throws Exception {
+ mRecoverableKeyStoreManager.startRecoverySession(
+ TEST_SESSION_ID,
+ TEST_PUBLIC_KEY,
+ TEST_VAULT_PARAMS,
+ TEST_VAULT_CHALLENGE,
+ ImmutableList.of(new KeyStoreRecoveryMetadata(
+ TYPE_LOCKSCREEN,
+ TYPE_PASSWORD,
+ KeyDerivationParameters.createSHA256Parameters(TEST_SALT),
+ TEST_SECRET)),
+ TEST_USER_ID);
+ byte[] keyClaimant = mRecoverySessionStorage.get(TEST_USER_ID, TEST_SESSION_ID)
+ .getKeyClaimant();
+ SecretKey recoveryKey = randomRecoveryKey();
+ byte[] encryptedClaimResponse = encryptClaimResponse(
+ keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
+ KeyEntryRecoveryData badApplicationKey = new KeyEntryRecoveryData(
+ TEST_ALIAS.getBytes(StandardCharsets.UTF_8),
+ randomBytes(32));
+
+ try {
+ mRecoverableKeyStoreManager.recoverKeys(
+ TEST_SESSION_ID,
+ /*encryptedRecoveryKey=*/ encryptedClaimResponse,
+ /*applicationKeys=*/ ImmutableList.of(badApplicationKey),
+ /*uid=*/ TEST_USER_ID);
+ fail("should have thrown");
+ } catch (RemoteException e) {
+ assertEquals("Failed to recover key with alias 'nick'", e.getMessage());
+ }
+ }
+
+ @Test
+ public void recoverKeys_doesNotThrowIfAllIsOk() throws Exception {
+ mRecoverableKeyStoreManager.startRecoverySession(
+ TEST_SESSION_ID,
+ TEST_PUBLIC_KEY,
+ TEST_VAULT_PARAMS,
+ TEST_VAULT_CHALLENGE,
+ ImmutableList.of(new KeyStoreRecoveryMetadata(
+ TYPE_LOCKSCREEN,
+ TYPE_PASSWORD,
+ KeyDerivationParameters.createSHA256Parameters(TEST_SALT),
+ TEST_SECRET)),
+ TEST_USER_ID);
+ byte[] keyClaimant = mRecoverySessionStorage.get(TEST_USER_ID, TEST_SESSION_ID)
+ .getKeyClaimant();
+ SecretKey recoveryKey = randomRecoveryKey();
+ byte[] encryptedClaimResponse = encryptClaimResponse(
+ keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
+ KeyEntryRecoveryData applicationKey = new KeyEntryRecoveryData(
+ TEST_ALIAS.getBytes(StandardCharsets.UTF_8),
+ randomEncryptedApplicationKey(recoveryKey)
+ );
+
+ mRecoverableKeyStoreManager.recoverKeys(
+ TEST_SESSION_ID,
+ encryptedClaimResponse,
+ ImmutableList.of(applicationKey),
+ TEST_USER_ID);
+ }
+
+ @Test
+ public void setSnapshotCreatedPendingIntent() throws Exception {
+ int uid = Binder.getCallingUid();
+ PendingIntent intent = PendingIntent.getBroadcast(
+ InstrumentationRegistry.getTargetContext(), /*requestCode=*/1,
+ new Intent(), /*flags=*/ 0);
+ mRecoverableKeyStoreManager.setSnapshotCreatedPendingIntent(intent, /*userId=*/ 0);
+ verify(mMockListenersStorage).setSnapshotListener(eq(uid), any(PendingIntent.class));
+ }
+
+ @Test
+ public void setRecoveryStatus_forOneAlias() throws Exception {
+ int userId = UserHandle.getCallingUserId();
+ int uid = Binder.getCallingUid();
+ int status = 100;
+ int status2 = 200;
+ String alias = "key1";
+ WrappedKey wrappedKey = new WrappedKey(NONCE, KEY_MATERIAL, GENERATION_ID, status);
+ mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
+ Map<String, Integer> statuses =
+ mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null, userId);
+ assertThat(statuses).hasSize(1);
+ assertThat(statuses).containsEntry(alias, status);
+
+ mRecoverableKeyStoreManager.setRecoveryStatus(
+ /*packageName=*/ null, new String[] {alias}, status2, userId);
+ statuses = mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null, userId);
+ assertThat(statuses).hasSize(1);
+ assertThat(statuses).containsEntry(alias, status2); // updated
+ }
+
+ @Test
+ public void setRecoveryStatus_for2Aliases() throws Exception {
+ int userId = UserHandle.getCallingUserId();
+ int uid = Binder.getCallingUid();
+ int status = 100;
+ int status2 = 200;
+ int status3 = 300;
+ String alias = "key1";
+ String alias2 = "key2";
+ WrappedKey wrappedKey = new WrappedKey(NONCE, KEY_MATERIAL, GENERATION_ID, status);
+ mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
+ mRecoverableKeyStoreDb.insertKey(userId, uid, alias2, wrappedKey);
+ Map<String, Integer> statuses =
+ mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null, userId);
+ assertThat(statuses).hasSize(2);
+ assertThat(statuses).containsEntry(alias, status);
+ assertThat(statuses).containsEntry(alias2, status);
+
+ mRecoverableKeyStoreManager.setRecoveryStatus(
+ /*packageName=*/ null, /*aliases=*/ null, status2, userId);
+ statuses = mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null, userId);
+ assertThat(statuses).hasSize(2);
+ assertThat(statuses).containsEntry(alias, status2); // updated
+ assertThat(statuses).containsEntry(alias2, status2); // updated
+
+ mRecoverableKeyStoreManager.setRecoveryStatus(
+ /*packageName=*/ null, new String[] {alias2}, status3, userId);
+
+ statuses = mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null, userId);
+ assertThat(statuses).hasSize(2);
+ assertThat(statuses).containsEntry(alias, status2);
+ assertThat(statuses).containsEntry(alias2, status3); // updated
+
+ mRecoverableKeyStoreManager.setRecoveryStatus(
+ /*packageName=*/ null, new String[] {alias, alias2}, status, userId);
+
+ statuses = mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null, userId);
+ assertThat(statuses).hasSize(2);
+ assertThat(statuses).containsEntry(alias, status); // updated
+ assertThat(statuses).containsEntry(alias2, status); // updated
+ }
+
+ private static byte[] randomEncryptedApplicationKey(SecretKey recoveryKey) throws Exception {
+ return KeySyncUtils.encryptKeysWithRecoveryKey(recoveryKey, ImmutableMap.of(
+ "alias", new SecretKeySpec(randomBytes(32), "AES")
+ )).get("alias");
+ }
+
+ private static byte[] encryptClaimResponse(
+ byte[] keyClaimant,
+ byte[] lskfHash,
+ byte[] vaultParams,
+ SecretKey recoveryKey) throws Exception {
+ byte[] locallyEncryptedRecoveryKey = KeySyncUtils.locallyEncryptRecoveryKey(
+ lskfHash, recoveryKey);
+ return SecureBox.encrypt(
+ /*theirPublicKey=*/ null,
+ /*sharedSecret=*/ keyClaimant,
+ /*header=*/ KeySyncUtils.concat(RECOVERY_RESPONSE_HEADER, vaultParams),
+ /*payload=*/ locallyEncryptedRecoveryKey);
+ }
+
+ private static SecretKey randomRecoveryKey() {
+ return new SecretKeySpec(randomBytes(32), "AES");
+ }
+
private static byte[] getUtf8Bytes(String s) {
return s.getBytes(StandardCharsets.UTF_8);
}
+
+ private static byte[] randomBytes(int n) {
+ byte[] bytes = new byte[n];
+ new Random().nextBytes(bytes);
+ return bytes;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/SecureBoxTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/SecureBoxTest.java
index 72b69f0..35ec23b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/SecureBoxTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/SecureBoxTest.java
@@ -274,9 +274,9 @@
@Test
public void decrypt_nullEncryptedPayload() throws Exception {
- IllegalArgumentException expected =
+ NullPointerException expected =
expectThrows(
- IllegalArgumentException.class,
+ NullPointerException.class,
() ->
SecureBox.decrypt(
THM_PRIVATE_KEY,
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
index 76cbea8..aaeaf7c 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
@@ -16,6 +16,7 @@
package com.android.server.locksettings.recoverablekeystore.storage;
+import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -27,6 +28,7 @@
import org.junit.runner.RunWith;
import android.content.Context;
+import android.security.recoverablekeystore.RecoverableKeyStoreLoader;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -119,10 +121,11 @@
int userId = 12;
int uid = 1009;
int generationId = 6;
+ int status = 120;
String alias = "test";
byte[] nonce = getUtf8Bytes("nonce");
byte[] keyMaterial = getUtf8Bytes("keymaterial");
- WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId);
+ WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId, 120);
mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias);
@@ -130,6 +133,7 @@
assertArrayEquals(nonce, retrievedKey.getNonce());
assertArrayEquals(keyMaterial, retrievedKey.getKeyMaterial());
assertEquals(generationId, retrievedKey.getPlatformKeyGenerationId());
+ assertEquals(status,retrievedKey.getRecoveryStatus());
}
@Test
@@ -208,6 +212,84 @@
assertEquals(2, mRecoverableKeyStoreDb.getPlatformKeyGenerationId(userId));
}
+ @Test
+ public void setRecoveryStatus_withSingleKey() {
+ int userId = 12;
+ int uid = 1009;
+ int generationId = 6;
+ int status = 120;
+ int status2 = 121;
+ String alias = "test";
+ byte[] nonce = getUtf8Bytes("nonce");
+ byte[] keyMaterial = getUtf8Bytes("keymaterial");
+ WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId, status);
+ mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
+
+ WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias);
+ assertThat(retrievedKey.getRecoveryStatus()).isEqualTo(status);
+
+ mRecoverableKeyStoreDb.setRecoveryStatus(uid, alias, status2);
+
+ retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias);
+ assertThat(retrievedKey.getRecoveryStatus()).isEqualTo(status2);
+ }
+
+ @Test
+ public void getStatusForAllKeys_with3Keys() {
+ int userId = 12;
+ int uid = 1009;
+ int generationId = 6;
+ int status = 120;
+ int status2 = 121;
+ String alias = "test";
+ String alias2 = "test2";
+ String alias3 = "test3";
+ byte[] nonce = getUtf8Bytes("nonce");
+ byte[] keyMaterial = getUtf8Bytes("keymaterial");
+
+ WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId, status);
+ mRecoverableKeyStoreDb.insertKey(userId, uid, alias2, wrappedKey);
+ WrappedKey wrappedKey2 = new WrappedKey(nonce, keyMaterial, generationId, status);
+ mRecoverableKeyStoreDb.insertKey(userId, uid, alias3, wrappedKey);
+ WrappedKey wrappedKeyWithDefaultStatus = new WrappedKey(nonce, keyMaterial, generationId);
+ mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKeyWithDefaultStatus);
+
+ Map<String, Integer> statuses = mRecoverableKeyStoreDb.getStatusForAllKeys(uid);
+ assertThat(statuses).hasSize(3);
+ assertThat(statuses).containsEntry(alias,
+ RecoverableKeyStoreLoader.RECOVERY_STATUS_SYNC_IN_PROGRESS);
+ assertThat(statuses).containsEntry(alias2, status);
+ assertThat(statuses).containsEntry(alias3, status);
+
+ int updates = mRecoverableKeyStoreDb.setRecoveryStatus(uid, alias, status2);
+ assertThat(updates).isEqualTo(1);
+ updates = mRecoverableKeyStoreDb.setRecoveryStatus(uid, alias3, status2);
+ assertThat(updates).isEqualTo(1);
+ statuses = mRecoverableKeyStoreDb.getStatusForAllKeys(uid);
+
+ assertThat(statuses).hasSize(3);
+ assertThat(statuses).containsEntry(alias, status2); // updated from default
+ assertThat(statuses).containsEntry(alias2, status);
+ assertThat(statuses).containsEntry(alias3, status2); // updated
+ }
+
+ @Test
+ public void setRecoveryStatus_withEmptyDatabase() throws Exception{
+ int uid = 1009;
+ String alias = "test";
+ int status = 120;
+ int updates = mRecoverableKeyStoreDb.setRecoveryStatus(uid, alias, status);
+ assertThat(updates).isEqualTo(0); // database was empty
+ }
+
+
+ @Test
+ public void getStatusForAllKeys_withEmptyDatabase() {
+ int uid = 1009;
+ Map<String, Integer> statuses = mRecoverableKeyStoreDb.getStatusForAllKeys(uid);
+ assertThat(statuses).hasSize(0);
+ }
+
private static byte[] getUtf8Bytes(String s) {
return s.getBytes(StandardCharsets.UTF_8);
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorageTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorageTest.java
index 6aeff28..6f93fe4 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorageTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorageTest.java
@@ -37,6 +37,7 @@
private static final int TEST_USER_ID = 696;
private static final byte[] TEST_LSKF_HASH = getUtf8Bytes("lskf");
private static final byte[] TEST_KEY_CLAIMANT = getUtf8Bytes("0000111122223333");
+ private static final byte[] TEST_VAULT_PARAMS = getUtf8Bytes("vault params vault params");
@Test
public void size_isZeroForEmpty() {
@@ -47,7 +48,7 @@
public void size_incrementsAfterAdd() {
RecoverySessionStorage storage = new RecoverySessionStorage();
storage.add(TEST_USER_ID, new RecoverySessionStorage.Entry(
- TEST_SESSION_ID, lskfHashFixture(), keyClaimantFixture()));
+ TEST_SESSION_ID, lskfHashFixture(), keyClaimantFixture(), vaultParamsFixture()));
assertEquals(1, storage.size());
}
@@ -56,7 +57,7 @@
public void size_decrementsAfterRemove() {
RecoverySessionStorage storage = new RecoverySessionStorage();
storage.add(TEST_USER_ID, new RecoverySessionStorage.Entry(
- TEST_SESSION_ID, lskfHashFixture(), keyClaimantFixture()));
+ TEST_SESSION_ID, lskfHashFixture(), keyClaimantFixture(), vaultParamsFixture()));
storage.remove(TEST_USER_ID);
assertEquals(0, storage.size());
@@ -66,7 +67,7 @@
public void remove_overwritesLskfHashMemory() {
RecoverySessionStorage storage = new RecoverySessionStorage();
RecoverySessionStorage.Entry entry = new RecoverySessionStorage.Entry(
- TEST_SESSION_ID, lskfHashFixture(), keyClaimantFixture());
+ TEST_SESSION_ID, lskfHashFixture(), keyClaimantFixture(), vaultParamsFixture());
storage.add(TEST_USER_ID, entry);
storage.remove(TEST_USER_ID);
@@ -78,7 +79,7 @@
public void remove_overwritesKeyClaimantMemory() {
RecoverySessionStorage storage = new RecoverySessionStorage();
RecoverySessionStorage.Entry entry = new RecoverySessionStorage.Entry(
- TEST_SESSION_ID, lskfHashFixture(), keyClaimantFixture());
+ TEST_SESSION_ID, lskfHashFixture(), keyClaimantFixture(), vaultParamsFixture());
storage.add(TEST_USER_ID, entry);
storage.remove(TEST_USER_ID);
@@ -90,7 +91,7 @@
public void destroy_overwritesLskfHashMemory() {
RecoverySessionStorage storage = new RecoverySessionStorage();
RecoverySessionStorage.Entry entry = new RecoverySessionStorage.Entry(
- TEST_SESSION_ID, lskfHashFixture(), keyClaimantFixture());
+ TEST_SESSION_ID, lskfHashFixture(), keyClaimantFixture(), vaultParamsFixture());
storage.add(TEST_USER_ID, entry);
storage.destroy();
@@ -102,7 +103,7 @@
public void destroy_overwritesKeyClaimantMemory() {
RecoverySessionStorage storage = new RecoverySessionStorage();
RecoverySessionStorage.Entry entry = new RecoverySessionStorage.Entry(
- TEST_SESSION_ID, lskfHashFixture(), keyClaimantFixture());
+ TEST_SESSION_ID, lskfHashFixture(), keyClaimantFixture(), vaultParamsFixture());
storage.add(TEST_USER_ID, entry);
storage.destroy();
@@ -126,6 +127,10 @@
return Arrays.copyOf(TEST_KEY_CLAIMANT, TEST_KEY_CLAIMANT.length);
}
+ private static byte[] vaultParamsFixture() {
+ return Arrays.copyOf(TEST_VAULT_PARAMS, TEST_VAULT_PARAMS.length);
+ }
+
private static byte[] getUtf8Bytes(String s) {
return s.getBytes(StandardCharsets.UTF_8);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/crossprofile/CrossProfileAppsServiceImplTest.java b/services/tests/servicestests/src/com/android/server/pm/crossprofile/CrossProfileAppsServiceImplTest.java
index 880b77e..ff55a2b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/crossprofile/CrossProfileAppsServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/crossprofile/CrossProfileAppsServiceImplTest.java
@@ -205,8 +205,6 @@
mCrossProfileAppsServiceImpl.startActivityAsUser(
PACKAGE_ONE,
ACTIVITY_COMPONENT,
- null,
- null,
UserHandle.of(PRIMARY_USER)));
verify(mContext, never())
@@ -217,33 +215,6 @@
}
@Test
- public void startActivityAsUser_profile_successWithOption() throws Exception {
- Bundle options = Bundle.forPair("test_key", "test_value");
-
- mCrossProfileAppsServiceImpl.startActivityAsUser(
- PACKAGE_ONE,
- ACTIVITY_COMPONENT,
- null,
- options,
- UserHandle.of(PROFILE_OF_PRIMARY_USER));
-
- ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
- ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
-
- verify(mContext)
- .startActivityAsUser(
- intentCaptor.capture(),
- bundleCaptor.capture(),
- eq(UserHandle.of(PROFILE_OF_PRIMARY_USER)));
-
- Intent intent = intentCaptor.getValue();
- assertEquals(ACTIVITY_COMPONENT, intent.getComponent());
-
- Bundle bundle = bundleCaptor.getValue();
- assertEquals("test_value", bundle.getString("test_key"));
- }
-
- @Test
public void startActivityAsUser_profile_notInstalled() throws Exception {
mockAppsInstalled(PACKAGE_ONE, PROFILE_OF_PRIMARY_USER, false);
@@ -253,8 +224,6 @@
mCrossProfileAppsServiceImpl.startActivityAsUser(
PACKAGE_ONE,
ACTIVITY_COMPONENT,
- null,
- null,
UserHandle.of(PROFILE_OF_PRIMARY_USER)));
verify(mContext, never())
@@ -272,8 +241,6 @@
mCrossProfileAppsServiceImpl.startActivityAsUser(
PACKAGE_TWO,
ACTIVITY_COMPONENT,
- null,
- null,
UserHandle.of(PROFILE_OF_PRIMARY_USER)));
verify(mContext, never())
@@ -293,8 +260,6 @@
mCrossProfileAppsServiceImpl.startActivityAsUser(
PACKAGE_ONE,
ACTIVITY_COMPONENT,
- null,
- null,
UserHandle.of(PROFILE_OF_PRIMARY_USER)));
verify(mContext, never())
@@ -312,8 +277,6 @@
mCrossProfileAppsServiceImpl.startActivityAsUser(
PACKAGE_ONE,
new ComponentName(PACKAGE_TWO, "test"),
- null,
- null,
UserHandle.of(PROFILE_OF_PRIMARY_USER)));
verify(mContext, never())
@@ -331,8 +294,6 @@
mCrossProfileAppsServiceImpl.startActivityAsUser(
PACKAGE_ONE,
ACTIVITY_COMPONENT,
- null,
- null,
UserHandle.of(SECONDARY_USER)));
verify(mContext, never())
@@ -349,8 +310,6 @@
mCrossProfileAppsServiceImpl.startActivityAsUser(
PACKAGE_ONE,
ACTIVITY_COMPONENT,
- null,
- null,
UserHandle.of(PRIMARY_USER));
verify(mContext)
diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index a554c69..059a2d0 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -502,8 +502,10 @@
* Asynchronous errors through the callback may include any error not specific to the
* streaming use-case.
* @param request The request that specifies what should be downloaded.
+ * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
+ * and some other error code otherwise.
*/
- public void download(@NonNull DownloadRequest request) {
+ public int download(@NonNull DownloadRequest request) {
IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) {
throw new IllegalStateException("Middleware not yet bound");
@@ -519,13 +521,16 @@
setTempFileRootDirectory(tempRootDirectory);
}
- writeDownloadRequestToken(request);
try {
- downloadService.download(request);
+ int result = downloadService.download(request);
+ if (result == MbmsErrors.SUCCESS) {
+ writeDownloadRequestToken(request);
+ }
+ return result;
} catch (RemoteException e) {
mService.set(null);
sIsInitialized.set(false);
- sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ return MbmsErrors.ERROR_MIDDLEWARE_LOST;
}
}
@@ -565,8 +570,10 @@
* @param callback The callback that should be called when the middleware has information to
* share on the download.
* @param handler The {@link Handler} on which calls to {@code callback} should be enqueued on.
+ * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
+ * and some other error code otherwise.
*/
- public void registerStateCallback(@NonNull DownloadRequest request,
+ public int registerStateCallback(@NonNull DownloadRequest request,
@NonNull DownloadStateCallback callback, @NonNull Handler handler) {
IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) {
@@ -583,16 +590,15 @@
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request.");
}
- sendErrorToApp(result, null);
- return;
+ return result;
}
} catch (RemoteException e) {
mService.set(null);
sIsInitialized.set(false);
- sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
- return;
+ return MbmsErrors.ERROR_MIDDLEWARE_LOST;
}
mInternalDownloadCallbacks.put(callback, internalCallback);
+ return MbmsErrors.SUCCESS;
}
/**
@@ -606,8 +612,10 @@
*
* @param request The {@link DownloadRequest} provided during registration
* @param callback The callback provided during registration.
+ * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
+ * and some other error code otherwise.
*/
- public void unregisterStateCallback(@NonNull DownloadRequest request,
+ public int unregisterStateCallback(@NonNull DownloadRequest request,
@NonNull DownloadStateCallback callback) {
try {
IMbmsDownloadService downloadService = mService.get();
@@ -617,6 +625,9 @@
InternalDownloadStateCallback internalCallback =
mInternalDownloadCallbacks.get(callback);
+ if (internalCallback == null) {
+ throw new IllegalArgumentException("Provided callback was never registered");
+ }
try {
int result = downloadService.unregisterStateCallback(request, internalCallback);
@@ -624,12 +635,12 @@
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request.");
}
- sendErrorToApp(result, null);
+ return result;
}
} catch (RemoteException e) {
mService.set(null);
sIsInitialized.set(false);
- sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ return MbmsErrors.ERROR_MIDDLEWARE_LOST;
}
} finally {
InternalDownloadStateCallback internalCallback =
@@ -638,6 +649,7 @@
internalCallback.stop();
}
}
+ return MbmsErrors.SUCCESS;
}
/**
@@ -647,8 +659,10 @@
* this method will throw an {@link IllegalArgumentException}.
*
* @param downloadRequest The download request that you wish to cancel.
+ * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
+ * and some other error code otherwise.
*/
- public void cancelDownload(@NonNull DownloadRequest downloadRequest) {
+ public int cancelDownload(@NonNull DownloadRequest downloadRequest) {
IMbmsDownloadService downloadService = mService.get();
if (downloadService == null) {
throw new IllegalStateException("Middleware not yet bound");
@@ -660,16 +674,15 @@
if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
throw new IllegalArgumentException("Unknown download request.");
}
- sendErrorToApp(result, null);
- return;
+ } else {
+ deleteDownloadRequestToken(downloadRequest);
}
+ return result;
} catch (RemoteException e) {
mService.set(null);
sIsInitialized.set(false);
- sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
- return;
+ return MbmsErrors.ERROR_MIDDLEWARE_LOST;
}
- deleteDownloadRequestToken(downloadRequest);
}
/**
diff --git a/telephony/java/android/telephony/NetworkScan.java b/telephony/java/android/telephony/NetworkScan.java
index f15fde8..a277212 100644
--- a/telephony/java/android/telephony/NetworkScan.java
+++ b/telephony/java/android/telephony/NetworkScan.java
@@ -19,50 +19,92 @@
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.annotation.IntDef;
import android.util.Log;
import com.android.internal.telephony.ITelephony;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
- * Allows applications to request the system to perform a network scan.
- *
- * The caller of {@link #requestNetworkScan(NetworkScanRequest, NetworkScanCallback)} will
- * receive a NetworkScan which contains the callback method to stop the scan requested.
- * @hide
+ * The caller of
+ * {@link TelephonyManager#requestNetworkScan(NetworkScanRequest, NetworkScanCallback)}
+ * will receive an instance of {@link NetworkScan}, which contains a callback method
+ * {@link #stop()} for stopping the in-progress scan.
*/
public class NetworkScan {
- public static final String TAG = "NetworkScan";
+ private static final String TAG = "NetworkScan";
// Below errors are mapped from RadioError which is returned from RIL. We will consolidate
// RadioErrors during the mapping if those RadioErrors mean no difference to the users.
+
+ /**
+ * Defines acceptable values of scan error code.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({ERROR_MODEM_ERROR, ERROR_INVALID_SCAN, ERROR_MODEM_UNAVAILABLE, ERROR_UNSUPPORTED,
+ ERROR_RADIO_INTERFACE_ERROR, ERROR_INVALID_SCANID, ERROR_INTERRUPTED})
+ public @interface ScanErrorCode {}
+
+ /**
+ * The RIL has successfully performed the network scan.
+ */
public static final int SUCCESS = 0; // RadioError:NONE
+
+ /**
+ * The scan has failed due to some modem errors.
+ */
public static final int ERROR_MODEM_ERROR = 1; // RadioError:RADIO_NOT_AVAILABLE
// RadioError:NO_MEMORY
// RadioError:INTERNAL_ERR
// RadioError:MODEM_ERR
// RadioError:OPERATION_NOT_ALLOWED
+
+ /**
+ * The parameters of the scan is invalid.
+ */
public static final int ERROR_INVALID_SCAN = 2; // RadioError:INVALID_ARGUMENTS
- public static final int ERROR_MODEM_BUSY = 3; // RadioError:DEVICE_IN_USE
+
+ /**
+ * The modem can not perform the scan because it is doing something else.
+ */
+ public static final int ERROR_MODEM_UNAVAILABLE = 3; // RadioError:DEVICE_IN_USE
+
+ /**
+ * The modem does not support the request scan.
+ */
public static final int ERROR_UNSUPPORTED = 4; // RadioError:REQUEST_NOT_SUPPORTED
+
// Below errors are generated at the Telephony.
- public static final int ERROR_RIL_ERROR = 10000; // Nothing or only exception is
- // returned from RIL.
- public static final int ERROR_INVALID_SCANID = 10001; // The scanId is invalid. The user is
- // either trying to stop a scan which
- // does not exist or started by others.
- public static final int ERROR_INTERRUPTED = 10002; // Scan was interrupted by another scan
- // with higher priority.
+
+ /**
+ * The RIL returns nothing or exceptions.
+ */
+ public static final int ERROR_RADIO_INTERFACE_ERROR = 10000;
+
+ /**
+ * The scan ID is invalid. The user is either trying to stop a scan which does not exist
+ * or started by others.
+ */
+ public static final int ERROR_INVALID_SCANID = 10001;
+
+ /**
+ * The scan has been interrupted by another scan with higher priority.
+ */
+ public static final int ERROR_INTERRUPTED = 10002;
+
private final int mScanId;
private final int mSubId;
/**
* Stops the network scan
*
- * This is the callback method to stop an ongoing scan. When user requests a new scan,
- * a NetworkScan object will be returned, and the user can stop the scan by calling this
- * method.
+ * Use this method to stop an ongoing scan. When user requests a new scan, a {@link NetworkScan}
+ * object will be returned, and the user can stop the scan by calling this method.
*/
public void stop() throws RemoteException {
try {
diff --git a/telephony/java/android/telephony/NetworkScanRequest.java b/telephony/java/android/telephony/NetworkScanRequest.java
index 9674c93..ea503c3 100644
--- a/telephony/java/android/telephony/NetworkScanRequest.java
+++ b/telephony/java/android/telephony/NetworkScanRequest.java
@@ -16,11 +16,14 @@
package android.telephony;
+import android.annotation.IntDef;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
import java.util.Arrays;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* Defines a request to peform a network scan.
@@ -28,7 +31,6 @@
* This class defines whether the network scan will be performed only once or periodically until
* cancelled, when the scan is performed periodically, the time interval is not controlled by the
* user but defined by the modem vendor.
- * @hide
*/
public final class NetworkScanRequest implements Parcelable {
@@ -54,6 +56,14 @@
/** @hide */
public static final int MAX_INCREMENTAL_PERIODICITY_SEC = 10;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ SCAN_TYPE_ONE_SHOT,
+ SCAN_TYPE_PERIODIC,
+ })
+ public @interface ScanType {}
+
/** Performs the scan only once */
public static final int SCAN_TYPE_ONE_SHOT = 0;
/**
@@ -65,21 +75,21 @@
public static final int SCAN_TYPE_PERIODIC = 1;
/** Defines the type of the scan. */
- public int scanType;
+ private int mScanType;
/**
* Search periodicity (in seconds).
* Expected range for the input is [5s - 300s]
- * This value must be less than or equal to maxSearchTime
+ * This value must be less than or equal to mMaxSearchTime
*/
- public int searchPeriodicity;
+ private int mSearchPeriodicity;
/**
* Maximum duration of the periodic search (in seconds).
* Expected range for the input is [60s - 3600s]
* If the search lasts this long, it will be terminated.
*/
- public int maxSearchTime;
+ private int mMaxSearchTime;
/**
* Indicates whether the modem should report incremental
@@ -87,18 +97,18 @@
* FALSE – Incremental results are not reported.
* TRUE (default) – Incremental results are reported
*/
- public boolean incrementalResults;
+ private boolean mIncrementalResults;
/**
* Indicates the periodicity with which the modem should
* report incremental results to the client (in seconds).
* Expected range for the input is [1s - 10s]
- * This value must be less than or equal to maxSearchTime
+ * This value must be less than or equal to mMaxSearchTime
*/
- public int incrementalResultsPeriodicity;
+ private int mIncrementalResultsPeriodicity;
/** Describes the radio access technologies with bands or channels that need to be scanned. */
- public RadioAccessSpecifier[] specifiers;
+ private RadioAccessSpecifier[] mSpecifiers;
/**
* Describes the List of PLMN ids (MCC-MNC)
@@ -107,20 +117,24 @@
* If list not sent, search to be completed till end and all PLMNs found to be reported.
* Max size of array is MAX_MCC_MNC_LIST_SIZE
*/
- public ArrayList<String> mccMncs;
+ private ArrayList<String> mMccMncs;
/**
- * Creates a new NetworkScanRequest with scanType and network specifiers
+ * Creates a new NetworkScanRequest with mScanType and network mSpecifiers
*
- * @param scanType The type of the scan
+ * @param scanType The type of the scan, can be either one shot or periodic
* @param specifiers the radio network with bands / channels to be scanned
- * @param searchPeriodicity Search periodicity (in seconds)
- * @param maxSearchTime Maximum duration of the periodic search (in seconds)
+ * @param searchPeriodicity The modem will restart the scan every searchPeriodicity seconds if
+ * no network has been found, until it reaches the maxSearchTime. Only
+ * valid when scan type is periodic scan.
+ * @param maxSearchTime Maximum duration of the search (in seconds)
* @param incrementalResults Indicates whether the modem should report incremental
* results of the network scan to the client
* @param incrementalResultsPeriodicity Indicates the periodicity with which the modem should
- * report incremental results to the client (in seconds)
- * @param mccMncs Describes the List of PLMN ids (MCC-MNC)
+ * report incremental results to the client (in seconds),
+ * only valid when incrementalResults is true
+ * @param mccMncs Describes the list of PLMN ids (MCC-MNC), once any network in the list has
+ * been found, the scan will be terminated by the modem.
*/
public NetworkScanRequest(int scanType, RadioAccessSpecifier[] specifiers,
int searchPeriodicity,
@@ -128,19 +142,63 @@
boolean incrementalResults,
int incrementalResultsPeriodicity,
ArrayList<String> mccMncs) {
- this.scanType = scanType;
- this.specifiers = specifiers;
- this.searchPeriodicity = searchPeriodicity;
- this.maxSearchTime = maxSearchTime;
- this.incrementalResults = incrementalResults;
- this.incrementalResultsPeriodicity = incrementalResultsPeriodicity;
- if (mccMncs != null) {
- this.mccMncs = mccMncs;
+ this.mScanType = scanType;
+ this.mSpecifiers = specifiers.clone();
+ this.mSearchPeriodicity = searchPeriodicity;
+ this.mMaxSearchTime = maxSearchTime;
+ this.mIncrementalResults = incrementalResults;
+ this.mIncrementalResultsPeriodicity = incrementalResultsPeriodicity;
+ if (mMccMncs != null) {
+ this.mMccMncs = (ArrayList<String>) mccMncs.clone();
} else {
- this.mccMncs = new ArrayList<>();
+ this.mMccMncs = new ArrayList<>();
}
}
+ /** Returns the type of the scan. */
+ @ScanType
+ public int getScanType() {
+ return mScanType;
+ }
+
+ /** Returns the search periodicity in seconds. */
+ public int getSearchPeriodicity() {
+ return mSearchPeriodicity;
+ }
+
+ /** Returns maximum duration of the periodic search in seconds. */
+ public int getMaxSearchTime() {
+ return mMaxSearchTime;
+ }
+
+ /**
+ * Returns whether incremental result is enabled.
+ * FALSE – Incremental results is not enabled.
+ * TRUE – Incremental results is reported.
+ */
+ public boolean getIncrementalResults() {
+ return mIncrementalResults;
+ }
+
+ /** Returns the periodicity in seconds of incremental results. */
+ public int getIncrementalResultsPeriodicity() {
+ return mIncrementalResultsPeriodicity;
+ }
+
+ /** Returns the radio access technologies with bands or channels that need to be scanned. */
+ public RadioAccessSpecifier[] getSpecifiers() {
+ return mSpecifiers.clone();
+ }
+
+ /**
+ * Returns the List of PLMN ids (MCC-MNC) for early termination of scan.
+ * If any PLMN of this list is found, search should end at that point and
+ * results with all PLMN found till that point should be sent as response.
+ */
+ public ArrayList<String> getPlmns() {
+ return (ArrayList<String>) mMccMncs.clone();
+ }
+
@Override
public int describeContents() {
return 0;
@@ -148,26 +206,26 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(scanType);
- dest.writeParcelableArray(specifiers, flags);
- dest.writeInt(searchPeriodicity);
- dest.writeInt(maxSearchTime);
- dest.writeBoolean(incrementalResults);
- dest.writeInt(incrementalResultsPeriodicity);
- dest.writeStringList(mccMncs);
+ dest.writeInt(mScanType);
+ dest.writeParcelableArray(mSpecifiers, flags);
+ dest.writeInt(mSearchPeriodicity);
+ dest.writeInt(mMaxSearchTime);
+ dest.writeBoolean(mIncrementalResults);
+ dest.writeInt(mIncrementalResultsPeriodicity);
+ dest.writeStringList(mMccMncs);
}
private NetworkScanRequest(Parcel in) {
- scanType = in.readInt();
- specifiers = (RadioAccessSpecifier[]) in.readParcelableArray(
+ mScanType = in.readInt();
+ mSpecifiers = (RadioAccessSpecifier[]) in.readParcelableArray(
Object.class.getClassLoader(),
RadioAccessSpecifier.class);
- searchPeriodicity = in.readInt();
- maxSearchTime = in.readInt();
- incrementalResults = in.readBoolean();
- incrementalResultsPeriodicity = in.readInt();
- mccMncs = new ArrayList<>();
- in.readStringList(mccMncs);
+ mSearchPeriodicity = in.readInt();
+ mMaxSearchTime = in.readInt();
+ mIncrementalResults = in.readBoolean();
+ mIncrementalResultsPeriodicity = in.readInt();
+ mMccMncs = new ArrayList<>();
+ in.readStringList(mMccMncs);
}
@Override
@@ -184,25 +242,25 @@
return false;
}
- return (scanType == nsr.scanType
- && Arrays.equals(specifiers, nsr.specifiers)
- && searchPeriodicity == nsr.searchPeriodicity
- && maxSearchTime == nsr.maxSearchTime
- && incrementalResults == nsr.incrementalResults
- && incrementalResultsPeriodicity == nsr.incrementalResultsPeriodicity
- && (((mccMncs != null)
- && mccMncs.equals(nsr.mccMncs))));
+ return (mScanType == nsr.mScanType
+ && Arrays.equals(mSpecifiers, nsr.mSpecifiers)
+ && mSearchPeriodicity == nsr.mSearchPeriodicity
+ && mMaxSearchTime == nsr.mMaxSearchTime
+ && mIncrementalResults == nsr.mIncrementalResults
+ && mIncrementalResultsPeriodicity == nsr.mIncrementalResultsPeriodicity
+ && (((mMccMncs != null)
+ && mMccMncs.equals(nsr.mMccMncs))));
}
@Override
public int hashCode () {
- return ((scanType * 31)
- + (Arrays.hashCode(specifiers)) * 37
- + (searchPeriodicity * 41)
- + (maxSearchTime * 43)
- + ((incrementalResults == true? 1 : 0) * 47)
- + (incrementalResultsPeriodicity * 53)
- + (mccMncs.hashCode() * 59));
+ return ((mScanType * 31)
+ + (Arrays.hashCode(mSpecifiers)) * 37
+ + (mSearchPeriodicity * 41)
+ + (mMaxSearchTime * 43)
+ + ((mIncrementalResults == true? 1 : 0) * 47)
+ + (mIncrementalResultsPeriodicity * 53)
+ + (mMccMncs.hashCode() * 59));
}
public static final Creator<NetworkScanRequest> CREATOR =
diff --git a/telephony/java/android/telephony/RadioAccessSpecifier.java b/telephony/java/android/telephony/RadioAccessSpecifier.java
index 33ce8b4..5412c61 100644
--- a/telephony/java/android/telephony/RadioAccessSpecifier.java
+++ b/telephony/java/android/telephony/RadioAccessSpecifier.java
@@ -25,34 +25,40 @@
* Describes a particular radio access network to be scanned.
*
* The scan can be performed on either bands or channels for a specific radio access network type.
- * @hide
*/
public final class RadioAccessSpecifier implements Parcelable {
/**
* The radio access network that needs to be scanned
*
+ * This parameter must be provided or else the scan will be rejected.
+ *
* See {@link RadioNetworkConstants.RadioAccessNetworks} for details.
*/
- public int radioAccessNetwork;
+ private int mRadioAccessNetwork;
/**
* The frequency bands that need to be scanned
*
- * bands must be used together with radioAccessNetwork
+ * When no specific bands are specified (empty array or null), all the frequency bands
+ * supported by the modem will be scanned.
*
* See {@link RadioNetworkConstants} for details.
*/
- public int[] bands;
+ private int[] mBands;
/**
* The frequency channels that need to be scanned
*
- * channels must be used together with radioAccessNetwork
+ * When any specific channels are provided for scan, the corresponding frequency bands that
+ * contains those channels must also be provided, or else the channels will be ignored.
*
- * See {@link RadioNetworkConstants.RadioAccessNetworks} for details.
+ * When no specific channels are specified (empty array or null), all the frequency channels
+ * supported by the modem will be scanned.
+ *
+ * See {@link RadioNetworkConstants} for details.
*/
- public int[] channels;
+ private int[] mChannels;
/**
* Creates a new RadioAccessSpecifier with radio network, bands and channels
@@ -65,9 +71,34 @@
* @param channels the frequency bands to be scanned
*/
public RadioAccessSpecifier(int ran, int[] bands, int[] channels) {
- this.radioAccessNetwork = ran;
- this.bands = bands;
- this.channels = channels;
+ this.mRadioAccessNetwork = ran;
+ this.mBands = bands.clone();
+ this.mChannels = channels.clone();
+ }
+
+ /**
+ * Returns the radio access network that needs to be scanned.
+ *
+ * The returned value is define in {@link RadioNetworkConstants.RadioAccessNetworks};
+ */
+ public int getRadioAccessNetwork() {
+ return mRadioAccessNetwork;
+ }
+
+ /**
+ * Returns the frequency bands that need to be scanned.
+ *
+ * The returned value is defined in either of {@link RadioNetworkConstants.GeranBands},
+ * {@link RadioNetworkConstants.UtranBands} and {@link RadioNetworkConstants.EutranBands}, and
+ * it depends on the returned value of {@link #getRadioAccessNetwork()}.
+ */
+ public int[] getBands() {
+ return mBands.clone();
+ }
+
+ /** Returns the frequency channels that need to be scanned. */
+ public int[] getChannels() {
+ return mChannels.clone();
}
public static final Parcelable.Creator<RadioAccessSpecifier> CREATOR =
@@ -90,15 +121,15 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(radioAccessNetwork);
- dest.writeIntArray(bands);
- dest.writeIntArray(channels);
+ dest.writeInt(mRadioAccessNetwork);
+ dest.writeIntArray(mBands);
+ dest.writeIntArray(mChannels);
}
private RadioAccessSpecifier(Parcel in) {
- radioAccessNetwork = in.readInt();
- bands = in.createIntArray();
- channels = in.createIntArray();
+ mRadioAccessNetwork = in.readInt();
+ mBands = in.createIntArray();
+ mChannels = in.createIntArray();
}
@Override
@@ -115,15 +146,15 @@
return false;
}
- return (radioAccessNetwork == ras.radioAccessNetwork
- && Arrays.equals(bands, ras.bands)
- && Arrays.equals(channels, ras.channels));
+ return (mRadioAccessNetwork == ras.mRadioAccessNetwork
+ && Arrays.equals(mBands, ras.mBands)
+ && Arrays.equals(mChannels, ras.mChannels));
}
@Override
public int hashCode () {
- return ((radioAccessNetwork * 31)
- + (Arrays.hashCode(bands) * 37)
- + (Arrays.hashCode(channels)) * 39);
+ return ((mRadioAccessNetwork * 31)
+ + (Arrays.hashCode(mBands) * 37)
+ + (Arrays.hashCode(mChannels)) * 39);
}
}
diff --git a/telephony/java/android/telephony/RadioNetworkConstants.java b/telephony/java/android/telephony/RadioNetworkConstants.java
index 1a9072d..5f5dd82 100644
--- a/telephony/java/android/telephony/RadioNetworkConstants.java
+++ b/telephony/java/android/telephony/RadioNetworkConstants.java
@@ -18,7 +18,6 @@
/**
* Contains radio access network related constants.
- * @hide
*/
public final class RadioNetworkConstants {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 8996dfe..af5b190 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4965,15 +4965,14 @@
* Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
* Or the calling app has carrier privileges. @see #hasCarrierPrivileges
- *
- * @hide
- * TODO: Add an overload that takes no args.
*/
- public void setNetworkSelectionModeAutomatic(int subId) {
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void setNetworkSelectionModeAutomatic() {
try {
ITelephony telephony = getITelephony();
- if (telephony != null)
- telephony.setNetworkSelectionModeAutomatic(subId);
+ if (telephony != null) {
+ telephony.setNetworkSelectionModeAutomatic(getSubId());
+ }
} catch (RemoteException ex) {
Rlog.e(TAG, "setNetworkSelectionModeAutomatic RemoteException", ex);
} catch (NullPointerException ex) {
@@ -5021,9 +5020,9 @@
*
* @param request Contains all the RAT with bands/channels that need to be scanned.
* @param callback Returns network scan results or errors.
- * @return A NetworkScan obj which contains a callback which can stop the scan.
- * @hide
+ * @return A NetworkScan obj which contains a callback which can be used to stop the scan.
*/
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public NetworkScan requestNetworkScan(
NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) {
synchronized (this) {
@@ -5042,15 +5041,20 @@
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
* Or the calling app has carrier privileges. @see #hasCarrierPrivileges
*
- * @hide
- * TODO: Add an overload that takes no args.
+ * @param operatorNumeric the PLMN ID of the network to select.
+ * @param persistSelection whether the selection will persist until reboot. If true, only allows
+ * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
+ * normal network selection next time.
+ * @return true on success; false on any failure.
*/
- public boolean setNetworkSelectionModeManual(int subId, OperatorInfo operator,
- boolean persistSelection) {
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean setNetworkSelectionModeManual(String operatorNumeric, boolean persistSelection) {
try {
ITelephony telephony = getITelephony();
- if (telephony != null)
- return telephony.setNetworkSelectionModeManual(subId, operator, persistSelection);
+ if (telephony != null) {
+ return telephony.setNetworkSelectionModeManual(
+ getSubId(), operatorNumeric, persistSelection);
+ }
} catch (RemoteException ex) {
Rlog.e(TAG, "setNetworkSelectionModeManual RemoteException", ex);
} catch (NullPointerException ex) {
@@ -5075,8 +5079,9 @@
public boolean setPreferredNetworkType(int subId, int networkType) {
try {
ITelephony telephony = getITelephony();
- if (telephony != null)
+ if (telephony != null) {
return telephony.setPreferredNetworkType(subId, networkType);
+ }
} catch (RemoteException ex) {
Rlog.e(TAG, "setPreferredNetworkType RemoteException", ex);
} catch (NullPointerException ex) {
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 7bcdcdc..c182e34 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -38,7 +38,6 @@
/**
* Manages the radio access network scan requests and callbacks.
- * @hide
*/
public final class TelephonyScanManager {
@@ -55,7 +54,8 @@
public static final int CALLBACK_SCAN_COMPLETE = 3;
/**
- * The caller of {@link #requestNetworkScan(NetworkScanRequest, NetworkScanCallback)} should
+ * The caller of
+ * {@link TelephonyManager#requestNetworkScan(NetworkScanRequest, NetworkScanCallback)} should
* implement and provide this callback so that the scan results or errors can be returned.
*/
public static abstract class NetworkScanCallback {
@@ -75,8 +75,10 @@
*
* This callback will be called whenever there is any error about the scan, and the scan
* will be terminated. onComplete() will NOT be called.
+ *
+ * @param error Error code when the scan is failed, as defined in {@link NetworkScan}.
*/
- public void onError(int error) {}
+ public void onError(@NetworkScan.ScanErrorCode int error) {}
}
private static class NetworkScanInfo {
diff --git a/telephony/java/android/telephony/mbms/ServiceInfo.java b/telephony/java/android/telephony/mbms/ServiceInfo.java
index 8529f52..f78e7a6 100644
--- a/telephony/java/android/telephony/mbms/ServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/ServiceInfo.java
@@ -51,8 +51,8 @@
/** @hide */
public ServiceInfo(Map<Locale, String> newNames, String newClassName, List<Locale> newLocales,
String newServiceId, Date start, Date end) {
- if (newNames == null || newNames.isEmpty() || TextUtils.isEmpty(newClassName)
- || newLocales == null || newLocales.isEmpty() || TextUtils.isEmpty(newServiceId)
+ if (newNames == null || newClassName == null
+ || newLocales == null || newServiceId == null
|| start == null || end == null) {
throw new IllegalArgumentException("Bad ServiceInfo construction");
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 8ea53a5..416146f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -846,13 +846,13 @@
* Ask the radio to connect to the input network and change selection mode to manual.
*
* @param subId the id of the subscription.
- * @param operatorInfo the operator to attach to.
- * @param persistSelection should the selection persist till reboot or its
- * turned off? Will also result in notification being not shown to
- * the user if the signal is lost.
+ * @param operatorNumeric the PLMN of the operator to attach to.
+ * @param persistSelection Whether the selection will persist until reboot. If true, only allows
+ * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
+ * normal network selection next time.
* @return true if the request suceeded.
*/
- boolean setNetworkSelectionModeManual(int subId, in OperatorInfo operator,
+ boolean setNetworkSelectionModeManual(int subId, in String operatorNumeric,
boolean persistSelection);
/**