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);
 
     /**