Merge changes from topic 'android-lowpan-next'

* changes:
  lowpan: AIDL refactor to no longer use property design pattern
  LowpanException: Refactor exception handling
  lowpan: Introduce new unit tests for data classes
  lowpan: Make various data classes Parcelable
  lowpan: Remove libandroid_net_lowpan from platform/frameworks/base
  LowpanEnergyScanResult: Remove `public` designation from setChannel/setMaxRssi
diff --git a/Android.mk b/Android.mk
index 2d8739b..cca87ca 100644
--- a/Android.mk
+++ b/Android.mk
@@ -712,6 +712,7 @@
 	frameworks/base/core/java/android/print/PrinterInfo.aidl \
 	frameworks/base/core/java/android/print/PrintJobId.aidl \
 	frameworks/base/core/java/android/printservice/recommendation/RecommendationInfo.aidl \
+	frameworks/base/core/java/android/hardware/radio/ProgramSelector.aidl \
 	frameworks/base/core/java/android/hardware/radio/RadioManager.aidl \
 	frameworks/base/core/java/android/hardware/radio/RadioMetadata.aidl \
 	frameworks/base/core/java/android/hardware/usb/UsbDevice.aidl \
diff --git a/api/current.txt b/api/current.txt
index 21e66d3..37cb728 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -31900,6 +31900,7 @@
     method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
     method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
     method public java.util.UUID getUuidForPath(java.io.File) throws java.io.IOException;
+    method public boolean isAllocationSupported(java.io.FileDescriptor);
     method public boolean isCacheBehaviorGroup(java.io.File) throws java.io.IOException;
     method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException;
     method public boolean isEncrypted(java.io.File);
@@ -38683,14 +38684,18 @@
 
   public final class StructStat {
     ctor public StructStat(long, long, int, long, int, int, long, long, long, long, long, long, long);
+    ctor public StructStat(long, long, int, long, int, int, long, long, android.system.StructTimespec, android.system.StructTimespec, android.system.StructTimespec, long, long);
+    field public final android.system.StructTimespec st_atim;
     field public final long st_atime;
     field public final long st_blksize;
     field public final long st_blocks;
+    field public final android.system.StructTimespec st_ctim;
     field public final long st_ctime;
     field public final long st_dev;
     field public final int st_gid;
     field public final long st_ino;
     field public final int st_mode;
+    field public final android.system.StructTimespec st_mtim;
     field public final long st_mtime;
     field public final long st_nlink;
     field public final long st_rdev;
@@ -38713,6 +38718,13 @@
     field public final long f_namemax;
   }
 
+  public final class StructTimespec implements java.lang.Comparable {
+    ctor public StructTimespec(long, long);
+    method public int compareTo(android.system.StructTimespec);
+    field public final long tv_nsec;
+    field public final long tv_sec;
+  }
+
   public final class StructUtsname {
     ctor public StructUtsname(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
     field public final java.lang.String machine;
@@ -49068,7 +49080,7 @@
     method public void setNetworkAvailable(boolean);
     method public deprecated void setPictureListener(android.webkit.WebView.PictureListener);
     method public void setRendererPriorityPolicy(int, boolean);
-    method public static void setSafeBrowsingWhiteList(java.util.List<java.lang.String>);
+    method public static void setSafeBrowsingWhitelist(java.util.List<java.lang.String>, android.webkit.ValueCallback<java.lang.Boolean>);
     method public deprecated void setVerticalScrollbarOverlay(boolean);
     method public void setWebChromeClient(android.webkit.WebChromeClient);
     method public static void setWebContentsDebuggingEnabled(boolean);
diff --git a/api/removed.txt b/api/removed.txt
index 3968fd3..ca34142 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -267,6 +267,14 @@
 
 }
 
+package android.net.wifi {
+
+  public class WifiManager {
+    method public deprecated boolean setWifiApEnabled(android.net.wifi.WifiConfiguration, boolean);
+  }
+
+}
+
 package android.os {
 
   public class BatteryManager {
@@ -302,18 +310,8 @@
 package android.os.storage {
 
   public class StorageManager {
-    method public deprecated void allocateBytes(java.io.File, long, int) throws java.io.IOException;
-    method public deprecated long getAllocatableBytes(java.io.File, int) throws java.io.IOException;
-    method public deprecated long getCacheQuotaBytes(java.io.File) throws java.io.IOException;
-    method public deprecated long getCacheQuotaBytes() throws java.io.IOException;
-    method public deprecated long getCacheSizeBytes(java.io.File) throws java.io.IOException;
-    method public deprecated long getCacheSizeBytes() throws java.io.IOException;
-    method public deprecated long getExternalCacheQuotaBytes() throws java.io.IOException;
-    method public deprecated long getExternalCacheSizeBytes() throws java.io.IOException;
     method public android.os.storage.StorageVolume getPrimaryVolume();
     method public android.os.storage.StorageVolume[] getVolumeList();
-    method public deprecated boolean isCacheBehaviorAtomic(java.io.File) throws java.io.IOException;
-    method public deprecated void setCacheBehaviorAtomic(java.io.File, boolean) throws java.io.IOException;
   }
 
 }
diff --git a/api/system-current.txt b/api/system-current.txt
index 268d8ec..bb12f7c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -146,6 +146,7 @@
     field public static final java.lang.String MANAGE_CA_CERTIFICATES = "android.permission.MANAGE_CA_CERTIFICATES";
     field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
     field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
+    field public static final java.lang.String MANAGE_FALLBACK_SUBSCRIPTION_PLANS = "android.permission.MANAGE_FALLBACK_SUBSCRIPTION_PLANS";
     field public static final java.lang.String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
     field public static final java.lang.String MANAGE_USB = "android.permission.MANAGE_USB";
     field public static final java.lang.String MANAGE_USERS = "android.permission.MANAGE_USERS";
@@ -157,7 +158,7 @@
     field public static final java.lang.String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS";
     field public static final java.lang.String MODIFY_CELL_BROADCASTS = "android.permission.MODIFY_CELL_BROADCASTS";
     field public static final java.lang.String MODIFY_DAY_NIGHT_MODE = "android.permission.MODIFY_DAY_NIGHT_MODE";
-    field public static final java.lang.String MODIFY_NETWORK_ACCOUNTING = "android.permission.MODIFY_NETWORK_ACCOUNTING";
+    field public static final deprecated java.lang.String MODIFY_NETWORK_ACCOUNTING = "android.permission.MODIFY_NETWORK_ACCOUNTING";
     field public static final java.lang.String MODIFY_PARENTAL_CONTROLS = "android.permission.MODIFY_PARENTAL_CONTROLS";
     field public static final java.lang.String MODIFY_PHONE_STATE = "android.permission.MODIFY_PHONE_STATE";
     field public static final java.lang.String MOUNT_FORMAT_FILESYSTEMS = "android.permission.MOUNT_FORMAT_FILESYSTEMS";
@@ -190,6 +191,8 @@
     field public static final java.lang.String READ_OEM_UNLOCK_STATE = "android.permission.READ_OEM_UNLOCK_STATE";
     field public static final java.lang.String READ_PHONE_NUMBERS = "android.permission.READ_PHONE_NUMBERS";
     field public static final java.lang.String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE";
+    field public static final java.lang.String READ_PRINT_SERVICES = "android.permission.READ_PRINT_SERVICES";
+    field public static final java.lang.String READ_PRINT_SERVICE_RECOMMENDATIONS = "android.permission.READ_PRINT_SERVICE_RECOMMENDATIONS";
     field public static final java.lang.String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
     field public static final java.lang.String READ_SEARCH_INDEXABLES = "android.permission.READ_SEARCH_INDEXABLES";
     field public static final java.lang.String READ_SMS = "android.permission.READ_SMS";
@@ -17212,6 +17215,63 @@
 
 package android.hardware.radio {
 
+  public final class ProgramSelector implements android.os.Parcelable {
+    ctor public ProgramSelector(int, android.hardware.radio.ProgramSelector.Identifier, android.hardware.radio.ProgramSelector.Identifier[], long[]);
+    method public static android.hardware.radio.ProgramSelector createAmFmSelector(int, int);
+    method public static android.hardware.radio.ProgramSelector createAmFmSelector(int, int, int);
+    method public int describeContents();
+    method public android.hardware.radio.ProgramSelector.Identifier[] getAllIds(int);
+    method public long getFirstId(int);
+    method public android.hardware.radio.ProgramSelector.Identifier getPrimaryId();
+    method public int getProgramType();
+    method public android.hardware.radio.ProgramSelector.Identifier[] getSecondaryIds();
+    method public long[] getVendorIds();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.hardware.radio.ProgramSelector> CREATOR;
+    field public static final int IDENTIFIER_TYPE_AMFM_FREQUENCY = 1; // 0x1
+    field public static final int IDENTIFIER_TYPE_DAB_ENSEMBLE = 6; // 0x6
+    field public static final int IDENTIFIER_TYPE_DAB_FREQUENCY = 8; // 0x8
+    field public static final int IDENTIFIER_TYPE_DAB_SCID = 7; // 0x7
+    field public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5; // 0x5
+    field public static final int IDENTIFIER_TYPE_DRMO_FREQUENCY = 10; // 0xa
+    field public static final int IDENTIFIER_TYPE_DRMO_SERVICE_ID = 9; // 0x9
+    field public static final int IDENTIFIER_TYPE_HD_STATION_ID_EXT = 3; // 0x3
+    field public static final int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4; // 0x4
+    field public static final int IDENTIFIER_TYPE_RDS_PI = 2; // 0x2
+    field public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 12; // 0xc
+    field public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 11; // 0xb
+    field public static final int IDENTIFIER_TYPE_VENDOR1_PRIMARY = 13; // 0xd
+    field public static final int IDENTIFIER_TYPE_VENDOR2_PRIMARY = 14; // 0xe
+    field public static final int IDENTIFIER_TYPE_VENDOR3_PRIMARY = 15; // 0xf
+    field public static final int IDENTIFIER_TYPE_VENDOR4_PRIMARY = 16; // 0x10
+    field public static final int PROGRAM_TYPE_AM = 1; // 0x1
+    field public static final int PROGRAM_TYPE_AM_HD = 3; // 0x3
+    field public static final int PROGRAM_TYPE_DAB = 5; // 0x5
+    field public static final int PROGRAM_TYPE_DRMO = 6; // 0x6
+    field public static final int PROGRAM_TYPE_FM = 2; // 0x2
+    field public static final int PROGRAM_TYPE_FM_HD = 4; // 0x4
+    field public static final int PROGRAM_TYPE_SXM = 7; // 0x7
+    field public static final int PROGRAM_TYPE_VENDOR1 = 8; // 0x8
+    field public static final int PROGRAM_TYPE_VENDOR2 = 9; // 0x9
+    field public static final int PROGRAM_TYPE_VENDOR3 = 10; // 0xa
+    field public static final int PROGRAM_TYPE_VENDOR4 = 11; // 0xb
+  }
+
+  public static final class ProgramSelector.Identifier implements android.os.Parcelable {
+    ctor public ProgramSelector.Identifier(int, long);
+    method public int describeContents();
+    method public int getType();
+    method public long getValue();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.hardware.radio.ProgramSelector.Identifier> CREATOR;
+  }
+
+  public static abstract class ProgramSelector.IdentifierType implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ProgramSelector.ProgramType implements java.lang.annotation.Annotation {
+  }
+
   public class RadioManager {
     method public int listModules(java.util.List<android.hardware.radio.RadioManager.ModuleProperties>);
     method public android.hardware.radio.RadioTuner openTuner(int, android.hardware.radio.RadioManager.BandConfig, boolean, android.hardware.radio.RadioTuner.Callback, android.os.Handler);
@@ -17219,6 +17279,7 @@
     field public static final int BAND_AM_HD = 3; // 0x3
     field public static final int BAND_FM = 1; // 0x1
     field public static final int BAND_FM_HD = 2; // 0x2
+    field public static final int BAND_INVALID = -1; // 0xffffffff
     field public static final int CLASS_AM_FM = 0; // 0x0
     field public static final int CLASS_DT = 2; // 0x2
     field public static final int CLASS_SAT = 1; // 0x1
@@ -17254,6 +17315,9 @@
     field public static final android.os.Parcelable.Creator<android.hardware.radio.RadioManager.AmBandDescriptor> CREATOR;
   }
 
+  public static abstract class RadioManager.Band implements java.lang.annotation.Annotation {
+  }
+
   public static class RadioManager.BandConfig implements android.os.Parcelable {
     method public int describeContents();
     method public int getLowerLimit();
@@ -17322,21 +17386,26 @@
     method public java.lang.String getVersion();
     method public boolean isBackgroundScanningSupported();
     method public boolean isCaptureSupported();
+    method public boolean isProgramIdentifierSupported(int);
+    method public boolean isProgramTypeSupported(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.radio.RadioManager.ModuleProperties> CREATOR;
   }
 
   public static class RadioManager.ProgramInfo implements android.os.Parcelable {
     method public int describeContents();
-    method public int getChannel();
+    method public deprecated int getChannel();
     method public android.hardware.radio.RadioMetadata getMetadata();
+    method public android.hardware.radio.ProgramSelector getSelector();
     method public int getSignalStrength();
-    method public int getSubChannel();
+    method public deprecated int getSubChannel();
     method public java.lang.String getVendorExension();
     method public boolean isDigital();
     method public boolean isLive();
     method public boolean isMuted();
     method public boolean isStereo();
+    method public boolean isTrafficAnnouncementActive();
+    method public boolean isTrafficProgram();
     method public boolean isTuned();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.radio.RadioManager.ProgramInfo> CREATOR;
@@ -17389,6 +17458,7 @@
   public abstract class RadioTuner {
     ctor public RadioTuner();
     method public abstract int cancel();
+    method public abstract void cancelAnnouncement();
     method public abstract void close();
     method public abstract int getConfiguration(android.hardware.radio.RadioManager.BandConfig[]);
     method public abstract boolean getMute();
@@ -17403,7 +17473,8 @@
     method public abstract int setMute(boolean);
     method public abstract boolean startBackgroundScan();
     method public abstract int step(int, boolean);
-    method public abstract int tune(int, int);
+    method public abstract deprecated int tune(int, int);
+    method public abstract void tune(android.hardware.radio.ProgramSelector);
     field public static final int DIRECTION_DOWN = 1; // 0x1
     field public static final int DIRECTION_UP = 0; // 0x0
     field public static final int ERROR_BACKGROUND_SCAN_FAILED = 6; // 0x6
@@ -17424,7 +17495,7 @@
     method public void onControlChanged(boolean);
     method public void onEmergencyAnnouncement(boolean);
     method public void onError(int);
-    method public void onMetadataChanged(android.hardware.radio.RadioMetadata);
+    method public deprecated void onMetadataChanged(android.hardware.radio.RadioMetadata);
     method public void onProgramInfoChanged(android.hardware.radio.RadioManager.ProgramInfo);
     method public void onProgramListChanged();
     method public void onTrafficAnnouncement(boolean);
@@ -29183,7 +29254,7 @@
     method public void setTdlsEnabled(java.net.InetAddress, boolean);
     method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean);
     method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
-    method public boolean setWifiApEnabled(android.net.wifi.WifiConfiguration, boolean);
+    method public deprecated boolean setWifiApEnabled(android.net.wifi.WifiConfiguration, boolean);
     method public boolean setWifiEnabled(boolean);
     method public void startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, android.os.Handler);
     method public deprecated boolean startLocationRestrictedScan(android.os.WorkSource);
@@ -34779,6 +34850,7 @@
     method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
     method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
     method public java.util.UUID getUuidForPath(java.io.File) throws java.io.IOException;
+    method public boolean isAllocationSupported(java.io.FileDescriptor);
     method public boolean isCacheBehaviorGroup(java.io.File) throws java.io.IOException;
     method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException;
     method public boolean isEncrypted(java.io.File);
@@ -38562,6 +38634,22 @@
     field public static final java.lang.String TYPE = "type";
   }
 
+  public final class TimeZoneRulesDataContract {
+    field public static final java.lang.String AUTHORITY = "com.android.timezone";
+  }
+
+  public static final class TimeZoneRulesDataContract.Operation {
+    field public static final java.lang.String COLUMN_DISTRO_MAJOR_VERSION = "distro_major_version";
+    field public static final java.lang.String COLUMN_DISTRO_MINOR_VERSION = "distro_minor_version";
+    field public static final java.lang.String COLUMN_REVISION = "revision";
+    field public static final java.lang.String COLUMN_RULES_VERSION = "rules_version";
+    field public static final java.lang.String COLUMN_TYPE = "type";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String TYPE_INSTALL = "INSTALL";
+    field public static final java.lang.String TYPE_NO_OP = "NOOP";
+    field public static final java.lang.String TYPE_UNINSTALL = "UNINSTALL";
+  }
+
   public class UserDictionary {
     ctor public UserDictionary();
     field public static final java.lang.String AUTHORITY = "user_dictionary";
@@ -41923,14 +42011,18 @@
 
   public final class StructStat {
     ctor public StructStat(long, long, int, long, int, int, long, long, long, long, long, long, long);
+    ctor public StructStat(long, long, int, long, int, int, long, long, android.system.StructTimespec, android.system.StructTimespec, android.system.StructTimespec, long, long);
+    field public final android.system.StructTimespec st_atim;
     field public final long st_atime;
     field public final long st_blksize;
     field public final long st_blocks;
+    field public final android.system.StructTimespec st_ctim;
     field public final long st_ctime;
     field public final long st_dev;
     field public final int st_gid;
     field public final long st_ino;
     field public final int st_mode;
+    field public final android.system.StructTimespec st_mtim;
     field public final long st_mtime;
     field public final long st_nlink;
     field public final long st_rdev;
@@ -41953,6 +42045,13 @@
     field public final long f_namemax;
   }
 
+  public final class StructTimespec implements java.lang.Comparable {
+    ctor public StructTimespec(long, long);
+    method public int compareTo(android.system.StructTimespec);
+    field public final long tv_nsec;
+    field public final long tv_sec;
+  }
+
   public final class StructUtsname {
     ctor public StructUtsname(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
     field public final java.lang.String machine;
@@ -52735,7 +52834,7 @@
     method public void setNetworkAvailable(boolean);
     method public deprecated void setPictureListener(android.webkit.WebView.PictureListener);
     method public void setRendererPriorityPolicy(int, boolean);
-    method public static void setSafeBrowsingWhiteList(java.util.List<java.lang.String>);
+    method public static void setSafeBrowsingWhitelist(java.util.List<java.lang.String>, android.webkit.ValueCallback<java.lang.Boolean>);
     method public deprecated void setVerticalScrollbarOverlay(boolean);
     method public void setWebChromeClient(android.webkit.WebChromeClient);
     method public static void setWebContentsDebuggingEnabled(boolean);
@@ -52937,7 +53036,7 @@
     method public abstract java.lang.String getDefaultUserAgent(android.content.Context);
     method public abstract void initSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean>);
     method public abstract android.net.Uri[] parseFileChooserResult(int, android.content.Intent);
-    method public abstract void setSafeBrowsingWhiteList(java.util.List<java.lang.String>);
+    method public abstract void setSafeBrowsingWhitelist(java.util.List<java.lang.String>, android.webkit.ValueCallback<java.lang.Boolean>);
     method public abstract void setWebContentsDebuggingEnabled(boolean);
     method public abstract void shutdownSafeBrowsing();
   }
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 48f62b1..dfadae4 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -295,18 +295,8 @@
 package android.os.storage {
 
   public class StorageManager {
-    method public deprecated void allocateBytes(java.io.File, long, int) throws java.io.IOException;
-    method public deprecated long getAllocatableBytes(java.io.File, int) throws java.io.IOException;
-    method public deprecated long getCacheQuotaBytes(java.io.File) throws java.io.IOException;
-    method public deprecated long getCacheQuotaBytes() throws java.io.IOException;
-    method public deprecated long getCacheSizeBytes(java.io.File) throws java.io.IOException;
-    method public deprecated long getCacheSizeBytes() throws java.io.IOException;
-    method public deprecated long getExternalCacheQuotaBytes() throws java.io.IOException;
-    method public deprecated long getExternalCacheSizeBytes() throws java.io.IOException;
     method public android.os.storage.StorageVolume getPrimaryVolume();
     method public android.os.storage.StorageVolume[] getVolumeList();
-    method public deprecated boolean isCacheBehaviorAtomic(java.io.File) throws java.io.IOException;
-    method public deprecated void setCacheBehaviorAtomic(java.io.File, boolean) throws java.io.IOException;
   }
 
 }
diff --git a/api/test-current.txt b/api/test-current.txt
index c2a1231..b9f9be5d2 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -32041,6 +32041,7 @@
     method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
     method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
     method public java.util.UUID getUuidForPath(java.io.File) throws java.io.IOException;
+    method public boolean isAllocationSupported(java.io.FileDescriptor);
     method public boolean isCacheBehaviorGroup(java.io.File) throws java.io.IOException;
     method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException;
     method public boolean isEncrypted(java.io.File);
@@ -32732,9 +32733,7 @@
 
   public final class PrintManager {
     method public java.util.List<android.print.PrintJob> getPrintJobs();
-    method public java.util.List<android.printservice.PrintServiceInfo> getPrintServices(int);
     method public android.print.PrintJob print(java.lang.String, android.print.PrintDocumentAdapter, android.print.PrintAttributes);
-    field public static final int ALL_SERVICES = 3; // 0x3
   }
 
   public final class PrinterCapabilitiesInfo implements android.os.Parcelable {
@@ -32864,13 +32863,6 @@
     field public static final java.lang.String SERVICE_META_DATA = "android.printservice";
   }
 
-  public final class PrintServiceInfo implements android.os.Parcelable {
-    method public int describeContents();
-    method public android.content.ComponentName getComponentName();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.printservice.PrintServiceInfo> CREATOR;
-  }
-
   public abstract class PrinterDiscoverySession {
     ctor public PrinterDiscoverySession();
     method public final void addPrinters(java.util.List<android.print.PrinterInfo>);
@@ -38902,14 +38894,18 @@
 
   public final class StructStat {
     ctor public StructStat(long, long, int, long, int, int, long, long, long, long, long, long, long);
+    ctor public StructStat(long, long, int, long, int, int, long, long, android.system.StructTimespec, android.system.StructTimespec, android.system.StructTimespec, long, long);
+    field public final android.system.StructTimespec st_atim;
     field public final long st_atime;
     field public final long st_blksize;
     field public final long st_blocks;
+    field public final android.system.StructTimespec st_ctim;
     field public final long st_ctime;
     field public final long st_dev;
     field public final int st_gid;
     field public final long st_ino;
     field public final int st_mode;
+    field public final android.system.StructTimespec st_mtim;
     field public final long st_mtime;
     field public final long st_nlink;
     field public final long st_rdev;
@@ -38932,6 +38928,13 @@
     field public final long f_namemax;
   }
 
+  public final class StructTimespec implements java.lang.Comparable {
+    ctor public StructTimespec(long, long);
+    method public int compareTo(android.system.StructTimespec);
+    field public final long tv_nsec;
+    field public final long tv_sec;
+  }
+
   public final class StructUtsname {
     ctor public StructUtsname(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
     field public final java.lang.String machine;
@@ -46190,6 +46193,7 @@
     method public void setFocusable(int);
     method public void setFocusableInTouchMode(boolean);
     method public void setFocusedByDefault(boolean);
+    method public final void setFocusedInCluster();
     method public void setForeground(android.graphics.drawable.Drawable);
     method public void setForegroundGravity(int);
     method public void setForegroundTintList(android.content.res.ColorStateList);
@@ -49498,7 +49502,7 @@
     method public void setNetworkAvailable(boolean);
     method public deprecated void setPictureListener(android.webkit.WebView.PictureListener);
     method public void setRendererPriorityPolicy(int, boolean);
-    method public static void setSafeBrowsingWhiteList(java.util.List<java.lang.String>);
+    method public static void setSafeBrowsingWhitelist(java.util.List<java.lang.String>, android.webkit.ValueCallback<java.lang.Boolean>);
     method public deprecated void setVerticalScrollbarOverlay(boolean);
     method public void setWebChromeClient(android.webkit.WebChromeClient);
     method public static void setWebContentsDebuggingEnabled(boolean);
diff --git a/api/test-removed.txt b/api/test-removed.txt
index 3968fd3..ca34142 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -267,6 +267,14 @@
 
 }
 
+package android.net.wifi {
+
+  public class WifiManager {
+    method public deprecated boolean setWifiApEnabled(android.net.wifi.WifiConfiguration, boolean);
+  }
+
+}
+
 package android.os {
 
   public class BatteryManager {
@@ -302,18 +310,8 @@
 package android.os.storage {
 
   public class StorageManager {
-    method public deprecated void allocateBytes(java.io.File, long, int) throws java.io.IOException;
-    method public deprecated long getAllocatableBytes(java.io.File, int) throws java.io.IOException;
-    method public deprecated long getCacheQuotaBytes(java.io.File) throws java.io.IOException;
-    method public deprecated long getCacheQuotaBytes() throws java.io.IOException;
-    method public deprecated long getCacheSizeBytes(java.io.File) throws java.io.IOException;
-    method public deprecated long getCacheSizeBytes() throws java.io.IOException;
-    method public deprecated long getExternalCacheQuotaBytes() throws java.io.IOException;
-    method public deprecated long getExternalCacheSizeBytes() throws java.io.IOException;
     method public android.os.storage.StorageVolume getPrimaryVolume();
     method public android.os.storage.StorageVolume[] getVolumeList();
-    method public deprecated boolean isCacheBehaviorAtomic(java.io.File) throws java.io.IOException;
-    method public deprecated void setCacheBehaviorAtomic(java.io.File, boolean) throws java.io.IOException;
   }
 
 }
diff --git a/cmds/bootanimation/BootAnimationUtil.cpp b/cmds/bootanimation/BootAnimationUtil.cpp
index 377d6ce..7718daf 100644
--- a/cmds/bootanimation/BootAnimationUtil.cpp
+++ b/cmds/bootanimation/BootAnimationUtil.cpp
@@ -29,7 +29,7 @@
     char value[PROPERTY_VALUE_MAX];
     property_get("debug.sf.nobootanimation", value, "0");
     if (atoi(value) > 0) {
-      return false;
+        return true;
     }
 
     property_get("ro.boot.quiescent", value, "0");
diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp
index daac588..8501982 100644
--- a/cmds/bootanimation/bootanimation_main.cpp
+++ b/cmds/bootanimation/bootanimation_main.cpp
@@ -157,8 +157,10 @@
 
         // create the boot animation object
         sp<BootAnimation> boot = new BootAnimation(new AudioAnimationCallbacks());
+        ALOGV("Boot animation set up. Joining pool.");
 
         IPCThreadState::self()->joinThreadPool();
     }
+    ALOGV("Boot animation exit");
     return 0;
 }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 9c9d655..c48be77 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -60,12 +60,10 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.IStorageManager;
-import android.os.storage.StorageManager;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
@@ -621,7 +619,8 @@
     @Override
     public File getExternalFilesDir(String type) {
         // Operates on primary external storage
-        return getExternalFilesDirs(type)[0];
+        final File[] dirs = getExternalFilesDirs(type);
+        return (dirs != null && dirs.length > 0) ? dirs[0] : null;
     }
 
     @Override
@@ -638,7 +637,8 @@
     @Override
     public File getObbDir() {
         // Operates on primary external storage
-        return getObbDirs()[0];
+        final File[] dirs = getObbDirs();
+        return (dirs != null && dirs.length > 0) ? dirs[0] : null;
     }
 
     @Override
@@ -672,7 +672,8 @@
     @Override
     public File getExternalCacheDir() {
         // Operates on primary external storage
-        return getExternalCacheDirs()[0];
+        final File[] dirs = getExternalCacheDirs();
+        return (dirs != null && dirs.length > 0) ? dirs[0] : null;
     }
 
     @Override
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 5baaeb3..b444f17 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -18,9 +18,9 @@
 
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.SdkConstant.SdkConstantType;
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
@@ -36,8 +36,8 @@
 import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
 import android.provider.Downloads;
-import android.provider.Settings;
 import android.provider.MediaStore.Images;
+import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.text.TextUtils;
 import android.util.Pair;
@@ -393,7 +393,6 @@
         private int mFlags = 0;
         private boolean mIsVisibleInDownloadsUi = true;
         private boolean mScannable = false;
-        private boolean mUseSystemCache = false;
         /** if a file is designated as a MediaScanner scannable file, the following value is
          * stored in the database column {@link Downloads.Impl#COLUMN_MEDIA_SCANNED}.
          */
@@ -474,24 +473,6 @@
         }
 
         /**
-         * Set the local destination for the downloaded file to the system cache dir (/cache).
-         * This is only available to System apps with the permission
-         * {@link android.Manifest.permission#ACCESS_CACHE_FILESYSTEM}.
-         * <p>
-         * The downloaded file is not scanned by MediaScanner.
-         * But it can be made scannable by calling {@link #allowScanningByMediaScanner()}.
-         * <p>
-         * Files downloaded to /cache may be deleted by the system at any time to reclaim space.
-         *
-         * @return this object
-         * @hide
-         */
-        public Request setDestinationToSystemCache() {
-            mUseSystemCache = true;
-            return this;
-        }
-
-        /**
          * Set the local destination for the downloaded file to a path within
          * the application's external files directory (as returned by
          * {@link Context#getExternalFilesDir(String)}.
@@ -772,13 +753,13 @@
             values.put(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE, packageName);
 
             if (mDestinationUri != null) {
-                values.put(Downloads.Impl.COLUMN_DESTINATION, Downloads.Impl.DESTINATION_FILE_URI);
-                values.put(Downloads.Impl.COLUMN_FILE_NAME_HINT, mDestinationUri.toString());
+                values.put(Downloads.Impl.COLUMN_DESTINATION,
+                        Downloads.Impl.DESTINATION_FILE_URI);
+                values.put(Downloads.Impl.COLUMN_FILE_NAME_HINT,
+                        mDestinationUri.toString());
             } else {
                 values.put(Downloads.Impl.COLUMN_DESTINATION,
-                           (this.mUseSystemCache) ?
-                                   Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION :
-                                   Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE);
+                        Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE);
             }
             // is the file supposed to be media-scannable?
             values.put(Downloads.Impl.COLUMN_MEDIA_SCANNED, (mScannable) ? SCANNABLE_VALUE_YES :
diff --git a/core/java/android/app/timezone/Callback.java b/core/java/android/app/timezone/Callback.java
index b51e5ba..aea8038 100644
--- a/core/java/android/app/timezone/Callback.java
+++ b/core/java/android/app/timezone/Callback.java
@@ -27,7 +27,6 @@
  *
  * @hide
  */
-// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728
 public abstract class Callback {
 
     @Retention(RetentionPolicy.SOURCE)
diff --git a/core/java/android/app/timezone/DistroFormatVersion.java b/core/java/android/app/timezone/DistroFormatVersion.java
index e879e8f..be732e4 100644
--- a/core/java/android/app/timezone/DistroFormatVersion.java
+++ b/core/java/android/app/timezone/DistroFormatVersion.java
@@ -35,7 +35,6 @@
  *
  * @hide
  */
-// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728
 public final class DistroFormatVersion implements Parcelable {
 
     private final int mMajorVersion;
diff --git a/core/java/android/app/timezone/DistroRulesVersion.java b/core/java/android/app/timezone/DistroRulesVersion.java
index 1eb9f45..a680594 100644
--- a/core/java/android/app/timezone/DistroRulesVersion.java
+++ b/core/java/android/app/timezone/DistroRulesVersion.java
@@ -36,7 +36,6 @@
  *
  * @hide
  */
-// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728
 public final class DistroRulesVersion implements Parcelable {
 
     private final String mRulesVersion;
diff --git a/core/java/android/app/timezone/RulesManager.java b/core/java/android/app/timezone/RulesManager.java
index 649d894..ad9b698 100644
--- a/core/java/android/app/timezone/RulesManager.java
+++ b/core/java/android/app/timezone/RulesManager.java
@@ -64,7 +64,6 @@
  * {@link Context#TIME_ZONE_RULES_MANAGER_SERVICE}.
  * @hide
  */
-// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728
 public final class RulesManager {
     private static final String TAG = "timezone.RulesManager";
     private static final boolean DEBUG = false;
diff --git a/core/java/android/app/timezone/RulesState.java b/core/java/android/app/timezone/RulesState.java
index 7d6ad21..ec247eb 100644
--- a/core/java/android/app/timezone/RulesState.java
+++ b/core/java/android/app/timezone/RulesState.java
@@ -60,7 +60,6 @@
  *
  * @hide
  */
-// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728
 public final class RulesState implements Parcelable {
 
     @Retention(RetentionPolicy.SOURCE)
diff --git a/core/java/android/app/timezone/RulesUpdaterContract.java b/core/java/android/app/timezone/RulesUpdaterContract.java
index 07b2f33..9c62f46 100644
--- a/core/java/android/app/timezone/RulesUpdaterContract.java
+++ b/core/java/android/app/timezone/RulesUpdaterContract.java
@@ -27,7 +27,6 @@
  *
  * @hide
  */
-// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728
 public final class RulesUpdaterContract {
 
     /**
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index e800e88..afb798c 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -513,7 +513,7 @@
      * configuration.
      */
     boolean performDexOpt(String packageName, boolean checkProfiles,
-            int compileReason, boolean force, boolean bootComplete);
+            int compileReason, boolean force, boolean bootComplete, boolean downgrade);
 
     /**
      * Ask the package manager to perform a dex-opt with the given compiler filter.
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index ed41e79..aa9562f 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -608,15 +608,15 @@
     }
 
     /**
-     * Get {@link ApplicationInfo} for a profile
+     * Returns {@link ApplicationInfo} about an application installed for a specific user profile.
      *
      * @param packageName The package name of the application
      * @param flags Additional option flags {@link PackageManager#getApplicationInfo}
      * @param user The UserHandle of the profile.
      *
-     * @return An {@link ApplicationInfo} containing information about the package or
-     *         null if the package isn't installed for the given user, or the target user
-     *         is not enabled.
+     * @return {@link ApplicationInfo} containing information about the package. Returns
+     *         {@code null} if the package isn't installed for the given profile, or the profile
+     *         isn't enabled.
      */
     public ApplicationInfo getApplicationInfo(@NonNull String packageName,
             @ApplicationInfoFlags int flags, @NonNull UserHandle user)
diff --git a/core/java/android/content/pm/PackageBackwardCompatibility.java b/core/java/android/content/pm/PackageBackwardCompatibility.java
new file mode 100644
index 0000000..4de160b
--- /dev/null
+++ b/core/java/android/content/pm/PackageBackwardCompatibility.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.Nullable;
+import android.content.pm.PackageParser.Package;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+import java.util.ArrayList;
+
+/**
+ * Modifies {@link Package} in order to maintain backwards compatibility.
+ *
+ * @hide
+ */
+@VisibleForTesting
+public class PackageBackwardCompatibility {
+
+    private static final String ANDROID_TEST_MOCK = "android.test.mock";
+
+    private static final String ANDROID_TEST_RUNNER = "android.test.runner";
+
+    /**
+     * Modify the shared libraries in the supplied {@link Package} to maintain backwards
+     * compatibility.
+     *
+     * @param pkg the {@link Package} to modify.
+     */
+    @VisibleForTesting
+    public static void modifySharedLibraries(Package pkg) {
+        ArrayList<String> usesLibraries = pkg.usesLibraries;
+        ArrayList<String> usesOptionalLibraries = pkg.usesOptionalLibraries;
+
+        usesLibraries = orgApacheHttpLegacy(usesLibraries);
+        usesOptionalLibraries = orgApacheHttpLegacy(usesOptionalLibraries);
+
+        // android.test.runner has a dependency on android.test.mock so if android.test.runner
+        // is present but android.test.mock is not then add android.test.mock.
+        boolean androidTestMockPresent = ArrayUtils.contains(usesLibraries, ANDROID_TEST_MOCK)
+                || ArrayUtils.contains(usesOptionalLibraries, ANDROID_TEST_MOCK);
+        if (ArrayUtils.contains(usesLibraries, ANDROID_TEST_RUNNER) && !androidTestMockPresent) {
+            usesLibraries.add(ANDROID_TEST_MOCK);
+        }
+        if (ArrayUtils.contains(usesOptionalLibraries, ANDROID_TEST_RUNNER)
+                && !androidTestMockPresent) {
+            usesOptionalLibraries.add(ANDROID_TEST_MOCK);
+        }
+
+        pkg.usesLibraries = usesLibraries;
+        pkg.usesOptionalLibraries = usesOptionalLibraries;
+    }
+
+    private static ArrayList<String> orgApacheHttpLegacy(@Nullable ArrayList<String> libraries) {
+        // "org.apache.http.legacy" is now a part of the boot classpath so it doesn't need
+        // to be an explicit dependency.
+        //
+        // A future change will remove this library from the boot classpath, at which point
+        // all apps that target SDK 21 and earlier will have it automatically added to their
+        // dependency lists.
+        return ArrayUtils.remove(libraries, "org.apache.http.legacy");
+    }
+}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 426c4f2..a37e89e 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3846,7 +3846,7 @@
         // every activity info has had a chance to set it from its attributes.
         setMaxAspectRatio(owner);
 
-        modifySharedLibrariesForBackwardCompatibility(owner);
+        PackageBackwardCompatibility.modifySharedLibraries(owner);
 
         if (hasDomainURLs(owner)) {
             owner.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS;
@@ -3857,18 +3857,6 @@
         return true;
     }
 
-    private static void modifySharedLibrariesForBackwardCompatibility(Package owner) {
-        // "org.apache.http.legacy" is now a part of the boot classpath so it doesn't need
-        // to be an explicit dependency.
-        //
-        // A future change will remove this library from the boot classpath, at which point
-        // all apps that target SDK 21 and earlier will have it automatically added to their
-        // dependency lists.
-        owner.usesLibraries = ArrayUtils.remove(owner.usesLibraries, "org.apache.http.legacy");
-        owner.usesOptionalLibraries = ArrayUtils.remove(owner.usesOptionalLibraries,
-                "org.apache.http.legacy");
-    }
-
     /**
      * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI
      */
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index d332b38..a8b8c4b 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -84,6 +84,8 @@
     /** Used only when TRACE_FOR_DETAILED_PRELOAD is true. */
     private static int sPreloadTracingNumLoadedDrawables;
     private long mPreloadTracingPreloadStartTime;
+    private long mPreloadTracingStartBitmapSize;
+    private long mPreloadTracingStartBitmapCount;
 
     private static final int ID_OTHER = 0x01000004;
 
@@ -1167,6 +1169,8 @@
 
             if (TRACE_FOR_DETAILED_PRELOAD) {
                 mPreloadTracingPreloadStartTime = SystemClock.uptimeMillis();
+                mPreloadTracingStartBitmapSize = Bitmap.sPreloadTracingTotalBitmapsSize;
+                mPreloadTracingStartBitmapCount = Bitmap.sPreloadTracingNumInstantiatedBitmaps;
                 Log.d(TAG_PRELOAD, "Preload starting");
             }
         }
@@ -1180,7 +1184,12 @@
         if (mPreloading) {
             if (TRACE_FOR_DETAILED_PRELOAD) {
                 final long time = SystemClock.uptimeMillis() - mPreloadTracingPreloadStartTime;
-                Log.d(TAG_PRELOAD, "Preload finished in " + time + " ms");
+                final long size =
+                        Bitmap.sPreloadTracingTotalBitmapsSize - mPreloadTracingStartBitmapSize;
+                final long count = Bitmap.sPreloadTracingNumInstantiatedBitmaps
+                        - mPreloadTracingStartBitmapCount;
+                Log.d(TAG_PRELOAD, "Preload finished, "
+                        + count + " bitmaps of " + size + " bytes in " + time + " ms");
             }
 
             mPreloading = false;
diff --git a/core/java/android/hardware/radio/ITuner.aidl b/core/java/android/hardware/radio/ITuner.aidl
index 69e135d..2dc6601 100644
--- a/core/java/android/hardware/radio/ITuner.aidl
+++ b/core/java/android/hardware/radio/ITuner.aidl
@@ -16,6 +16,7 @@
 
 package android.hardware.radio;
 
+import android.hardware.radio.ProgramSelector;
 import android.hardware.radio.RadioManager;
 
 /** {@hide} */
@@ -52,13 +53,15 @@
      * @throws IllegalArgumentException if invalid arguments are passed
      * @throws IllegalStateException if called out of sequence
      */
-    void tune(int channel, int subChannel);
+    void tune(in ProgramSelector selector);
 
     /**
      * @throws IllegalStateException if called out of sequence
      */
     void cancel();
 
+    void cancelAnnouncement();
+
     RadioManager.ProgramInfo getProgramInformation();
 
     /**
diff --git a/core/java/android/hardware/radio/ITunerCallback.aidl b/core/java/android/hardware/radio/ITunerCallback.aidl
index aed114e..c3bbaec 100644
--- a/core/java/android/hardware/radio/ITunerCallback.aidl
+++ b/core/java/android/hardware/radio/ITunerCallback.aidl
@@ -23,8 +23,7 @@
 oneway interface ITunerCallback {
     void onError(int status);
     void onConfigurationChanged(in RadioManager.BandConfig config);
-    void onProgramInfoChanged(in RadioManager.ProgramInfo info);
-    void onMetadataChanged(in RadioMetadata metadata);
+    void onProgramInfoChanged();
     void onTrafficAnnouncement(boolean active);
     void onEmergencyAnnouncement(boolean active);
     void onAntennaState(boolean connected);
diff --git a/core/java/android/hardware/radio/ProgramSelector.aidl b/core/java/android/hardware/radio/ProgramSelector.aidl
new file mode 100644
index 0000000..545269a
--- /dev/null
+++ b/core/java/android/hardware/radio/ProgramSelector.aidl
@@ -0,0 +1,20 @@
+/**
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio;
+
+/** @hide */
+parcelable ProgramSelector;
diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java
new file mode 100644
index 0000000..4638dd5
--- /dev/null
+++ b/core/java/android/hardware/radio/ProgramSelector.java
@@ -0,0 +1,506 @@
+/**
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Stream;
+
+/**
+ * A set of identifiers necessary to tune to a given station.
+ *
+ * This can hold various identifiers, like
+ * - AM/FM frequency
+ * - HD Radio subchannel
+ * - DAB channel info
+ *
+ * The primary ID uniquely identifies a station and can be used for equality
+ * check. The secondary IDs are supplementary and can speed up tuning process,
+ * but the primary ID is sufficient (ie. after a full band scan).
+ *
+ * Two selectors with different secondary IDs, but the same primary ID are
+ * considered equal. In particular, secondary IDs vector may get updated for
+ * an entry on the program list (ie. when a better frequency for a given
+ * station is found).
+ *
+ * The primaryId of a given programType MUST be of a specific type:
+ * - AM, FM: RDS_PI if the station broadcasts RDS, AMFM_FREQUENCY otherwise;
+ * - AM_HD, FM_HD: HD_STATION_ID_EXT;
+ * - DAB: DAB_SIDECC;
+ * - DRMO: DRMO_SERVICE_ID;
+ * - SXM: SXM_SERVICE_ID;
+ * - VENDOR: VENDOR_PRIMARY.
+ * @hide
+ */
+@SystemApi
+public final class ProgramSelector implements Parcelable {
+    /** Analogue AM radio (with or without RDS). */
+    public static final int PROGRAM_TYPE_AM = 1;
+    /** analogue FM radio (with or without RDS). */
+    public static final int PROGRAM_TYPE_FM = 2;
+    /** AM HD Radio. */
+    public static final int PROGRAM_TYPE_AM_HD = 3;
+    /** FM HD Radio. */
+    public static final int PROGRAM_TYPE_FM_HD = 4;
+    /** Digital audio broadcasting. */
+    public static final int PROGRAM_TYPE_DAB = 5;
+    /** Digital Radio Mondiale. */
+    public static final int PROGRAM_TYPE_DRMO = 6;
+    /** SiriusXM Satellite Radio. */
+    public static final int PROGRAM_TYPE_SXM = 7;
+    /** Vendor-specific, not synced across devices. */
+    public static final int PROGRAM_TYPE_VENDOR1 = 8;
+    public static final int PROGRAM_TYPE_VENDOR2 = 9;
+    public static final int PROGRAM_TYPE_VENDOR3 = 10;
+    public static final int PROGRAM_TYPE_VENDOR4 = 11;
+    @IntDef(prefix = { "PROGRAM_TYPE_" }, value = {
+        PROGRAM_TYPE_AM,
+        PROGRAM_TYPE_FM,
+        PROGRAM_TYPE_AM_HD,
+        PROGRAM_TYPE_FM_HD,
+        PROGRAM_TYPE_DAB,
+        PROGRAM_TYPE_DRMO,
+        PROGRAM_TYPE_SXM,
+        PROGRAM_TYPE_VENDOR1,
+        PROGRAM_TYPE_VENDOR2,
+        PROGRAM_TYPE_VENDOR3,
+        PROGRAM_TYPE_VENDOR4,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProgramType {}
+
+    /** kHz */
+    public static final int IDENTIFIER_TYPE_AMFM_FREQUENCY = 1;
+    /** 16bit */
+    public static final int IDENTIFIER_TYPE_RDS_PI = 2;
+    /**
+     * 64bit compound primary identifier for HD Radio.
+     *
+     * Consists of (from the LSB):
+     * - 32bit: Station ID number;
+     * - 4bit: HD_SUBCHANNEL;
+     * - 18bit: AMFM_FREQUENCY.
+     * The remaining bits should be set to zeros when writing on the chip side
+     * and ignored when read.
+     */
+    public static final int IDENTIFIER_TYPE_HD_STATION_ID_EXT = 3;
+    /**
+     * HD Radio subchannel - a value of range 0-7.
+     *
+     * The subchannel index is 0-based (where 0 is MPS and 1..7 are SPS),
+     * as opposed to HD Radio standard (where it's 1-based).
+     */
+    public static final int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4;
+    /**
+     * 24bit compound primary identifier for DAB.
+     *
+     * Consists of (from the LSB):
+     * - 16bit: SId;
+     * - 8bit: ECC code.
+     * The remaining bits should be set to zeros when writing on the chip side
+     * and ignored when read.
+     */
+    public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5;
+    /** 16bit */
+    public static final int IDENTIFIER_TYPE_DAB_ENSEMBLE = 6;
+    /** 12bit */
+    public static final int IDENTIFIER_TYPE_DAB_SCID = 7;
+    /** kHz */
+    public static final int IDENTIFIER_TYPE_DAB_FREQUENCY = 8;
+    /** 24bit */
+    public static final int IDENTIFIER_TYPE_DRMO_SERVICE_ID = 9;
+    /** kHz */
+    public static final int IDENTIFIER_TYPE_DRMO_FREQUENCY = 10;
+    /** 32bit */
+    public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 11;
+    /** 0-999 range */
+    public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 12;
+    /**
+     * Primary identifier for vendor-specific radio technology.
+     * The value format is determined by a vendor.
+     *
+     * It must not be used in any other programType than VENDORx.
+     */
+    public static final int IDENTIFIER_TYPE_VENDOR1_PRIMARY = 13;
+    public static final int IDENTIFIER_TYPE_VENDOR2_PRIMARY = 14;
+    public static final int IDENTIFIER_TYPE_VENDOR3_PRIMARY = 15;
+    public static final int IDENTIFIER_TYPE_VENDOR4_PRIMARY = 16;
+    @IntDef(prefix = { "IDENTIFIER_TYPE_" }, value = {
+        IDENTIFIER_TYPE_AMFM_FREQUENCY,
+        IDENTIFIER_TYPE_RDS_PI,
+        IDENTIFIER_TYPE_HD_STATION_ID_EXT,
+        IDENTIFIER_TYPE_HD_SUBCHANNEL,
+        IDENTIFIER_TYPE_DAB_SIDECC,
+        IDENTIFIER_TYPE_DAB_ENSEMBLE,
+        IDENTIFIER_TYPE_DAB_SCID,
+        IDENTIFIER_TYPE_DAB_FREQUENCY,
+        IDENTIFIER_TYPE_DRMO_SERVICE_ID,
+        IDENTIFIER_TYPE_DRMO_FREQUENCY,
+        IDENTIFIER_TYPE_SXM_SERVICE_ID,
+        IDENTIFIER_TYPE_SXM_CHANNEL,
+        IDENTIFIER_TYPE_VENDOR1_PRIMARY,
+        IDENTIFIER_TYPE_VENDOR2_PRIMARY,
+        IDENTIFIER_TYPE_VENDOR3_PRIMARY,
+        IDENTIFIER_TYPE_VENDOR4_PRIMARY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface IdentifierType {}
+
+    private final @ProgramType int mProgramType;
+    private final @NonNull Identifier mPrimaryId;
+    private final @NonNull Identifier[] mSecondaryIds;
+    private final @NonNull long[] mVendorIds;
+
+    /**
+     * Constructor for ProgramSelector.
+     *
+     * It's not desired to modify selector objects, so all its fields are initialized at creation.
+     *
+     * Identifier lists must not contain any nulls, but can itself be null to be interpreted
+     * as empty list at object creation.
+     *
+     * @param programType type of a radio technology.
+     * @param primaryId primary program identifier.
+     * @param secondaryIds list of secondary program identifiers.
+     * @param vendorIds list of vendor-specific program identifiers.
+     */
+    public ProgramSelector(@ProgramType int programType, @NonNull Identifier primaryId,
+            @Nullable Identifier[] secondaryIds, @Nullable long[] vendorIds) {
+        if (secondaryIds == null) secondaryIds = new Identifier[0];
+        if (vendorIds == null) vendorIds = new long[0];
+        if (Stream.of(secondaryIds).anyMatch(id -> id == null)) {
+            throw new IllegalArgumentException("secondaryIds list must not contain nulls");
+        }
+        mProgramType = programType;
+        mPrimaryId = Objects.requireNonNull(primaryId);
+        mSecondaryIds = secondaryIds;
+        mVendorIds = vendorIds;
+    }
+
+    /**
+     * Type of a radio technology.
+     *
+     * @returns program type.
+     */
+    public @ProgramType int getProgramType() {
+        return mProgramType;
+    }
+
+    /**
+     * Primary program identifier uniquely identifies a station and is used to
+     * determine equality between two ProgramSelectors.
+     *
+     * @returns primary identifier.
+     */
+    public @NonNull Identifier getPrimaryId() {
+        return mPrimaryId;
+    }
+
+    /**
+     * Secondary program identifier is not required for tuning, but may make it
+     * faster or more reliable.
+     *
+     * @returns secondary identifier list, must not be modified.
+     */
+    public @NonNull Identifier[] getSecondaryIds() {
+        return mSecondaryIds;
+    }
+
+    /**
+     * Looks up an identifier of a given type (either primary or secondary).
+     *
+     * If there are multiple identifiers if a given type, then first in order (where primary id is
+     * before any secondary) is selected.
+     *
+     * @param type type of identifier.
+     * @return identifier value, if found.
+     * @throws IllegalArgumentException, if not found.
+     */
+    public long getFirstId(@IdentifierType int type) {
+        if (mPrimaryId.getType() == type) return mPrimaryId.getValue();
+        for (Identifier id : mSecondaryIds) {
+            if (id.getType() == type) return id.getValue();
+        }
+        throw new IllegalArgumentException("Identifier " + type + " not found");
+    }
+
+    /**
+     * Looks up all identifier of a given type (either primary or secondary).
+     *
+     * Some identifiers may be provided multiple times, for example
+     * IDENTIFIER_TYPE_AMFM_FREQUENCY for FM Alternate Frequencies.
+     *
+     * @param type type of identifier.
+     * @return a list of identifiers, generated on each call. May be modified.
+     */
+    public @NonNull Identifier[] getAllIds(@IdentifierType int type) {
+        List<Identifier> out = new ArrayList<>();
+
+        if (mPrimaryId.getType() == type) out.add(mPrimaryId);
+        for (Identifier id : mSecondaryIds) {
+            if (id.getType() == type) out.add(id);
+        }
+
+        return out.toArray(new Identifier[out.size()]);
+    }
+
+    /**
+     * Vendor identifiers are passed as-is to the HAL implementation,
+     * preserving elements order.
+     *
+     * @return a array of vendor identifiers, must not be modified.
+     */
+    public @NonNull long[] getVendorIds() {
+        return mVendorIds;
+    }
+
+    /**
+     * Builds new ProgramSelector for AM/FM frequency.
+     *
+     * @param band the band.
+     * @param frequencyKhz the frequency in kHz.
+     * @return new ProgramSelector object representing given frequency.
+     * @throws IllegalArgumentException if provided frequency is out of bounds.
+     */
+    public static @NonNull ProgramSelector createAmFmSelector(
+            @RadioManager.Band int band, int frequencyKhz) {
+        return createAmFmSelector(band, frequencyKhz, 0);
+    }
+
+    /**
+     * Checks, if a given AM/FM frequency is roughly valid and in correct unit.
+     *
+     * It does not check the range precisely. In particular, it may be way off for certain regions.
+     * The main purpose is to avoid passing inproper units, ie. MHz instead of kHz.
+     *
+     * @param isAm true, if AM, false if FM.
+     * @param frequencyKhz the frequency in kHz.
+     * @return true, if the frequency is rougly valid.
+     */
+    private static boolean isValidAmFmFrequency(boolean isAm, int frequencyKhz) {
+        if (isAm) {
+            return frequencyKhz > 150 && frequencyKhz < 30000;
+        } else {
+            return frequencyKhz > 60000 && frequencyKhz < 110000;
+        }
+    }
+
+    /**
+     * Builds new ProgramSelector for AM/FM frequency.
+     *
+     * This method variant supports HD Radio subchannels, but it's undesirable to
+     * select them manually. Instead, the value should be retrieved from program list.
+     *
+     * @param band the band.
+     * @param frequencyKhz the frequency in kHz.
+     * @param subChannel 1-based HD Radio subchannel.
+     * @return new ProgramSelector object representing given frequency.
+     * @throws IllegalArgumentException if provided frequency is out of bounds,
+     *         or tried setting a subchannel for analog AM/FM.
+     */
+    public static @NonNull ProgramSelector createAmFmSelector(
+            @RadioManager.Band int band, int frequencyKhz, int subChannel) {
+        boolean isAm = (band == RadioManager.BAND_AM || band == RadioManager.BAND_AM_HD);
+        boolean isDigital = (band == RadioManager.BAND_AM_HD || band == RadioManager.BAND_FM_HD);
+        if (!isAm && !isDigital && band != RadioManager.BAND_FM) {
+            throw new IllegalArgumentException("Unknown band: " + band);
+        }
+        if (subChannel < 0 || subChannel > 8) {
+            throw new IllegalArgumentException("Invalid subchannel: " + subChannel);
+        }
+        if (subChannel > 0 && !isDigital) {
+            throw new IllegalArgumentException("Subchannels are not supported for non-HD radio");
+        }
+        if (!isValidAmFmFrequency(isAm, frequencyKhz)) {
+            throw new IllegalArgumentException("Provided value is not a valid AM/FM frequency");
+        }
+
+        // We can't use AM_HD or FM_HD, because we don't know HD station ID.
+        @ProgramType int programType = isAm ? PROGRAM_TYPE_AM : PROGRAM_TYPE_FM;
+        Identifier primary = new Identifier(IDENTIFIER_TYPE_AMFM_FREQUENCY, frequencyKhz);
+
+        Identifier[] secondary = null;
+        if (subChannel > 0) {
+            /* Stating sub channel for non-HD AM/FM does not give any guarantees,
+             * but we can't do much more without HD station ID.
+             *
+             * The legacy APIs had 1-based subChannels, while ProgramSelector is 0-based.
+             */
+            secondary = new Identifier[]{
+                    new Identifier(IDENTIFIER_TYPE_HD_SUBCHANNEL, subChannel - 1)};
+        }
+
+        return new ProgramSelector(programType, primary, secondary, null);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("ProgramSelector(type=").append(mProgramType)
+                .append(", primary=").append(mPrimaryId);
+        if (mSecondaryIds.length > 0) sb.append(", secondary=").append(mSecondaryIds);
+        if (mVendorIds.length > 0) sb.append(", vendor=").append(mVendorIds);
+        sb.append(")");
+        return sb.toString();
+    }
+
+    @Override
+    public int hashCode() {
+        // secondaryIds and vendorIds are ignored for equality/hashing
+        return Objects.hash(mProgramType, mPrimaryId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (!(obj instanceof ProgramSelector)) return false;
+        ProgramSelector other = (ProgramSelector) obj;
+        // secondaryIds and vendorIds are ignored for equality/hashing
+        return other.getProgramType() == mProgramType && mPrimaryId.equals(other.getPrimaryId());
+    }
+
+    private ProgramSelector(Parcel in) {
+        mProgramType = in.readInt();
+        mPrimaryId = in.readTypedObject(Identifier.CREATOR);
+        mSecondaryIds = in.createTypedArray(Identifier.CREATOR);
+        if (Stream.of(mSecondaryIds).anyMatch(id -> id == null)) {
+            throw new IllegalArgumentException("secondaryIds list must not contain nulls");
+        }
+        mVendorIds = in.createLongArray();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mProgramType);
+        dest.writeTypedObject(mPrimaryId, 0);
+        dest.writeTypedArray(mSecondaryIds, 0);
+        dest.writeLongArray(mVendorIds);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<ProgramSelector> CREATOR =
+            new Parcelable.Creator<ProgramSelector>() {
+        public ProgramSelector createFromParcel(Parcel in) {
+            return new ProgramSelector(in);
+        }
+
+        public ProgramSelector[] newArray(int size) {
+            return new ProgramSelector[size];
+        }
+    };
+
+    /**
+     * A single program identifier component, eg. frequency or channel ID.
+     *
+     * The long value field holds the value in format described in comments for
+     * IdentifierType constants.
+     */
+    public static final class Identifier implements Parcelable {
+        private final @IdentifierType int mType;
+        private final long mValue;
+
+        public Identifier(@IdentifierType int type, long value) {
+            mType = type;
+            mValue = value;
+        }
+
+        /**
+         * Type of an identifier.
+         *
+         * @return type of an identifier.
+         */
+        public @IdentifierType int getType() {
+            return mType;
+        }
+
+        /**
+         * Value of an identifier.
+         *
+         * Its meaning depends on identifier type, ie. for IDENTIFIER_TYPE_AMFM_FREQUENCY type,
+         * the value is a frequency in kHz.
+         *
+         * The range of a value depends on its type; it does not always require the whole long
+         * range. Casting to necessary type (ie. int) without range checking is correct in front-end
+         * code - any range violations are either errors in the framework or in the
+         * HAL implementation. For example, IDENTIFIER_TYPE_AMFM_FREQUENCY always fits in int,
+         * as Integer.MAX_VALUE would mean 2.1THz.
+         *
+         * @return value of an identifier.
+         */
+        public long getValue() {
+            return mValue;
+        }
+
+        @Override
+        public String toString() {
+            return "Identifier(" + mType + ", " + mValue + ")";
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mType, mValue);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) return true;
+            if (!(obj instanceof Identifier)) return false;
+            Identifier other = (Identifier) obj;
+            return other.getType() == mType && other.getValue() == mValue;
+        }
+
+        private Identifier(Parcel in) {
+            mType = in.readInt();
+            mValue = in.readLong();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(mType);
+            dest.writeLong(mValue);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final Parcelable.Creator<Identifier> CREATOR =
+                new Parcelable.Creator<Identifier>() {
+            public Identifier createFromParcel(Parcel in) {
+                return new Identifier(in);
+            }
+
+            public Identifier[] newArray(int size) {
+                return new Identifier[size];
+            }
+        };
+    }
+}
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index d25e752..6a59be0 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -16,6 +16,7 @@
 
 package android.hardware.radio;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
@@ -29,8 +30,12 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * The RadioManager class allows to control a broadcast radio tuner present on the device.
@@ -70,7 +75,7 @@
     /** Radio module class supporting Digital terrestrial radio */
     public static final int CLASS_DT = 2;
 
-    // keep in sync with radio_band_t in /system/core/incluse/system/radio.h
+    public static final int BAND_INVALID = -1;
     /** AM radio band (LW/MW/SW).
      * @see BandDescriptor */
     public static final int BAND_AM = 0;
@@ -83,6 +88,15 @@
     /** AM HD radio or DRM band.
      * @see BandDescriptor */
     public static final int BAND_AM_HD = 3;
+    @IntDef(prefix = { "BAND_" }, value = {
+        BAND_INVALID,
+        BAND_AM,
+        BAND_FM,
+        BAND_AM_HD,
+        BAND_FM_HD,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Band {}
 
     // keep in sync with radio_region_t in /system/core/incluse/system/radio.h
     /** Africa, Europe.
@@ -120,11 +134,15 @@
         private final boolean mIsCaptureSupported;
         private final BandDescriptor[] mBands;
         private final boolean mIsBgScanSupported;
+        private final Set<Integer> mSupportedProgramTypes;
+        private final Set<Integer> mSupportedIdentifierTypes;
         private final String mVendorExension;
 
         ModuleProperties(int id, String serviceName, int classId, String implementor,
                 String product, String version, String serial, int numTuners, int numAudioSources,
                 boolean isCaptureSupported, BandDescriptor[] bands, boolean isBgScanSupported,
+                @ProgramSelector.ProgramType int[] supportedProgramTypes,
+                @ProgramSelector.IdentifierType int[] supportedIdentifierTypes,
                 String vendorExension) {
             mId = id;
             mServiceName = TextUtils.isEmpty(serviceName) ? "default" : serviceName;
@@ -138,9 +156,18 @@
             mIsCaptureSupported = isCaptureSupported;
             mBands = bands;
             mIsBgScanSupported = isBgScanSupported;
+            mSupportedProgramTypes = arrayToSet(supportedProgramTypes);
+            mSupportedIdentifierTypes = arrayToSet(supportedIdentifierTypes);
             mVendorExension = vendorExension;
         }
 
+        private static Set<Integer> arrayToSet(int[] arr) {
+            return Arrays.stream(arr).boxed().collect(Collectors.toSet());
+        }
+
+        private static int[] setToArray(Set<Integer> set) {
+            return set.stream().mapToInt(Integer::intValue).toArray();
+        }
 
         /** Unique module identifier provided by the native service.
          * For use with {@link #openTuner(int, BandConfig, boolean, Callback, Handler)}.
@@ -233,6 +260,31 @@
         }
 
         /**
+         * Checks, if a given program type is supported by this tuner.
+         *
+         * If a program type is supported by radio module, it means it can tune
+         * to ProgramSelector of a given type.
+         *
+         * @return {@code true} if a given program type is supported.
+         */
+        public boolean isProgramTypeSupported(@ProgramSelector.ProgramType int type) {
+            return mSupportedProgramTypes.contains(type);
+        }
+
+        /**
+         * Checks, if a given program identifier is supported by this tuner.
+         *
+         * If an identifier is supported by radio module, it means it can use it for
+         * tuning to ProgramSelector with either primary or secondary Identifier of
+         * a given type.
+         *
+         * @return {@code true} if a given program type is supported.
+         */
+        public boolean isProgramIdentifierSupported(@ProgramSelector.IdentifierType int type) {
+            return mSupportedIdentifierTypes.contains(type);
+        }
+
+        /**
          * Opaque vendor-specific string, passed from HAL without changes.
          * Format of this string can vary across vendors.
          *
@@ -271,6 +323,8 @@
                 mBands[i] = (BandDescriptor) tmp[i];
             }
             mIsBgScanSupported = in.readInt() == 1;
+            mSupportedProgramTypes = arrayToSet(in.createIntArray());
+            mSupportedIdentifierTypes = arrayToSet(in.createIntArray());
             mVendorExension = in.readString();
         }
 
@@ -299,6 +353,8 @@
             dest.writeInt(mIsCaptureSupported ? 1 : 0);
             dest.writeParcelableArray(mBands, flags);
             dest.writeInt(mIsBgScanSupported ? 1 : 0);
+            dest.writeIntArray(setToArray(mSupportedProgramTypes));
+            dest.writeIntArray(setToArray(mSupportedIdentifierTypes));
             dest.writeString(mVendorExension);
         }
 
@@ -1255,11 +1311,12 @@
     public static class ProgramInfo implements Parcelable {
 
         // sourced from hardware/interfaces/broadcastradio/1.1/types.hal
-        private final static int FLAG_LIVE = 1 << 0;
-        private final static int FLAG_MUTED = 1 << 1;
+        private static final int FLAG_LIVE = 1 << 0;
+        private static final int FLAG_MUTED = 1 << 1;
+        private static final int FLAG_TRAFFIC_PROGRAM = 1 << 2;
+        private static final int FLAG_TRAFFIC_ANNOUNCEMENT = 1 << 3;
 
-        private final int mChannel;
-        private final int mSubChannel;
+        @NonNull private final ProgramSelector mSelector;
         private final boolean mTuned;
         private final boolean mStereo;
         private final boolean mDigital;
@@ -1268,11 +1325,10 @@
         private final RadioMetadata mMetadata;
         private final String mVendorExension;
 
-        ProgramInfo(int channel, int subChannel, boolean tuned, boolean stereo,
+        ProgramInfo(@NonNull ProgramSelector selector, boolean tuned, boolean stereo,
                 boolean digital, int signalStrength, RadioMetadata metadata, int flags,
                 String vendorExension) {
-            mChannel = channel;
-            mSubChannel = subChannel;
+            mSelector = selector;
             mTuned = tuned;
             mStereo = stereo;
             mDigital = digital;
@@ -1282,19 +1338,45 @@
             mVendorExension = vendorExension;
         }
 
+        /**
+         * Program selector, necessary for tuning to a program.
+         *
+         * @return the program selector.
+         */
+        public @NonNull ProgramSelector getSelector() {
+            return mSelector;
+        }
+
         /** Main channel expressed in units according to band type.
          * Currently all defined band types express channels as frequency in kHz
          * @return the program channel
+         * @deprecated Use {@link getSelector()} instead.
          */
+        @Deprecated
         public int getChannel() {
-            return mChannel;
+            try {
+                return (int) mSelector.getFirstId(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY);
+            } catch (IllegalArgumentException ex) {
+                Log.w(TAG, "Not an AM/FM program");
+                return 0;
+            }
         }
+
         /** Sub channel ID. E.g 1 for HD radio HD1
          * @return the program sub channel
+         * @deprecated Use {@link getSelector()} instead.
          */
+        @Deprecated
         public int getSubChannel() {
-            return mSubChannel;
+            try {
+                return (int) mSelector.getFirstId(
+                        ProgramSelector.IDENTIFIER_TYPE_HD_SUBCHANNEL) + 1;
+            } catch (IllegalArgumentException ex) {
+                // this is a normal behavior for analog AM/FM selector
+                return 0;
+            }
         }
+
         /** {@code true} if the tuner is currently tuned on a valid station
          * @return {@code true} if currently tuned, {@code false} otherwise.
          */
@@ -1333,6 +1415,22 @@
             return (mFlags & FLAG_MUTED) != 0;
         }
 
+        /**
+         * {@code true} if radio station transmits traffic information
+         * regularily.
+         */
+        public boolean isTrafficProgram() {
+            return (mFlags & FLAG_TRAFFIC_PROGRAM) != 0;
+        }
+
+        /**
+         * {@code true} if radio station transmits traffic information
+         * at the very moment.
+         */
+        public boolean isTrafficAnnouncementActive() {
+            return (mFlags & FLAG_TRAFFIC_ANNOUNCEMENT) != 0;
+        }
+
         /** Signal strength indicator from 0 (no signal) to 100 (excellent)
          * @return the signal strength indication.
          */
@@ -1362,8 +1460,7 @@
         }
 
         private ProgramInfo(Parcel in) {
-            mChannel = in.readInt();
-            mSubChannel = in.readInt();
+            mSelector = in.readParcelable(null);
             mTuned = in.readByte() == 1;
             mStereo = in.readByte() == 1;
             mDigital = in.readByte() == 1;
@@ -1390,8 +1487,7 @@
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(mChannel);
-            dest.writeInt(mSubChannel);
+            dest.writeParcelable(mSelector, 0);
             dest.writeByte((byte)(mTuned ? 1 : 0));
             dest.writeByte((byte)(mStereo ? 1 : 0));
             dest.writeByte((byte)(mDigital ? 1 : 0));
@@ -1413,7 +1509,7 @@
 
         @Override
         public String toString() {
-            return "ProgramInfo [mChannel=" + mChannel + ", mSubChannel=" + mSubChannel
+            return "ProgramInfo [mSelector=" + mSelector
                     + ", mTuned=" + mTuned + ", mStereo=" + mStereo + ", mDigital=" + mDigital
                     + ", mFlags=" + mFlags + ", mSignalStrength=" + mSignalStrength
                     + ((mMetadata == null) ? "" : (", mMetadata=" + mMetadata.toString()))
@@ -1424,8 +1520,7 @@
         public int hashCode() {
             final int prime = 31;
             int result = 1;
-            result = prime * result + mChannel;
-            result = prime * result + mSubChannel;
+            result = prime * result + mSelector.hashCode();
             result = prime * result + (mTuned ? 1 : 0);
             result = prime * result + (mStereo ? 1 : 0);
             result = prime * result + (mDigital ? 1 : 0);
@@ -1443,10 +1538,7 @@
             if (!(obj instanceof ProgramInfo))
                 return false;
             ProgramInfo other = (ProgramInfo) obj;
-            if (mChannel != other.getChannel())
-                return false;
-            if (mSubChannel != other.getSubChannel())
-                return false;
+            if (!mSelector.equals(other.getSelector())) return false;
             if (mTuned != other.isTuned())
                 return false;
             if (mStereo != other.isStereo())
@@ -1530,7 +1622,7 @@
         Log.d(TAG, "Opening tuner " + moduleId + "...");
 
         ITuner tuner;
-        ITunerCallback halCallback = new TunerCallbackAdapter(callback, handler);
+        TunerCallbackAdapter halCallback = new TunerCallbackAdapter(callback, handler);
         try {
             tuner = mService.openTuner(moduleId, config, withAudio, halCallback);
         } catch (RemoteException e) {
@@ -1541,7 +1633,8 @@
             Log.e(TAG, "Failed to open tuner");
             return null;
         }
-        return new TunerAdapter(tuner);
+        halCallback.attachTuner(tuner);
+        return new TunerAdapter(tuner, config != null ? config.getType() : BAND_INVALID);
     }
 
     @NonNull private final Context mContext;
diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java
index 61e62ea..6637ed2 100644
--- a/core/java/android/hardware/radio/RadioTuner.java
+++ b/core/java/android/hardware/radio/RadioTuner.java
@@ -170,10 +170,22 @@
      *  <li>{@link RadioManager#STATUS_DEAD_OBJECT} if the binder transaction to the native
      *  service fails, </li>
      * </ul>
+     * @deprecated Use {@link tune(ProgramSelector)} instead.
      */
+    @Deprecated
     public abstract int tune(int channel, int subChannel);
 
     /**
+     * Tune to a program.
+     *
+     * The operation is asynchronous and {@link Callback} onProgramInfoChanged() will be called
+     * when tune completes or onError() when cancelled or on timeout.
+     *
+     * @thows IllegalArgumentException if the provided selector is invalid
+     */
+    public abstract void tune(@NonNull ProgramSelector selector);
+
+    /**
      * Cancel a pending scan or tune operation.
      * If an operation is pending, {@link Callback} onError() will be called with
      * {@link #ERROR_CANCELLED}.
@@ -191,6 +203,17 @@
     public abstract int cancel();
 
     /**
+     * Cancels traffic or emergency announcement.
+     *
+     * If there was no announcement to cancel, no action is taken.
+     *
+     * There is a race condition between calling cancelAnnouncement and the actual announcement
+     * being finished, so onTrafficAnnouncement / onEmergencyAnnouncement callback should be
+     * tracked with proper locking.
+     */
+    public abstract void cancelAnnouncement();
+
+    /**
      * Get current station information.
      * @param info a ProgramInfo array of lengh 1 where the information is returned.
      * @return
@@ -318,20 +341,24 @@
          * or {@link RadioTuner#setConfiguration(RadioManager.BandConfig)}
          */
         public void onConfigurationChanged(RadioManager.BandConfig config) {}
+
         /**
-         * onProgramInfoChanged() is called upon successful completion of
-         * {@link RadioTuner#step(int, boolean)}, {@link RadioTuner#scan(int, boolean)},
-         * {@link RadioTuner#tune(int, int)} or when a switching to alternate frequency occurs.
-         * Note that if metadata only are updated,  {@link #onMetadataChanged(RadioMetadata)} will
-         * be called.
+         * Called when program info (including metadata) for the current program has changed.
+         *
+         * It happens either upon successful completion of {@link RadioTuner#step(int, boolean)},
+         * {@link RadioTuner#scan(int, boolean)}, {@link RadioTuner#tune(int, int)}; when
+         * a switching to alternate frequency occurs; or when metadata is updated.
          */
         public void onProgramInfoChanged(RadioManager.ProgramInfo info) {}
+
         /**
-         * onMetadataChanged() is called when new meta data are received on current program.
-         * Meta data are also received in {@link RadioManager.ProgramInfo} when
-         *  {@link #onProgramInfoChanged(RadioManager.ProgramInfo)} is called.
+         * Called when metadata is updated for the current program.
+         *
+         * @deprecated Use {@link #onProgramInfoChanged(RadioManager.ProgramInfo)} instead.
          */
+        @Deprecated
         public void onMetadataChanged(RadioMetadata metadata) {}
+
         /**
          * onTrafficAnnouncement() is called when a traffic announcement starts and stops.
          */
@@ -374,7 +401,7 @@
         /**
          * Called when available program list changed.
          *
-         * Use getProgramList() to get the actual list.
+         * Use {@link RadioTuner#getProgramList(String)} to get an actual list.
          */
         public void onProgramListChanged() {}
     }
diff --git a/core/java/android/hardware/radio/TunerAdapter.java b/core/java/android/hardware/radio/TunerAdapter.java
index dbaca62..3bc0ac3 100644
--- a/core/java/android/hardware/radio/TunerAdapter.java
+++ b/core/java/android/hardware/radio/TunerAdapter.java
@@ -32,11 +32,14 @@
     @NonNull private final ITuner mTuner;
     private boolean mIsClosed = false;
 
-    TunerAdapter(ITuner tuner) {
+    private @RadioManager.Band int mBand;
+
+    TunerAdapter(ITuner tuner, @RadioManager.Band int band) {
         if (tuner == null) {
             throw new NullPointerException();
         }
         mTuner = tuner;
+        mBand = band;
     }
 
     @Override
@@ -59,6 +62,7 @@
     public int setConfiguration(RadioManager.BandConfig config) {
         try {
             mTuner.setConfiguration(config);
+            mBand = config.getType();
             return RadioManager.STATUS_OK;
         } catch (IllegalArgumentException e) {
             Log.e(TAG, "Can't set configuration", e);
@@ -138,7 +142,7 @@
     @Override
     public int tune(int channel, int subChannel) {
         try {
-            mTuner.tune(channel, subChannel);
+            mTuner.tune(ProgramSelector.createAmFmSelector(mBand, channel, subChannel));
         } catch (IllegalStateException e) {
             Log.e(TAG, "Can't tune", e);
             return RadioManager.STATUS_INVALID_OPERATION;
@@ -153,6 +157,15 @@
     }
 
     @Override
+    public void tune(@NonNull ProgramSelector selector) {
+        try {
+            mTuner.tune(selector);
+        } catch (RemoteException e) {
+            throw new RuntimeException("service died", e);
+        }
+    }
+
+    @Override
     public int cancel() {
         try {
             mTuner.cancel();
@@ -167,6 +180,15 @@
     }
 
     @Override
+    public void cancelAnnouncement() {
+        try {
+            mTuner.cancelAnnouncement();
+        } catch (RemoteException e) {
+            throw new RuntimeException("service died", e);
+        }
+    }
+
+    @Override
     public int getProgramInformation(RadioManager.ProgramInfo[] info) {
         if (info == null || info.length != 1) {
             throw new IllegalArgumentException("The argument must be an array of length 1");
diff --git a/core/java/android/hardware/radio/TunerCallbackAdapter.java b/core/java/android/hardware/radio/TunerCallbackAdapter.java
index 155ffa7..8c3826c 100644
--- a/core/java/android/hardware/radio/TunerCallbackAdapter.java
+++ b/core/java/android/hardware/radio/TunerCallbackAdapter.java
@@ -20,13 +20,21 @@
 import android.annotation.Nullable;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
 
 /**
  * Implements the ITunerCallback interface by forwarding calls to RadioTuner.Callback.
  */
 class TunerCallbackAdapter extends ITunerCallback.Stub {
+    private static final String TAG = "radio.TunerCallbackAdapter";
+
     @NonNull private final RadioTuner.Callback mCallback;
     @NonNull private final Handler mHandler;
+    private final Object mLock = new Object();
+
+    @Nullable private ITuner mTuner;
+    boolean mPendingProgramInfoChanged = false;
 
     TunerCallbackAdapter(@NonNull RadioTuner.Callback callback, @Nullable Handler handler) {
         mCallback = callback;
@@ -37,6 +45,14 @@
         }
     }
 
+    public void attachTuner(@NonNull ITuner tuner) {
+        synchronized (mLock) {
+            if (mTuner != null) throw new IllegalStateException();
+            mTuner = tuner;
+            if (mPendingProgramInfoChanged) onProgramInfoChanged();
+        }
+    }
+
     @Override
     public void onError(int status) {
         mHandler.post(() -> mCallback.onError(status));
@@ -48,13 +64,28 @@
     }
 
     @Override
-    public void onProgramInfoChanged(RadioManager.ProgramInfo info) {
-        mHandler.post(() -> mCallback.onProgramInfoChanged(info));
-    }
+    public void onProgramInfoChanged() {
+        synchronized (mLock) {
+            if (mTuner == null) {
+                mPendingProgramInfoChanged = true;
+                return;
+            }
+        }
 
-    @Override
-    public void onMetadataChanged(RadioMetadata metadata) {
-        mHandler.post(() -> mCallback.onMetadataChanged(metadata));
+        RadioManager.ProgramInfo info;
+        try {
+            info = mTuner.getProgramInformation();
+        } catch (RemoteException e) {
+            Log.e(TAG, "service died", e);
+            return;
+        }
+
+        mHandler.post(() -> {
+            mCallback.onProgramInfoChanged(info);
+
+            RadioMetadata metadata = info.getMetadata();
+            if (metadata != null) mCallback.onMetadataChanged(metadata);
+        });
     }
 
     @Override
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index 9b5d0d3..5b15c0d 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.content.Context;
 import android.os.Build;
@@ -257,6 +258,7 @@
      * @hide
      */
     @SystemApi
+    @SuppressLint("Doclava125")
     public boolean resetDevice() {
         return native_reset_device();
     }
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 7b1e61e..7b948a7 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -21,6 +21,7 @@
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkState;
 import android.net.NetworkTemplate;
+import android.telephony.SubscriptionPlan;
 
 /**
  * Interface that creates and modifies network policy rules.
@@ -67,5 +68,10 @@
 
     NetworkQuotaInfo getNetworkQuotaInfo(in NetworkState state);
 
+    SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage);
+    void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage);
+
+    String getSubscriptionPlanOwner(int subId);
+
     void factoryReset(String subscriber);
 }
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index 5830667..edf9a28 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -46,17 +46,20 @@
     public static final long SNOOZE_NEVER = -1;
 
     public NetworkTemplate template;
-    public int cycleDay;
-    public String cycleTimezone;
-    public long warningBytes;
-    public long limitBytes;
-    public long lastWarningSnooze;
-    public long lastLimitSnooze;
-    @Deprecated public boolean metered;
-    public boolean inferred;
+    @Deprecated public int cycleDay = CYCLE_NONE;
+    @Deprecated public String cycleTimezone = "UTC";
+    public long warningBytes = WARNING_DISABLED;
+    public long limitBytes = LIMIT_DISABLED;
+    public long lastWarningSnooze = SNOOZE_NEVER;
+    public long lastLimitSnooze = SNOOZE_NEVER;
+    @Deprecated public boolean metered = true;
+    public boolean inferred = false;
 
     private static final long DEFAULT_MTU = 1500;
 
+    public NetworkPolicy() {
+    }
+
     @Deprecated
     public NetworkPolicy(NetworkTemplate template, int cycleDay, String cycleTimezone,
             long warningBytes, long limitBytes, boolean metered) {
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index fc96004..3fe9b0d 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -17,7 +17,6 @@
 package android.net;
 
 import static android.content.pm.PackageManager.GET_SIGNATURES;
-import static android.net.NetworkPolicy.CYCLE_NONE;
 
 import android.annotation.SystemService;
 import android.app.ActivityManager;
@@ -30,13 +29,15 @@
 import android.net.wifi.WifiInfo;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.telephony.SubscriptionPlan;
 import android.util.DebugUtils;
+import android.util.Pair;
 
 import com.google.android.collect.Sets;
 
-import java.util.Calendar;
+import java.time.ZonedDateTime;
 import java.util.HashSet;
-import java.util.TimeZone;
+import java.util.Iterator;
 
 /**
  * Manager for creating and modifying network policy rules.
@@ -251,73 +252,9 @@
         }
     }
 
-    /**
-     * Compute the last cycle boundary for the given {@link NetworkPolicy}. For
-     * example, if cycle day is 20th, and today is June 15th, it will return May
-     * 20th. When cycle day doesn't exist in current month, it snaps to the 1st
-     * of following month.
-     *
-     * @hide
-     */
-    public static long computeLastCycleBoundary(long currentTime, NetworkPolicy policy) {
-        if (policy.cycleDay == CYCLE_NONE) {
-            throw new IllegalArgumentException("Unable to compute boundary without cycleDay");
-        }
-
-        final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(policy.cycleTimezone));
-        cal.setTimeInMillis(currentTime);
-        snapToCycleDay(cal, policy.cycleDay);
-
-        if (cal.getTimeInMillis() >= currentTime) {
-            // Cycle boundary is beyond now, use last cycle boundary
-            cal.set(Calendar.DAY_OF_MONTH, 1);
-            cal.add(Calendar.MONTH, -1);
-            snapToCycleDay(cal, policy.cycleDay);
-        }
-
-        return cal.getTimeInMillis();
-    }
-
     /** {@hide} */
-    public static long computeNextCycleBoundary(long currentTime, NetworkPolicy policy) {
-        if (policy.cycleDay == CYCLE_NONE) {
-            throw new IllegalArgumentException("Unable to compute boundary without cycleDay");
-        }
-
-        final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(policy.cycleTimezone));
-        cal.setTimeInMillis(currentTime);
-        snapToCycleDay(cal, policy.cycleDay);
-
-        if (cal.getTimeInMillis() <= currentTime) {
-            // Cycle boundary is before now, use next cycle boundary
-            cal.set(Calendar.DAY_OF_MONTH, 1);
-            cal.add(Calendar.MONTH, 1);
-            snapToCycleDay(cal, policy.cycleDay);
-        }
-
-        return cal.getTimeInMillis();
-    }
-
-    /**
-     * Snap to the cycle day for the current month given; when cycle day doesn't
-     * exist, it snaps to last second of current month.
-     *
-     * @hide
-     */
-    public static void snapToCycleDay(Calendar cal, int cycleDay) {
-        cal.set(Calendar.HOUR_OF_DAY, 0);
-        cal.set(Calendar.MINUTE, 0);
-        cal.set(Calendar.SECOND, 0);
-        if (cycleDay > cal.getActualMaximum(Calendar.DAY_OF_MONTH)) {
-            cal.add(Calendar.MONTH, 1);
-            cal.set(Calendar.DAY_OF_MONTH, 1);
-            cal.set(Calendar.HOUR_OF_DAY, 0);
-            cal.set(Calendar.MINUTE, 0);
-            cal.set(Calendar.SECOND, 0);
-            cal.add(Calendar.SECOND, -1);
-        } else {
-            cal.set(Calendar.DAY_OF_MONTH, cycleDay);
-        }
+    public static Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator(NetworkPolicy policy) {
+        return SubscriptionPlan.convert(policy).cycleIterator();
     }
 
     /**
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index 2d9860c..4b79cbb 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -99,7 +99,7 @@
  *       shut down the tunnel gracefully.</li>
  * </ol>
  *
- * <p>Services extended this class need to be declared with appropriate
+ * <p>Services extending this class need to be declared with an appropriate
  * permission and intent filter. Their access must be secured by
  * {@link android.Manifest.permission#BIND_VPN_SERVICE} permission, and
  * their intent filter must match {@link #SERVICE_INTERFACE} action. Here
@@ -112,6 +112,13 @@
  *     &lt;/intent-filter&gt;
  * &lt;/service&gt;</pre>
  *
+ * <p> The Android system starts a VPN in the background by calling
+ * {@link android.content.Context#startService startService()}. In Android 8.0
+ * (API level 26) and higher, the system places VPN apps on the temporary
+ * whitelist for a short period so the app can start in the background. The VPN
+ * app must promote itself to the foreground after it's launched or the system
+ * will shut down the app.
+ *
  * @see Builder
  */
 public class VpnService extends Service {
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index a34668c..41f090a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -16,15 +16,6 @@
 
 package android.os;
 
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Formatter;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
 import android.app.job.JobParameters;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -43,6 +34,15 @@
 import com.android.internal.os.BatterySipper;
 import com.android.internal.os.BatteryStatsHelper;
 
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Formatter;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 /**
  * A class providing access to battery usage statistics, including information on
  * wakelocks, processes, packages, and services.  All times are represented in microseconds
@@ -168,6 +168,11 @@
     public static final int BLUETOOTH_UNOPTIMIZED_SCAN_ON = 21;
 
     /**
+     * A constant indicating a foreground service timer
+     */
+    public static final int FOREGROUND_SERVICE = 22;
+
+    /**
      * Include all of the data in the stats, including previously saved data.
      */
     public static final int STATS_SINCE_CHARGED = 0;
@@ -223,7 +228,11 @@
     private static final String CPU_TIMES_AT_FREQ_DATA = "ctf";
     private static final String SENSOR_DATA = "sr";
     private static final String VIBRATOR_DATA = "vib";
-    private static final String FOREGROUND_DATA = "fg";
+    private static final String FOREGROUND_ACTIVITY_DATA = "fg";
+    // fgs line is:
+    // BATTERY_STATS_CHECKIN_VERSION, uid, category, "fgs",
+    // foreground service time, count
+    private static final String FOREGROUND_SERVICE_DATA = "fgs";
     private static final String STATE_TIME_DATA = "st";
     // wl line is:
     // BATTERY_STATS_CHECKIN_VERSION, uid, which, "wl", name,
@@ -582,6 +591,11 @@
         public abstract Timer getFlashlightTurnedOnTimer();
         public abstract Timer getCameraTurnedOnTimer();
         public abstract Timer getForegroundActivityTimer();
+
+        /**
+         * Returns the timer keeping track of Foreground Service time
+         */
+        public abstract Timer getForegroundServiceTimer();
         public abstract Timer getBluetoothScanTimer();
         public abstract Timer getBluetoothScanBackgroundTimer();
         public abstract Timer getBluetoothUnoptimizedScanTimer();
@@ -3616,7 +3630,10 @@
             dumpTimer(pw, uid, category, VIBRATOR_DATA, u.getVibratorOnTimer(),
                     rawRealtime, which);
 
-            dumpTimer(pw, uid, category, FOREGROUND_DATA, u.getForegroundActivityTimer(),
+            dumpTimer(pw, uid, category, FOREGROUND_ACTIVITY_DATA, u.getForegroundActivityTimer(),
+                    rawRealtime, which);
+
+            dumpTimer(pw, uid, category, FOREGROUND_SERVICE_DATA, u.getForegroundServiceTimer(),
                     rawRealtime, which);
 
             final Object[] stateTimes = new Object[Uid.NUM_PROCESS_STATE];
@@ -5093,6 +5110,8 @@
                     "Vibrator");
             uidActivity |= printTimer(pw, sb, u.getForegroundActivityTimer(), rawRealtime, which,
                     prefix, "Foreground activities");
+            uidActivity |= printTimer(pw, sb, u.getForegroundServiceTimer(), rawRealtime, which,
+                    prefix, "Foreground services");
 
             long totalStateTime = 0;
             for (int ips=0; ips<Uid.NUM_PROCESS_STATE; ips++) {
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 71f5ff7..6372113 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -739,7 +739,7 @@
      * {@link Environment#getDataDirectory()}, the returned value will be
      * {@link #UUID_DEFAULT}.
      *
-     * @throws IOException when the storage device at the given path isn't
+     * @throws IOException when the storage device hosting the given path isn't
      *             present, or when it doesn't have a valid UUID.
      */
     public @NonNull UUID getUuidForPath(@NonNull File path) throws IOException {
@@ -771,6 +771,19 @@
         throw new FileNotFoundException("Failed to find a storage device for " + volumeUuid);
     }
 
+    /**
+     * Test if the given file descriptor supports allocation of disk space using
+     * {@link #allocateBytes(FileDescriptor, long)}.
+     */
+    public boolean isAllocationSupported(@NonNull FileDescriptor fd) {
+        try {
+            getUuidForPath(ParcelFileDescriptor.getFile(fd));
+            return true;
+        } catch (IOException e) {
+            return false;
+        }
+    }
+
     /** {@hide} */
     public @NonNull List<VolumeInfo> getVolumes() {
         try {
@@ -1562,12 +1575,6 @@
         }
     }
 
-    /** @removed */
-    @Deprecated
-    public long getCacheQuotaBytes(@NonNull File path) throws IOException {
-        return getCacheQuotaBytes(getUuidForPath(path));
-    }
-
     /**
      * Return total size in bytes of all cached data belonging to the calling
      * app on the given storage volume.
@@ -1603,36 +1610,6 @@
         }
     }
 
-    /** @removed */
-    @Deprecated
-    public long getCacheSizeBytes(@NonNull File path) throws IOException {
-        return getCacheSizeBytes(getUuidForPath(path));
-    }
-
-    /** @removed */
-    @Deprecated
-    public long getCacheQuotaBytes() throws IOException {
-        return getCacheQuotaBytes(mContext.getCacheDir());
-    }
-
-    /** @removed */
-    @Deprecated
-    public long getCacheSizeBytes() throws IOException {
-        return getCacheSizeBytes(mContext.getCacheDir());
-    }
-
-    /** @removed */
-    @Deprecated
-    public long getExternalCacheQuotaBytes() throws IOException {
-        return getCacheQuotaBytes(mContext.getExternalCacheDir());
-    }
-
-    /** @removed */
-    @Deprecated
-    public long getExternalCacheSizeBytes() throws IOException {
-        return getCacheSizeBytes(mContext.getExternalCacheDir());
-    }
-
     /**
      * Flag indicating that a disk space allocation request should operate in an
      * aggressive mode. This flag should only be rarely used in situations that
@@ -1741,15 +1718,6 @@
         }
     }
 
-    /** @removed */
-    @Deprecated
-    @WorkerThread
-    @SuppressLint("Doclava125")
-    public long getAllocatableBytes(@NonNull File path,
-            @RequiresPermission @AllocateFlags int flags) throws IOException {
-        return getAllocatableBytes(getUuidForPath(path), flags);
-    }
-
     /**
      * Allocate the requested number of bytes for your application to use on the
      * given storage volume. This will cause the system to delete any cached
@@ -1798,15 +1766,6 @@
         }
     }
 
-    /** @removed */
-    @Deprecated
-    @WorkerThread
-    @SuppressLint("Doclava125")
-    public void allocateBytes(@NonNull File path, @BytesLong long bytes,
-            @RequiresPermission @AllocateFlags int flags) throws IOException {
-        allocateBytes(getUuidForPath(path), bytes, flags);
-    }
-
     /**
      * Allocate the requested number of bytes for your application to use in the
      * given open file. This will cause the system to delete any cached files
@@ -1834,6 +1793,7 @@
      *             doesn't support allocating space, or if the device had
      *             trouble allocating the requested space.
      * @see #getAllocatableBytes(UUID, int)
+     * @see #isAllocationSupported(FileDescriptor)
      * @see Environment#isExternalStorageEmulated(File)
      */
     @WorkerThread
@@ -1848,17 +1808,28 @@
     public void allocateBytes(FileDescriptor fd, @BytesLong long bytes,
             @RequiresPermission @AllocateFlags int flags) throws IOException {
         final File file = ParcelFileDescriptor.getFile(fd);
+        final UUID uuid = getUuidForPath(file);
         for (int i = 0; i < 3; i++) {
             try {
                 final long haveBytes = Os.fstat(fd).st_blocks * 512;
                 final long needBytes = bytes - haveBytes;
 
                 if (needBytes > 0) {
-                    allocateBytes(file, needBytes, flags);
+                    allocateBytes(uuid, needBytes, flags);
                 }
 
-                Os.posix_fallocate(fd, 0, bytes);
-                return;
+                try {
+                    Os.posix_fallocate(fd, 0, bytes);
+                    return;
+                } catch (ErrnoException e) {
+                    if (e.errno == OsConstants.ENOSYS || e.errno == OsConstants.ENOTSUP) {
+                        Log.w(TAG, "fallocate() not supported; falling back to ftruncate()");
+                        Os.ftruncate(fd, bytes);
+                        return;
+                    } else {
+                        throw e;
+                    }
+                }
             } catch (ErrnoException e) {
                 if (e.errno == OsConstants.ENOSPC) {
                     Log.w(TAG, "Odd, not enough space; let's try again?");
@@ -1941,18 +1912,6 @@
         return isCacheBehavior(path, XATTR_CACHE_GROUP);
     }
 
-    /** @removed */
-    @Deprecated
-    public void setCacheBehaviorAtomic(File path, boolean atomic) throws IOException {
-        setCacheBehaviorGroup(path, atomic);
-    }
-
-    /** @removed */
-    @Deprecated
-    public boolean isCacheBehaviorAtomic(File path) throws IOException {
-        return isCacheBehaviorGroup(path);
-    }
-
     /**
      * Enable or disable special cache behavior that leaves deleted cache files
      * intact as tombstones.
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index 52dccb4..51b7798 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -18,9 +18,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.app.Activity;
 import android.app.Application.ActivityLifecycleCallbacks;
 import android.content.ComponentName;
@@ -142,7 +142,6 @@
      * @see #getPrintServices
      * @hide
      */
-    @TestApi
     public static final int ALL_SERVICES = ENABLED_SERVICES | DISABLED_SERVICES;
 
     /**
@@ -554,6 +553,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRINT_SERVICES)
     public void addPrintServicesChangeListener(@NonNull PrintServicesChangeListener listener,
             @Nullable Handler handler) {
         Preconditions.checkNotNull(listener);
@@ -589,6 +589,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRINT_SERVICES)
     public void removePrintServicesChangeListener(@NonNull PrintServicesChangeListener listener) {
         Preconditions.checkNotNull(listener);
 
@@ -629,8 +630,8 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRINT_SERVICES)
     public @NonNull List<PrintServiceInfo> getPrintServices(int selectionFlags) {
         Preconditions.checkFlagsArgument(selectionFlags, ALL_SERVICES);
 
@@ -656,6 +657,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS)
     public void addPrintServiceRecommendationsChangeListener(
             @NonNull PrintServiceRecommendationsChangeListener listener,
             @Nullable Handler handler) {
@@ -692,6 +694,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS)
     public void removePrintServiceRecommendationsChangeListener(
             @NonNull PrintServiceRecommendationsChangeListener listener) {
         Preconditions.checkNotNull(listener);
@@ -731,6 +734,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS)
     public @NonNull List<RecommendationInfo> getPrintServiceRecommendations() {
         try {
             List<RecommendationInfo> recommendations =
diff --git a/core/java/android/printservice/PrintServiceInfo.java b/core/java/android/printservice/PrintServiceInfo.java
index 5ef9319..57f1229 100644
--- a/core/java/android/printservice/PrintServiceInfo.java
+++ b/core/java/android/printservice/PrintServiceInfo.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -49,7 +48,6 @@
  *
  * @hide
  */
-@TestApi
 @SystemApi
 public final class PrintServiceInfo implements Parcelable {
 
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 9d83bd7..a2c5a92 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -485,6 +485,7 @@
          * partition. This option is only used by system apps and so it requires
          * android.permission.ACCESS_CACHE_FILESYSTEM permission.
          */
+        @Deprecated
         public static final int DESTINATION_SYSTEMCACHE_PARTITION = 5;
 
         /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ffa38d4..cda7f59 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7744,6 +7744,16 @@
          */
         public static final String CDMA_SUBSCRIPTION_MODE = "subscription_mode";
 
+        /**
+         * The default value for whether background data is enabled or not.
+         *
+         * Used by {@code NetworkPolicyManagerService}.
+         *
+         * @hide
+         */
+        public static final String DEFAULT_RESTRICT_BACKGROUND_DATA =
+                "default_restrict_background_data";
+
         /** Inactivity timeout to track mobile data activity.
         *
         * If set to a positive integer, it indicates the inactivity timeout value in seconds to
diff --git a/core/java/android/provider/TimeZoneRulesDataContract.java b/core/java/android/provider/TimeZoneRulesDataContract.java
index a607563..33d2588 100644
--- a/core/java/android/provider/TimeZoneRulesDataContract.java
+++ b/core/java/android/provider/TimeZoneRulesDataContract.java
@@ -16,6 +16,7 @@
 
 package android.provider;
 
+import android.annotation.SystemApi;
 import android.net.Uri;
 
 /**
@@ -24,7 +25,7 @@
  *
  * @hide
  */
-// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728
+@SystemApi
 public final class TimeZoneRulesDataContract {
 
     private TimeZoneRulesDataContract() {}
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 137cf57..ce678fc 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -17,7 +17,7 @@
 
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.app.NotificationChannel;
+import android.app.Notification;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -48,6 +48,12 @@
      * {@link NotificationAssistantService#onNotificationSnoozedUntilContext}.
      */
     public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
+    /**
+     * Data type: String. Used to change what {@link Notification#getGroup() group} a notification
+     * belongs to.
+     * @hide
+     */
+    public static final String KEY_GROUP_KEY = "key_group_key";
 
     /**
      * Create a notification adjustment.
@@ -146,4 +152,11 @@
         dest.writeBundle(mSignals);
         dest.writeInt(mUser);
     }
+
+    @Override
+    public String toString() {
+        return "Adjustment{"
+                + "mSignals=" + mSignals
+                + '}';
+    }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5225121..6af01f66 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -10180,6 +10180,7 @@
      *
      * @hide
      */
+    @TestApi
     public final void setFocusedInCluster() {
         setFocusedInCluster(findKeyboardNavigationCluster());
     }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e38a55f..05f9da5 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -28,7 +28,6 @@
 import android.Manifest;
 import android.animation.LayoutTransition;
 import android.annotation.NonNull;
-import android.annotation.TestApi;
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.ResourcesManager;
@@ -214,11 +213,8 @@
 
     /**
      * Always assign focus if a focusable View is available.
-     *
-     * @hide
      */
-    @TestApi
-    public static boolean sAlwaysAssignFocus;
+    private static boolean sAlwaysAssignFocus;
 
     /**
      * This list must only be modified by the main thread, so a lock is only needed when changing
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index ed2547f..4c38266 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1645,9 +1645,13 @@
      * TODO: Add documentation for the format of the urls.
      *
      * @param urls the list of URLs
+     * @param callback will be called with true if URLs are successfully added to the whitelist. It
+     * will be called with false if any URLs are malformed. The callback will be run on the UI
+     * thread.
      */
-    public static void setSafeBrowsingWhiteList(@Nullable List<String> urls) {
-        getFactory().getStatics().setSafeBrowsingWhiteList(urls);
+    public static void setSafeBrowsingWhitelist(@Nullable List<String> urls,
+            @Nullable ValueCallback<Boolean> callback) {
+        getFactory().getStatics().setSafeBrowsingWhitelist(urls, callback);
     }
 
     /**
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index 9b31a0c..613eb72 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -91,9 +91,10 @@
 
         /**
         * Implement the API method
-        * {@link android.webkit.WebView#setSafeBrowsingWhiteList(List<String>)}
+        * {@link android.webkit.WebView#setSafeBrowsingWhitelist(List<String>,
+        * ValueCallback<Boolean>)}
         */
-        void setSafeBrowsingWhiteList(List<String> urls);
+        void setSafeBrowsingWhitelist(List<String> urls, ValueCallback<Boolean> callback);
     }
 
     Statics getStatics();
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index ebea1a4..04874bd 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -28,6 +28,7 @@
 import android.graphics.Point;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Build;
 import android.os.CancellationSignal;
 import android.os.FileObserver;
 import android.os.FileUtils;
@@ -68,6 +69,8 @@
 
     private Handler mHandler;
 
+    private static final String MIMETYPE_PDF = "application/pdf";
+
     protected abstract File getFileForDocId(String docId, boolean visible)
             throws FileNotFoundException;
 
@@ -387,6 +390,13 @@
             String documentId, Point sizeHint, CancellationSignal signal)
             throws FileNotFoundException {
         final File file = getFileForDocId(documentId);
+        if (getTypeForFile(file).equals(MIMETYPE_PDF)) {
+            try {
+                return PdfUtils.openPdfThumbnail(file, sizeHint);
+            } catch (Exception e) {
+                Log.v(TAG, "Could not load PDF's thumbnail", e);
+            }
+        }
         return DocumentsContract.openImageThumbnail(file);
     }
 
@@ -416,7 +426,10 @@
 
         final String mimeType = getTypeForFile(file);
         final String displayName = file.getName();
-        if (mimeType.startsWith("image/")) {
+        // As of right now, we aren't sure on the performance affect of loading all PDF Thumbnails
+        // Until a solution is found, it will be behind a debuggable flag.
+        if (mimeType.startsWith("image/")
+                || (mimeType.equals(MIMETYPE_PDF) && Build.IS_DEBUGGABLE)) {
             flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
         }
 
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index 9c361b3..e923223 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -16,11 +16,14 @@
 
 package com.android.internal.content;
 
+import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.os.storage.VolumeInfo.ID_PRIVATE_INTERNAL;
+
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
 import android.content.pm.PackageInstaller.SessionParams;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageParser.PackageLite;
 import android.os.Environment;
@@ -39,21 +42,19 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import libcore.io.IoUtils;
+
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Collections;
 import java.util.Objects;
+import java.util.UUID;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 import java.util.zip.ZipOutputStream;
 
-import libcore.io.IoUtils;
-
-import static android.net.TrafficStats.MB_IN_BYTES;
-import static android.os.storage.VolumeInfo.ID_PRIVATE_INTERNAL;
-
 /**
  * Constants used internally between the PackageManager
  * and media container service transports.
@@ -358,7 +359,7 @@
         public boolean fitsOnInternalStorage(Context context, SessionParams params)
                 throws IOException {
             StorageManager storage = getStorageManager(context);
-            File target = getDataDirectory();
+            final UUID target = storage.getUuidForPath(getDataDirectory());
             return (params.sizeBytes <= storage.getAllocatableBytes(target,
                     translateAllocateFlags(params.installFlags)));
         }
@@ -466,7 +467,8 @@
             boolean isInternalStorage = ID_PRIVATE_INTERNAL.equals(vol.id);
             if (vol.type == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()
                     && (!isInternalStorage || allow3rdPartyOnInternal)) {
-                final long availBytes = storageManager.getAllocatableBytes(new File(vol.path),
+                final UUID target = storageManager.getUuidForPath(new File(vol.path));
+                final long availBytes = storageManager.getAllocatableBytes(target,
                         translateAllocateFlags(params.installFlags));
                 if (availBytes >= params.sizeBytes) {
                     allCandidates.add(vol.fsUuid);
@@ -523,7 +525,7 @@
 
     public static boolean fitsOnInternal(Context context, SessionParams params) throws IOException {
         final StorageManager storage = context.getSystemService(StorageManager.class);
-        final File target = Environment.getDataDirectory();
+        final UUID target = storage.getUuidForPath(Environment.getDataDirectory());
         return (params.sizeBytes <= storage.getAllocatableBytes(target,
                 translateAllocateFlags(params.installFlags)));
     }
diff --git a/core/java/com/android/internal/content/PdfUtils.java b/core/java/com/android/internal/content/PdfUtils.java
new file mode 100644
index 0000000..1716d42
--- /dev/null
+++ b/core/java/com/android/internal/content/PdfUtils.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.content;
+
+import android.annotation.Nullable;
+import android.content.res.AssetFileDescriptor;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.pdf.PdfRenderer;
+import android.os.AsyncTask;
+import android.os.ParcelFileDescriptor;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Utils class for extracting PDF Thumbnails
+ */
+public final class PdfUtils {
+
+    private PdfUtils() {
+    }
+
+    /**
+     * Returns the front page of the pdf as a thumbnail
+     * @param file Given PDF File
+     * @param size Cropping of the front page.
+     * @return AssetFileDescriptor containing the thumbnail as a bitmap.
+     * @throws IOException if the file isn't a pdf or if the file doesn't exist.
+     */
+    public static @Nullable AssetFileDescriptor openPdfThumbnail(File file, Point size)
+            throws IOException {
+        // Create the bitmap of the PDF's first page
+        ParcelFileDescriptor pdfDescriptor =
+                ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
+        PdfRenderer renderer = new PdfRenderer(pdfDescriptor);
+        PdfRenderer.Page frontPage = renderer.openPage(0);
+        Bitmap thumbnail = Bitmap.createBitmap(size.x, size.y,
+                Bitmap.Config.ARGB_8888);
+        frontPage.render(thumbnail, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
+
+        // Create an AssetFileDescriptor that contains the Bitmap's information
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+        // Quality is an integer that determines how much compression is used.
+        // However, this integer is ignored when using the PNG format
+        int quality = 100;
+        // The use of Bitmap.CompressFormat.JPEG leads to a black PDF background on the thumbnail
+        thumbnail.compress(Bitmap.CompressFormat.PNG, quality, out);
+
+        final ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+
+        final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createReliablePipe();
+        new AsyncTask<Object, Object, Object>() {
+            @Override
+            protected Object doInBackground(Object... params) {
+                final FileOutputStream fos = new FileOutputStream(fds[1].getFileDescriptor());
+                try {
+                    Streams.copy(in, fos);
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+                IoUtils.closeQuietly(fds[1]);
+                try {
+                    pdfDescriptor.close();
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+                return null;
+            }
+        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+        pdfDescriptor.close();
+        return new AssetFileDescriptor(fds[0], 0, AssetFileDescriptor.UNKNOWN_LENGTH);
+    }
+
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index fa23e40..c7f619d 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -119,7 +119,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 160 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 161 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS;
@@ -5637,6 +5637,7 @@
         StopwatchTimer mFlashlightTurnedOnTimer;
         StopwatchTimer mCameraTurnedOnTimer;
         StopwatchTimer mForegroundActivityTimer;
+        StopwatchTimer mForegroundServiceTimer;
         /** Total time spent by the uid holding any partial wakelocks. */
         DualTimer mAggregatedPartialWakelockTimer;
         DualTimer mBluetoothScanTimer;
@@ -5647,6 +5648,8 @@
         int mProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT;
         StopwatchTimer[] mProcessStateTimer;
 
+        boolean mInForegroundService = false;
+
         BatchTimer mVibratorOnTimer;
 
         Counter[] mUserActivityCounters;
@@ -6119,6 +6122,14 @@
             return mForegroundActivityTimer;
         }
 
+        public StopwatchTimer createForegroundServiceTimerLocked() {
+            if (mForegroundServiceTimer == null) {
+                mForegroundServiceTimer = new StopwatchTimer(mBsi.mClocks, Uid.this,
+                        FOREGROUND_SERVICE, null, mBsi.mOnBatteryTimeBase);
+            }
+            return mForegroundServiceTimer;
+        }
+
         public DualTimer createAggregatedPartialWakelockTimerLocked() {
             if (mAggregatedPartialWakelockTimer == null) {
                 mAggregatedPartialWakelockTimer = new DualTimer(mBsi.mClocks, this,
@@ -6207,6 +6218,16 @@
             }
         }
 
+        public void noteForegroundServiceResumedLocked(long elapsedRealtimeMs) {
+            createForegroundServiceTimerLocked().startRunningLocked(elapsedRealtimeMs);
+        }
+
+        public void noteForegroundServicePausedLocked(long elapsedRealtimeMs) {
+            if (mForegroundServiceTimer != null) {
+                mForegroundServiceTimer.stopRunningLocked(elapsedRealtimeMs);
+            }
+        }
+
         public BatchTimer createVibratorOnTimerLocked() {
             if (mVibratorOnTimer == null) {
                 mVibratorOnTimer = new BatchTimer(mBsi.mClocks, Uid.this, VIBRATOR_ON,
@@ -6335,6 +6356,11 @@
         }
 
         @Override
+        public Timer getForegroundServiceTimer() {
+            return mForegroundServiceTimer;
+        }
+
+        @Override
         public Timer getBluetoothScanTimer() {
             return mBluetoothScanTimer;
         }
@@ -6618,6 +6644,7 @@
             active |= !resetTimerIfNotNull(mFlashlightTurnedOnTimer, false);
             active |= !resetTimerIfNotNull(mCameraTurnedOnTimer, false);
             active |= !resetTimerIfNotNull(mForegroundActivityTimer, false);
+            active |= !resetTimerIfNotNull(mForegroundServiceTimer, false);
             active |= !resetTimerIfNotNull(mAggregatedPartialWakelockTimer, false);
             active |= !resetTimerIfNotNull(mBluetoothScanTimer, false);
             active |= !resetTimerIfNotNull(mBluetoothUnoptimizedScanTimer, false);
@@ -6817,6 +6844,10 @@
                     mForegroundActivityTimer.detach();
                     mForegroundActivityTimer = null;
                 }
+                if (mForegroundServiceTimer != null) {
+                    mForegroundServiceTimer.detach();
+                    mForegroundServiceTimer = null;
+                }
                 if (mAggregatedPartialWakelockTimer != null) {
                     mAggregatedPartialWakelockTimer.detach();
                     mAggregatedPartialWakelockTimer = null;
@@ -7026,6 +7057,12 @@
             } else {
                 out.writeInt(0);
             }
+            if (mForegroundServiceTimer != null) {
+                out.writeInt(1);
+                mForegroundServiceTimer.writeToParcel(out, elapsedRealtimeUs);
+            } else {
+                out.writeInt(0);
+            }
             if (mAggregatedPartialWakelockTimer != null) {
                 out.writeInt(1);
                 mAggregatedPartialWakelockTimer.writeToParcel(out, elapsedRealtimeUs);
@@ -7305,6 +7342,12 @@
                 mForegroundActivityTimer = null;
             }
             if (in.readInt() != 0) {
+                mForegroundServiceTimer = new StopwatchTimer(mBsi.mClocks, Uid.this,
+                        FOREGROUND_SERVICE, null, mBsi.mOnBatteryTimeBase, in);
+            } else {
+                mForegroundServiceTimer = null;
+            }
+            if (in.readInt() != 0) {
                 mAggregatedPartialWakelockTimer = new DualTimer(mBsi.mClocks, this,
                         AGGREGATED_WAKE_TYPE_PARTIAL, null,
                         mBsi.mOnBatteryScreenOffTimeBase, mOnBatteryScreenOffBackgroundTimeBase,
@@ -8350,6 +8393,9 @@
 
         public void updateUidProcessStateLocked(int procState) {
             int uidRunningState;
+            // Make special note of Foreground Services
+            final boolean userAwareService =
+                    (procState == ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
             if (procState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
                 uidRunningState = ActivityManager.PROCESS_STATE_NONEXISTENT;
             } else if (procState == ActivityManager.PROCESS_STATE_TOP) {
@@ -8368,24 +8414,37 @@
                 uidRunningState = PROCESS_STATE_CACHED;
             }
 
-            if (mProcessState == uidRunningState) return;
+            if (mProcessState == uidRunningState && userAwareService == mInForegroundService) {
+                return;
+            }
 
             final long elapsedRealtimeMs = mBsi.mClocks.elapsedRealtime();
-            final long uptimeMs = mBsi.mClocks.uptimeMillis();
+            if (mProcessState != uidRunningState) {
+                final long uptimeMs = mBsi.mClocks.uptimeMillis();
 
-            if (mProcessState != ActivityManager.PROCESS_STATE_NONEXISTENT) {
-                mProcessStateTimer[mProcessState].stopRunningLocked(elapsedRealtimeMs);
-            }
-            mProcessState = uidRunningState;
-            if (uidRunningState != ActivityManager.PROCESS_STATE_NONEXISTENT) {
-                if (mProcessStateTimer[uidRunningState] == null) {
-                    makeProcessState(uidRunningState, null);
+                if (mProcessState != ActivityManager.PROCESS_STATE_NONEXISTENT) {
+                    mProcessStateTimer[mProcessState].stopRunningLocked(elapsedRealtimeMs);
                 }
-                mProcessStateTimer[uidRunningState].startRunningLocked(elapsedRealtimeMs);
+                mProcessState = uidRunningState;
+                if (uidRunningState != ActivityManager.PROCESS_STATE_NONEXISTENT) {
+                    if (mProcessStateTimer[uidRunningState] == null) {
+                        makeProcessState(uidRunningState, null);
+                    }
+                    mProcessStateTimer[uidRunningState].startRunningLocked(elapsedRealtimeMs);
+                }
+
+                updateOnBatteryBgTimeBase(uptimeMs * 1000, elapsedRealtimeMs * 1000);
+                updateOnBatteryScreenOffBgTimeBase(uptimeMs * 1000, elapsedRealtimeMs * 1000);
             }
 
-            updateOnBatteryBgTimeBase(uptimeMs * 1000, elapsedRealtimeMs * 1000);
-            updateOnBatteryScreenOffBgTimeBase(uptimeMs * 1000, elapsedRealtimeMs * 1000);
+            if (userAwareService != mInForegroundService) {
+                if (userAwareService) {
+                    noteForegroundServiceResumedLocked(elapsedRealtimeMs);
+                } else {
+                    noteForegroundServicePausedLocked(elapsedRealtimeMs);
+                }
+                mInForegroundService = userAwareService;
+            }
         }
 
         /** Whether to consider Uid to be in the background for background timebase purposes. */
@@ -11610,6 +11669,9 @@
                 u.createForegroundActivityTimerLocked().readSummaryFromParcelLocked(in);
             }
             if (in.readInt() != 0) {
+                u.createForegroundServiceTimerLocked().readSummaryFromParcelLocked(in);
+            }
+            if (in.readInt() != 0) {
                 u.createAggregatedPartialWakelockTimerLocked().readSummaryFromParcelLocked(in);
             }
             if (in.readInt() != 0) {
@@ -12021,6 +12083,12 @@
             } else {
                 out.writeInt(0);
             }
+            if (u.mForegroundServiceTimer != null) {
+                out.writeInt(1);
+                u.mForegroundServiceTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+            } else {
+                out.writeInt(0);
+            }
             if (u.mAggregatedPartialWakelockTimer != null) {
                 out.writeInt(1);
                 u.mAggregatedPartialWakelockTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 07edc72..0dbe971 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -548,7 +548,8 @@
             int dexoptNeeded;
             try {
                 dexoptNeeded = DexFile.getDexOptNeeded(
-                    classPathElement, instructionSet, systemServerFilter, false /* newProfile */);
+                    classPathElement, instructionSet, systemServerFilter,
+                    false /* newProfile */, false /* downgrade */);
             } catch (FileNotFoundException ignored) {
                 // Do not add to the classpath.
                 Log.w(TAG, "Missing classpath element for system server: " + classPathElement);
@@ -572,7 +573,7 @@
                 try {
                     installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName,
                             instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
-                            uuid, sharedLibraries, seInfo);
+                            uuid, sharedLibraries, seInfo, false /* downgrade */);
                 } catch (RemoteException | ServiceSpecificException e) {
                     // Ignore (but log), we need this on the classpath for fallback mode.
                     Log.w(TAG, "Failed compiling classpath element for system server: "
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index 4f9e8a5..18990cf 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -217,6 +217,8 @@
                     "/proc/last_kmsg", -LOG_SIZE, "SYSTEM_LAST_KMSG");
             addFileWithFootersToDropBox(db, timestamps, headers, lastKmsgFooter,
                     "/sys/fs/pstore/console-ramoops", -LOG_SIZE, "SYSTEM_LAST_KMSG");
+            addFileWithFootersToDropBox(db, timestamps, headers, lastKmsgFooter,
+                    "/sys/fs/pstore/console-ramoops-0", -LOG_SIZE, "SYSTEM_LAST_KMSG");
             addFileToDropBox(db, timestamps, headers, "/cache/recovery/log", -LOG_SIZE,
                     "SYSTEM_RECOVERY_LOG");
             addFileToDropBox(db, timestamps, headers, "/cache/recovery/last_kmsg",
@@ -302,6 +304,10 @@
         if (fileTime <= 0) {
             file = new File("/sys/fs/pstore/console-ramoops");
             fileTime = file.lastModified();
+            if (fileTime <= 0) {
+                file = new File("/sys/fs/pstore/console-ramoops-0");
+                fileTime = file.lastModified();
+            }
         }
 
         if (fileTime <= 0) return;  // File does not exist
diff --git a/core/jni/android/graphics/GIFMovie.cpp b/core/jni/android/graphics/GIFMovie.cpp
index e501cf8..dd99b37 100644
--- a/core/jni/android/graphics/GIFMovie.cpp
+++ b/core/jni/android/graphics/GIFMovie.cpp
@@ -391,8 +391,8 @@
     }
 
     SkColor bgColor = SkPackARGB32(0, 0, 0, 0);
-    if (gif->SColorMap != nullptr) {
-        const GifColorType& col = gif->SColorMap->Colors[fGIF->SBackGroundColor];
+    if (gif->SColorMap != nullptr && gif->SBackGroundColor < gif->SColorMap->ColorCount) {
+        const GifColorType& col = gif->SColorMap->Colors[gif->SBackGroundColor];
         bgColor = SkColorSetARGB(0xFF, col.Red, col.Green, col.Blue);
     }
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 222ecbc..c052e5145 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2534,6 +2534,23 @@
     <permission android:name="android.permission.BIND_PRINT_RECOMMENDATION_SERVICE"
             android:protectionLevel="signature" />
 
+    <!-- Allows applications to get the installed and enabled print services.
+         @hide
+         @SystemApi
+         @TestApi
+         <p>Protection level: signature|preinstalled
+    -->
+    <permission android:name="android.permission.READ_PRINT_SERVICES"
+        android:protectionLevel="signature|preinstalled" />
+
+    <!-- Allows applications to get the currently recommended print services for printers.
+         @hide
+         @SystemApi
+         <p>Protection level: signature|preinstalled
+    -->
+    <permission android:name="android.permission.READ_PRINT_SERVICE_RECOMMENDATIONS"
+        android:protectionLevel="signature|preinstalled" />
+
     <!-- Must be required by a {@link android.nfc.cardemulation.HostApduService}
          or {@link android.nfc.cardemulation.OffHostApduService} to ensure that only
          the system can bind to it.
@@ -3139,9 +3156,13 @@
     <permission android:name="android.permission.MANAGE_NETWORK_POLICY"
         android:protectionLevel="signature" />
 
-    <!-- @SystemApi Allows an application to account its network traffic against other UIDs. Used
-         by system services like download manager and media server. Not for use by
-         third party apps. @hide -->
+    <!-- @SystemApi Allows an application to manage fallback subscription plans.
+         Note that another app providing plans for an explicit HNI will always
+         take precidence over these fallback plans. @hide -->
+    <permission android:name="android.permission.MANAGE_FALLBACK_SUBSCRIPTION_PLANS"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi @hide @deprecated use UPDATE_DEVICE_STATS instead -->
     <permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING"
         android:protectionLevel="signature|privileged" />
 
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 94b988e..733878b 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -22,67 +22,39 @@
     <!-- Do not translate. These are all of the drawable resources that should be preloaded by
          the zygote process before it starts forking application processes. -->
     <array name="preloaded_drawables">
-        <item>@drawable/ab_share_pack_material</item>
-        <item>@drawable/ab_solid_shadow_material</item>
         <item>@drawable/action_bar_item_background_material</item>
         <item>@drawable/activated_background_material</item>
         <item>@drawable/btn_borderless_material</item>
         <item>@drawable/btn_check_material_anim</item>
         <item>@drawable/btn_colored_material</item>
         <item>@drawable/btn_default_material</item>
-        <item>@drawable/btn_group_holo_dark</item>
-        <item>@drawable/btn_group_holo_light</item>
         <item>@drawable/btn_radio_material_anim</item>
-        <item>@drawable/btn_star_material</item>
         <item>@drawable/btn_toggle_material</item>
-        <item>@drawable/button_inset</item>
-        <item>@drawable/cab_background_bottom_material</item>
-        <item>@drawable/cab_background_top_material</item>
         <item>@drawable/control_background_32dp_material</item>
         <item>@drawable/control_background_40dp_material</item>
         <item>@drawable/dialog_background_material</item>
-        <item>@drawable/editbox_dropdown_background_dark</item>
-        <item>@drawable/edit_text_material</item>
-        <item>@drawable/expander_group_material</item>
         <item>@drawable/fastscroll_label_left_material</item>
         <item>@drawable/fastscroll_label_right_material</item>
         <item>@drawable/fastscroll_thumb_material</item>
         <item>@drawable/fastscroll_track_material</item>
         <item>@drawable/floating_popup_background_dark</item>
         <item>@drawable/floating_popup_background_light</item>
-        <item>@drawable/gallery_item_background</item>
         <item>@drawable/ic_ab_back_material</item>
         <item>@drawable/ic_ab_back_material_dark</item>
         <item>@drawable/ic_ab_back_material_light</item>
         <item>@drawable/ic_account_circle</item>
         <item>@drawable/ic_arrow_drop_right_black_24dp</item>
-        <item>@drawable/ic_clear</item>
-        <item>@drawable/ic_clear_disabled</item>
         <item>@drawable/ic_clear_material</item>
-        <item>@drawable/ic_clear_normal</item>
-        <item>@drawable/ic_commit_search_api_material</item>
         <item>@drawable/ic_dialog_alert_material</item>
-        <item>@drawable/ic_find_next_material</item>
-        <item>@drawable/ic_find_previous_material</item>
-        <item>@drawable/ic_go</item>
         <item>@drawable/ic_go_search_api_material</item>
-        <item>@drawable/ic_media_route_connecting_dark_material</item>
-        <item>@drawable/ic_media_route_dark_material</item>
-        <item>@drawable/ic_media_route_light_material</item>
-        <item>@drawable/ic_menu_close_clear_cancel</item>
         <item>@drawable/ic_menu_copy_material</item>
         <item>@drawable/ic_menu_cut_material</item>
-        <item>@drawable/ic_menu_find_material</item>
-        <item>@drawable/ic_menu_more</item>
         <item>@drawable/ic_menu_moreoverflow_material</item>
         <item>@drawable/ic_menu_paste_material</item>
-        <item>@drawable/ic_menu_search_material</item>
         <item>@drawable/ic_menu_selectall_material</item>
         <item>@drawable/ic_menu_share_material</item>
         <item>@drawable/ic_search_api_material</item>
         <item>@drawable/ic_voice_search_api_material</item>
-        <item>@drawable/indicator_check_mark_dark</item>
-        <item>@drawable/indicator_check_mark_light</item>
         <item>@drawable/item_background_borderless_material</item>
         <item>@drawable/item_background_borderless_material_dark</item>
         <item>@drawable/item_background_borderless_material_light</item>
@@ -91,30 +63,15 @@
         <item>@drawable/item_background_material_light</item>
         <item>@drawable/list_choice_background_material</item>
         <item>@drawable/list_divider_material</item>
-        <item>@drawable/list_section_divider_material</item>
-        <item>@drawable/menu_background_fill_parent_width</item>
         <item>@drawable/notification_material_action_background</item>
         <item>@drawable/notification_material_media_action_background</item>
         <item>@drawable/number_picker_divider_material</item>
         <item>@drawable/popup_background_material</item>
-        <item>@drawable/popup_inline_error_above_holo_dark</item>
-        <item>@drawable/popup_inline_error_above_holo_light</item>
-        <item>@drawable/popup_inline_error_holo_dark</item>
-        <item>@drawable/popup_inline_error_holo_light</item>
         <item>@drawable/progress_horizontal_material</item>
         <item>@drawable/progress_indeterminate_horizontal_material</item>
         <item>@drawable/progress_large_material</item>
         <item>@drawable/progress_medium_material</item>
         <item>@drawable/progress_small_material</item>
-        <item>@drawable/quickcontact_badge_overlay_dark</item>
-        <item>@drawable/quickcontact_badge_overlay_light</item>
-        <item>@drawable/quickcontact_badge_overlay_normal_dark</item>
-        <item>@drawable/quickcontact_badge_overlay_normal_light</item>
-        <item>@drawable/quickcontact_badge_overlay_pressed_dark</item>
-        <item>@drawable/quickcontact_badge_overlay_pressed_light</item>
-        <item>@drawable/ratingbar_indicator_material</item>
-        <item>@drawable/ratingbar_material</item>
-        <item>@drawable/ratingbar_small_material</item>
         <item>@drawable/screen_background_dark</item>
         <item>@drawable/screen_background_dark_transparent</item>
         <item>@drawable/screen_background_light</item>
@@ -126,17 +83,9 @@
         <item>@drawable/seekbar_tick_mark_material</item>
         <item>@drawable/seekbar_track_material</item>
         <item>@drawable/spinner_background_material</item>
-        <item>@drawable/spinner_textfield_background_material</item>
-        <item>@drawable/switch_thumb_material_anim</item>
         <item>@drawable/switch_track_material</item>
         <item>@drawable/tab_indicator_material</item>
         <item>@drawable/text_cursor_material</item>
-        <item>@drawable/text_edit_paste_window</item>
-        <item>@drawable/textfield_search_material</item>
-        <item>@drawable/text_select_handle_left_material</item>
-        <item>@drawable/text_select_handle_middle_material</item>
-        <item>@drawable/text_select_handle_right_material</item>
-        <item>@drawable/toast_frame</item>
     </array>
 
     <!-- Do not translate. These are all of the color state list resources that should be
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ceb7ccd..be1c4b8 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2230,11 +2230,19 @@
     <string-array name="config_disabledUntilUsedPreinstalledCarrierApps" translatable="false" />
 
     <!-- The list of classes that should be added to the notification ranking pipline.
-     See {@link com.android.server.notification.NotificationSignalExtractor} -->
+     See {@link com.android.server.notification.NotificationSignalExtractor}
+      If you add a new extractor to this list make sure to update
+      NotificationManagerService.handleRankingSort()-->
     <string-array name="config_notificationSignalExtractors">
+        <!-- many of the following extractors depend on the notification channel, so this
+        extractor must come first -->
+        <item>com.android.server.notification.NotificationChannelExtractor</item>
+        <item>com.android.server.notification.NotificationAdjustmentExtractor</item>
+        <!-- depends on AdjustmentExtractor-->
         <item>com.android.server.notification.ValidateNotificationPeople</item>
         <item>com.android.server.notification.PriorityExtractor</item>
         <item>com.android.server.notification.ImportanceExtractor</item>
+        <!-- depends on ImportanceExtractor-->
         <item>com.android.server.notification.NotificationIntrusivenessExtractor</item>
         <item>com.android.server.notification.VisibilityExtractor</item>
         <item>com.android.server.notification.BadgeExtractor</item>
diff --git a/core/tests/coretests/README b/core/tests/coretests/README
index aced441..ea282a0 100644
--- a/core/tests/coretests/README
+++ b/core/tests/coretests/README
@@ -28,7 +28,7 @@
 
 Next, install the resulting APK and run tests as you would normal JUnit tests:
 
-  adb install out/target/product/.../data/app/FrameworksCoreTests/FrameworksCoreTests.apk
+  adb install -r ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
   adb shell am instrument -w \
     com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
 
diff --git a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
index 42b06f5..68b9b00 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
@@ -29,13 +29,15 @@
 import android.net.wifi.WifiManager;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
-import android.os.UserHandle;
 import android.os.ParcelFileDescriptor.AutoCloseInputStream;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.test.InstrumentationTestCase;
 import android.util.Log;
 
+import libcore.io.Streams;
+
 import com.google.mockwebserver.MockResponse;
 import com.google.mockwebserver.MockWebServer;
 
@@ -54,8 +56,6 @@
 import java.util.Set;
 import java.util.concurrent.TimeoutException;
 
-import libcore.io.Streams;
-
 /**
  * Base class for Instrumented tests for the Download Manager.
  */
@@ -83,9 +83,6 @@
     protected static final int WAIT_FOR_DOWNLOAD_POLL_TIME = 1 * 1000;  // 1 second
     protected static final int MAX_WAIT_FOR_DOWNLOAD_TIME = 30 * 1000; // 30 seconds
 
-    protected static final int DOWNLOAD_TO_SYSTEM_CACHE = 1;
-    protected static final int DOWNLOAD_TO_DOWNLOAD_CACHE_DIR = 2;
-
     // Just a few popular file types used to return from a download
     protected enum DownloadFileType {
         PLAINTEXT,
@@ -923,13 +920,13 @@
      * @param body The body to return in the response from the server
      */
     protected long doStandardEnqueue(byte[] body) throws Exception {
-        return enqueueDownloadRequest(body, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
+        return enqueueDownloadRequest(body);
     }
 
-    protected long enqueueDownloadRequest(byte[] body, int location) throws Exception {
+    protected long enqueueDownloadRequest(byte[] body) throws Exception {
         // Prepare the mock server with a standard response
         mServer.enqueue(buildResponse(HTTP_OK, body));
-        return doEnqueue(location);
+        return doEnqueue();
     }
 
     /**
@@ -938,13 +935,13 @@
      * @param body The body to return in the response from the server, contained in the file
      */
     protected long doStandardEnqueue(File body) throws Exception {
-        return enqueueDownloadRequest(body, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
+        return enqueueDownloadRequest(body);
     }
 
-    protected long enqueueDownloadRequest(File body, int location) throws Exception {
+    protected long enqueueDownloadRequest(File body) throws Exception {
         // Prepare the mock server with a standard response
         mServer.enqueue(buildResponse(HTTP_OK, body));
-        return doEnqueue(location);
+        return doEnqueue();
     }
 
     /**
@@ -952,16 +949,12 @@
      * doing a standard enqueue request to the server.
      */
     protected long doCommonStandardEnqueue() throws Exception {
-        return doEnqueue(DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
+        return doEnqueue();
     }
 
-    private long doEnqueue(int location) throws Exception {
+    private long doEnqueue() throws Exception {
         Uri uri = getServerUri(DEFAULT_FILENAME);
         Request request = new Request(uri).setTitle(DEFAULT_FILENAME);
-        if (location == DOWNLOAD_TO_SYSTEM_CACHE) {
-            request.setDestinationToSystemCache();
-        }
-
         return mDownloadManager.enqueue(request);
     }
 
@@ -1026,8 +1019,8 @@
     /**
      * Helper that does the actual basic download verification.
      */
-    protected long doBasicDownload(byte[] blobData, int location) throws Exception {
-        long dlRequest = enqueueDownloadRequest(blobData, location);
+    protected long doBasicDownload(byte[] blobData) throws Exception {
+        long dlRequest = enqueueDownloadRequest(blobData);
 
         // wait for the download to complete
         waitForDownloadOrTimeout(dlRequest);
diff --git a/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java b/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
index d1a5d28..c1d4be0 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
@@ -23,12 +23,13 @@
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.test.suitebuilder.annotation.LargeTest;
+
 import com.google.mockwebserver.MockResponse;
 
 import java.io.File;
-import java.util.concurrent.TimeoutException;
 import java.util.Iterator;
 import java.util.Set;
+import java.util.concurrent.TimeoutException;
 
 /**
  * Integration tests of the DownloadManager API.
@@ -95,11 +96,11 @@
      * Test a basic download of a binary file 500k in size.
      */
     @LargeTest
-    public void testBinaryDownloadToSystemCache() throws Exception {
+    public void testBinaryDownload() throws Exception {
         int fileSize = 1024;
         byte[] blobData = generateData(fileSize, DataType.BINARY);
 
-        long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_SYSTEM_CACHE);
+        long dlRequest = doBasicDownload(blobData);
         verifyDownload(dlRequest, blobData);
         mDownloadManager.remove(dlRequest);
     }
@@ -108,11 +109,11 @@
      * Tests the basic downloading of a text file 300000 bytes in size.
      */
     @LargeTest
-    public void testTextDownloadToSystemCache() throws Exception {
+    public void testTextDownload() throws Exception {
         int fileSize = 1024;
         byte[] blobData = generateData(fileSize, DataType.TEXT);
 
-        long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_SYSTEM_CACHE);
+        long dlRequest = doBasicDownload(blobData);
         verifyDownload(dlRequest, blobData);
         mDownloadManager.remove(dlRequest);
     }
@@ -318,7 +319,7 @@
         int fileSize = 1024;
         byte[] blobData = generateData(fileSize, DataType.BINARY);
 
-        long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
+        long dlRequest = doBasicDownload(blobData);
         Cursor cursor = mDownloadManager.query(new Query().setFilterById(dlRequest));
         try {
             assertEquals("The count of downloads with this ID is not 1!", 1, cursor.getCount());
diff --git a/core/tests/coretests/src/android/app/DownloadManagerStressTest.java b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
index 9fa9131..39d9a8e 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
@@ -195,7 +195,7 @@
 
             // try to download 1MB file into /cache - and it should succeed
             byte[] blobData = generateData(DOWNLOAD_FILE_SIZE, DataType.TEXT);
-            long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_SYSTEM_CACHE);
+            long dlRequest = doBasicDownload(blobData);
             verifyAndCleanupSingleFileDownload(dlRequest, blobData);
         } finally {
             if (outFile != null) {
diff --git a/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java b/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java
index 9bbcd3d..70a0877 100644
--- a/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java
+++ b/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java
@@ -27,7 +27,6 @@
 /**
  * Tests for {@link DistroFormatVersion}.
  */
-// TODO(nfuller) Move to CTS once this class is part of the SystemApi. http://b/31008728
 public class DistroFormatVersionTest {
 
     @Test
diff --git a/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java b/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java
index 2fbc9a1..eecae46 100644
--- a/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java
+++ b/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java
@@ -27,7 +27,6 @@
 /**
  * Tests for {@link DistroRulesVersion}.
  */
-// TODO(nfuller) Move to CTS once this class is part of the SystemApi. http://b/31008728
 public class DistroRulesVersionTest {
 
     @Test
diff --git a/core/tests/coretests/src/android/app/timezone/RulesStateTest.java b/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
index 7f4819b..99abe24 100644
--- a/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
+++ b/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
@@ -29,7 +29,6 @@
 /**
  * Tests for {@link RulesState}.
  */
-// TODO(nfuller) Move to CTS once this class is part of the SystemApi. http://b/31008728
 public class RulesStateTest {
 
     @Test
diff --git a/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java b/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java
index e7a839c..91f8ebc 100644
--- a/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java
+++ b/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java
@@ -33,7 +33,6 @@
 /**
  * Tests for {@link RulesUpdaterContract}.
  */
-// TODO(nfuller) Move to CTS once this class is part of the SystemApi. http://b/31008728
 public class RulesUpdaterContractTest {
 
     @Test
diff --git a/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java b/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java
new file mode 100644
index 0000000..4d2a047
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import android.content.pm.PackageParser.Package;
+import android.os.Build;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+@SmallTest
+@RunWith(JUnit4.class)
+public class PackageBackwardCompatibilityTest {
+
+    private static final String ORG_APACHE_HTTP_LEGACY = "org.apache.http.legacy";
+
+    private static final String ANDROID_TEST_RUNNER = "android.test.runner";
+
+    private static final String ANDROID_TEST_MOCK = "android.test.mock";
+
+    private Package mPackage;
+
+    private static ArrayList<String> arrayList(String... strings) {
+        ArrayList<String> list = new ArrayList<>();
+        Collections.addAll(list, strings);
+        return list;
+    }
+
+    @Before
+    public void setUp() {
+        mPackage = new Package("org.package.name");
+        mPackage.applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+    }
+
+    @Test
+    public void null_usesLibraries() {
+        PackageBackwardCompatibility.modifySharedLibraries(mPackage);
+        assertNull("usesLibraries not updated correctly", mPackage.usesLibraries);
+    }
+
+    @Test
+    public void null_usesOptionalLibraries() {
+        PackageBackwardCompatibility.modifySharedLibraries(mPackage);
+        assertNull("usesOptionalLibraries not updated correctly", mPackage.usesOptionalLibraries);
+    }
+
+    @Test
+    public void remove_org_apache_http_legacy_from_usesLibraries() {
+        mPackage.usesLibraries = arrayList(ORG_APACHE_HTTP_LEGACY);
+        PackageBackwardCompatibility.modifySharedLibraries(mPackage);
+        assertNull("usesLibraries not updated correctly", mPackage.usesLibraries);
+    }
+
+    @Test
+    public void remove_org_apache_http_legacy_from_usesOptionalLibraries() {
+        mPackage.usesOptionalLibraries = arrayList(ORG_APACHE_HTTP_LEGACY);
+        PackageBackwardCompatibility.modifySharedLibraries(mPackage);
+        assertNull("usesOptionalLibraries not updated correctly", mPackage.usesOptionalLibraries);
+    }
+
+    @Test
+    public void android_test_runner_in_usesLibraries() {
+        mPackage.usesLibraries = arrayList(ANDROID_TEST_RUNNER);
+        PackageBackwardCompatibility.modifySharedLibraries(mPackage);
+        assertEquals("usesLibraries not updated correctly",
+                arrayList(ANDROID_TEST_RUNNER, ANDROID_TEST_MOCK),
+                mPackage.usesLibraries);
+    }
+
+    @Test
+    public void android_test_runner_in_usesOptionalLibraries() {
+        mPackage.usesOptionalLibraries = arrayList(ANDROID_TEST_RUNNER);
+        PackageBackwardCompatibility.modifySharedLibraries(mPackage);
+        assertEquals("usesOptionalLibraries not updated correctly",
+                arrayList(ANDROID_TEST_RUNNER, ANDROID_TEST_MOCK),
+                mPackage.usesOptionalLibraries);
+    }
+
+    @Test
+    public void android_test_runner_in_usesLibraries_android_test_mock_in_usesOptionalLibraries() {
+        mPackage.usesLibraries = arrayList(ANDROID_TEST_RUNNER);
+        mPackage.usesOptionalLibraries = arrayList(ANDROID_TEST_MOCK);
+        PackageBackwardCompatibility.modifySharedLibraries(mPackage);
+        assertEquals("usesLibraries not updated correctly",
+                arrayList(ANDROID_TEST_RUNNER),
+                mPackage.usesLibraries);
+        assertEquals("usesOptionalLibraries not updated correctly",
+                arrayList(ANDROID_TEST_MOCK),
+                mPackage.usesOptionalLibraries);
+    }
+}
diff --git a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java b/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
index 5c497b4..55092fa 100644
--- a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
@@ -34,6 +34,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.UUID;
 
 import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.os.storage.VolumeInfo.STATE_MOUNTED;
@@ -90,14 +91,20 @@
         File internalFile = new File(sInternalVolPath);
         File adoptedFile = new File(sAdoptedVolPath);
         File publicFile = new File(sPublicVolPath);
+        UUID internalUuid = UUID.randomUUID();
+        UUID adoptedUuid = UUID.randomUUID();
+        UUID publicUuid = UUID.randomUUID();
         Mockito.when(storageManager.getStorageBytesUntilLow(internalFile)).thenReturn(sInternalSize);
         Mockito.when(storageManager.getStorageBytesUntilLow(adoptedFile)).thenReturn(sAdoptedSize);
         Mockito.when(storageManager.getStorageBytesUntilLow(publicFile)).thenReturn(sPublicSize);
-        Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(internalFile), Mockito.anyInt()))
+        Mockito.when(storageManager.getUuidForPath(Mockito.eq(internalFile))).thenReturn(internalUuid);
+        Mockito.when(storageManager.getUuidForPath(Mockito.eq(adoptedFile))).thenReturn(adoptedUuid);
+        Mockito.when(storageManager.getUuidForPath(Mockito.eq(publicFile))).thenReturn(publicUuid);
+        Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(internalUuid), Mockito.anyInt()))
                 .thenReturn(sInternalSize);
-        Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(adoptedFile), Mockito.anyInt()))
+        Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(adoptedUuid), Mockito.anyInt()))
                 .thenReturn(sAdoptedSize);
-        Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(publicFile), Mockito.anyInt()))
+        Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(publicUuid), Mockito.anyInt()))
                 .thenReturn(sPublicSize);
         return storageManager;
     }
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 0001d7a..949fd16 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -158,6 +158,7 @@
                     Settings.Global.DEBUG_VIEW_ATTRIBUTES,
                     Settings.Global.DEFAULT_DNS_SERVER,
                     Settings.Global.DEFAULT_INSTALL_LOCATION,
+                    Settings.Global.DEFAULT_RESTRICT_BACKGROUND_DATA,
                     Settings.Global.DESK_DOCK_SOUND,
                     Settings.Global.DESK_UNDOCK_SOUND,
                     Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index 4e8ab31..fa3d34a 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -25,8 +25,22 @@
 
 import junit.framework.TestCase;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 /**
  * Test various BatteryStatsImpl noteStart methods.
+ *
+ * Build/Install/Run: bit FrameworksCoreTests:com.android.internal.os.BatteryStatsNoteTest
+ *
+ * Alternatively,
+ * Build: m FrameworksCoreTests
+ * Install: adb install -r \
+ *      ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
+ * Run: adb shell am instrument -e class com.android.internal.os.BatteryStatsNoteTest -w \
+ *      com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
  */
 public class BatteryStatsNoteTest extends TestCase{
     private static final int UID = 10500;
@@ -86,4 +100,95 @@
         assertEquals(220_000, actualTime);
         assertEquals(120_000, bgTime);
     }
+
+
+    /** Test BatteryStatsImpl.noteUidProcessStateLocked. */
+    @SmallTest
+    public void testNoteUidProcessStateLocked() throws Exception {
+        final MockClocks clocks = new MockClocks();
+        MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+
+        // map of ActivityManager process states and how long to simulate run time in each state
+        Map<Integer, Integer> stateRuntimeMap = new HashMap<Integer, Integer>();
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_TOP, 1111);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE, 1234);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 2468);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_TOP_SLEEPING, 7531);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 4455);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 1337);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_BACKUP, 90210);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_HEAVY_WEIGHT, 911);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_SERVICE, 404);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_RECEIVER, 31459);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_HOME, 1123);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_LAST_ACTIVITY, 5813);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY, 867);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT, 5309);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_CACHED_EMPTY, 42);
+
+        bi.updateTimeBasesLocked(true, false, 0, 0);
+
+        for (Map.Entry<Integer, Integer> entry : stateRuntimeMap.entrySet()) {
+            bi.noteUidProcessStateLocked(UID, entry.getKey());
+            clocks.realtime += entry.getValue();
+            clocks.uptime = clocks.realtime;
+        }
+
+        long actualRunTimeUs;
+        long expectedRunTimeMs;
+        long elapsedTimeUs = clocks.realtime * 1000;
+        BatteryStats.Uid uid = bi.getUidStats().get(UID);
+
+        // compare runtime of process states to the Uid process states they map to
+        actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_TOP, elapsedTimeUs,
+                STATS_SINCE_CHARGED);
+        expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_TOP);
+        assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs);
+
+
+        actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE,
+                elapsedTimeUs, STATS_SINCE_CHARGED);
+        expectedRunTimeMs = stateRuntimeMap.get(
+                ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE)
+                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs);
+
+
+        actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING,
+                elapsedTimeUs, STATS_SINCE_CHARGED);
+        expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+        assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs);
+
+
+        actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_FOREGROUND,
+                elapsedTimeUs, STATS_SINCE_CHARGED);
+        expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
+        assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs);
+
+
+        actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_BACKGROUND,
+                elapsedTimeUs, STATS_SINCE_CHARGED);
+        expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND)
+                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_BACKUP)
+                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_HEAVY_WEIGHT)
+                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_SERVICE)
+                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_RECEIVER);
+        assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs);
+
+
+        actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_CACHED,
+                elapsedTimeUs, STATS_SINCE_CHARGED);
+        expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_HOME)
+                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_LAST_ACTIVITY)
+                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY)
+                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT)
+                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+        assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs);
+
+        // Special check for foreground service timer
+        actualRunTimeUs = uid.getForegroundServiceTimer().getTotalTimeLocked(elapsedTimeUs,
+                STATS_SINCE_CHARGED);
+        expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs);
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java
index 4a23f40..27aec56 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java
@@ -114,7 +114,7 @@
     public void testOnTimeStarted() {
         initializeCounterArrayWithDefaultValues();
         mCounterArray.onTimeStarted(0, 0, 0);
-        assertArrayEquals(PLUGGED_COUNTS, mCounterArray.mCounts, "Unexpected counts");
+        assertArrayEquals(COUNTS, mCounterArray.mCounts, "Unexpected counts");
         assertArrayEquals(LOADED_COUNTS, mCounterArray.mLoadedCounts, "Unexpected loadedCounts");
         assertArrayEquals(PLUGGED_COUNTS, mCounterArray.mPluggedCounts, "Unexpected pluggedCounts");
         assertArrayEquals(PLUGGED_COUNTS, mCounterArray.mUnpluggedCounts,
@@ -150,6 +150,7 @@
     @Test
     public void testAddCountLocked() {
         final long[] deltas = {123, 234, 345, 456};
+        when(mTimeBase.isRunning()).thenReturn(true);
         mCounterArray.addCountLocked(deltas);
         assertArrayEquals(deltas, mCounterArray.mCounts, "Unexpected counts");
         assertArrayEquals(null, mCounterArray.mLoadedCounts, "Unexpected loadedCounts");
diff --git a/core/tests/utiltests/AndroidManifest.xml b/core/tests/utiltests/AndroidManifest.xml
index da09894..b0c3b3c 100644
--- a/core/tests/utiltests/AndroidManifest.xml
+++ b/core/tests/utiltests/AndroidManifest.xml
@@ -30,7 +30,6 @@
 
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
-    <uses-permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.MANAGE_USERS" />
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 85ea1ff..19007f9 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -93,7 +93,7 @@
     </permission>
 
     <!-- Group that can modify how network statistics are accounted -->
-    <permission name="android.permission.MODIFY_NETWORK_ACCOUNTING">
+    <permission name="android.permission.UPDATE_DEVICE_STATS">
         <group gid="net_bw_acct" />
     </permission>
 
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index a79376c..e9e2e8a 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -186,7 +186,6 @@
         <permission name="android.permission.ACCESS_CACHE_FILESYSTEM"/>
         <permission name="android.permission.CLEAR_APP_CACHE"/>
         <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
-        <permission name="android.permission.MODIFY_NETWORK_ACCOUNTING"/>
         <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
         <permission name="android.permission.UPDATE_DEVICE_STATS"/>
     </privapp-permissions>
diff --git a/media/jni/midi/android_media_midi_MidiDevice.cpp b/media/jni/midi/android_media_midi_MidiDevice.cpp
index 4df8436..5b35453 100644
--- a/media/jni/midi/android_media_midi_MidiDevice.cpp
+++ b/media/jni/midi/android_media_midi_MidiDevice.cpp
@@ -18,8 +18,8 @@
 #define LOG_TAG "Midi-JNI"
 
 #include <android_util_Binder.h>
+#include <jni.h>
 #include <midi_internal.h>
-#include <nativehelper/jni.h>
 #include <utils/Log.h>
 
 using namespace android;
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index 4b9415e..91e23dd 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -36,6 +36,8 @@
     <uses-permission android:name="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"/>
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+    <uses-permission android:name="android.permission.READ_PRINT_SERVICES" />
+    <uses-permission android:name="android.permission.READ_PRINT_SERVICE_RECOMMENDATIONS" />
 
     <application
         android:allowClearUserData="true"
diff --git a/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java b/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java
index 3640bfa..c346898 100644
--- a/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java
+++ b/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java
@@ -151,7 +151,7 @@
 
     public int getPolicyCycleDay(NetworkTemplate template) {
         final NetworkPolicy policy = getPolicy(template);
-        return (policy != null) ? policy.cycleDay : -1;
+        return (policy != null) ? policy.cycleDay : CYCLE_NONE;
     }
 
     public void setPolicyCycleDay(NetworkTemplate template, int cycleDay, String cycleTimezone) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
new file mode 100644
index 0000000..75b6696
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.development;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v4.content.LocalBroadcastManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.support.v7.preference.TwoStatePreference;
+import android.text.TextUtils;
+
+import com.android.settingslib.core.AbstractPreferenceController;
+
+public abstract class AbstractEnableAdbPreferenceController extends AbstractPreferenceController {
+    private static final String KEY_ENABLE_ADB = "enable_adb";
+    public static final String ACTION_ENABLE_ADB_STATE_CHANGED =
+            "com.android.settingslib.development.AbstractEnableAdbController."
+                    + "ENABLE_ADB_STATE_CHANGED";
+
+    private SwitchPreference mPreference;
+
+    public AbstractEnableAdbPreferenceController(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        if (isAvailable()) {
+            mPreference = (SwitchPreference) screen.findPreference(KEY_ENABLE_ADB);
+        }
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return mContext.getSystemService(UserManager.class).isAdminUser();
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_ENABLE_ADB;
+    }
+
+    private boolean isAdbEnabled() {
+        final ContentResolver cr = mContext.getContentResolver();
+        return Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0) != 0;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        ((TwoStatePreference)preference).setChecked(isAdbEnabled());
+    }
+
+    public void enablePreference(boolean enabled) {
+        if (isAvailable()) {
+            mPreference.setEnabled(enabled);
+        }
+    }
+
+    public void resetPreference() {
+        if (mPreference.isChecked()) {
+            mPreference.setChecked(false);
+            handlePreferenceTreeClick(mPreference);
+        }
+    }
+
+    public boolean haveDebugSettings() {
+        return isAdbEnabled();
+    }
+
+    @Override
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        if (TextUtils.equals(KEY_ENABLE_ADB, preference.getKey())) {
+            if (!isAdbEnabled()) {
+                showConfirmationDialog((SwitchPreference) preference);
+            } else {
+                writeAdbSetting(false);
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    protected void writeAdbSetting(boolean enabled) {
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.ADB_ENABLED, enabled ? 1 : 0);
+        notifyStateChanged();
+    }
+
+    protected void notifyStateChanged() {
+        LocalBroadcastManager.getInstance(mContext)
+                .sendBroadcast(new Intent(ACTION_ENABLE_ADB_STATE_CHANGED));
+    }
+
+    public abstract void showConfirmationDialog(SwitchPreference preference);
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index b69232c..ed3696c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -16,6 +16,14 @@
 
 package com.android.settingslib.net;
 
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
+import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
+import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.telephony.TelephonyManager.SIM_STATE_READY;
+import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
+import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
+
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.INetworkStatsService;
@@ -29,22 +37,15 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.format.DateUtils;
-import android.text.format.Time;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.internal.R;
 
+import java.time.ZonedDateTime;
 import java.util.Date;
 import java.util.Locale;
 
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
-import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
-import static android.telephony.TelephonyManager.SIM_STATE_READY;
-import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
-import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
-import static android.net.TrafficStats.MB_IN_BYTES;
-
 public class DataUsageController {
 
     private static final String TAG = "DataUsageController";
@@ -107,13 +108,6 @@
         return null;
     }
 
-    private static Time addMonth(Time t, int months) {
-        final Time rt = new Time(t);
-        rt.set(t.monthDay, t.month + months, t.year);
-        rt.normalize(false);
-        return rt;
-    }
-
     public DataUsageInfo getDataUsageInfo() {
         final String subscriberId = getActiveSubscriberId(mContext);
         if (subscriberId == null) {
@@ -140,22 +134,11 @@
             final NetworkStatsHistory history = session.getHistoryForNetwork(template, FIELDS);
             final long now = System.currentTimeMillis();
             final long start, end;
-            if (policy != null && policy.cycleDay > 0) {
-                // period = determined from cycleDay
-                if (DEBUG) Log.d(TAG, "Cycle day=" + policy.cycleDay + " tz="
-                        + policy.cycleTimezone);
-                final Time nowTime = new Time(policy.cycleTimezone);
-                nowTime.setToNow();
-                final Time policyTime = new Time(nowTime);
-                policyTime.set(policy.cycleDay, policyTime.month, policyTime.year);
-                policyTime.normalize(false);
-                if (nowTime.after(policyTime)) {
-                    start = policyTime.toMillis(false);
-                    end = addMonth(policyTime, 1).toMillis(false);
-                } else {
-                    start = addMonth(policyTime, -1).toMillis(false);
-                    end = policyTime.toMillis(false);
-                }
+            if (policy != null) {
+                final Pair<ZonedDateTime, ZonedDateTime> cycle = NetworkPolicyManager
+                        .cycleIterator(policy).next();
+                start = cycle.first.toInstant().toEpochMilli();
+                end = cycle.second.toInstant().toEpochMilli();
             } else {
                 // period = last 4 wks
                 end = now;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index c35c046..e0a88e4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -1166,8 +1166,9 @@
 
             if (nc != null) {
                 if (nc.hasCapability(nc.NET_CAPABILITY_CAPTIVE_PORTAL)) {
-                    return context.getString(
-                        com.android.internal.R.string.network_available_sign_in);
+                    int id = context.getResources()
+                            .getIdentifier("network_available_sign_in", "string", "android");
+                    return context.getString(id);
                 } else if (!nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
                     return context.getString(R.string.wifi_connected_no_internet);
                 }
diff --git a/packages/SettingsLib/tests/robotests/Android.mk b/packages/SettingsLib/tests/robotests/Android.mk
index eca2052..55b635e 100644
--- a/packages/SettingsLib/tests/robotests/Android.mk
+++ b/packages/SettingsLib/tests/robotests/Android.mk
@@ -41,7 +41,7 @@
 
 # Include the testing libraries (JUnit4 + Robolectric libs).
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    platform-system-robolectric \
+    mockito-robolectric-prebuilt \
     truth-prebuilt
 
 LOCAL_JAVA_LIBRARIES := \
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
new file mode 100644
index 0000000..0778b27
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.development;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settingslib.SettingLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class EnableAdbPreferenceControllerTest {
+    @Mock(answer = RETURNS_DEEP_STUBS)
+    private PreferenceScreen mScreen;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private PackageManager mPackageManager;
+
+    private Context mContext;
+    private SwitchPreference mPreference;
+    private ConcreteEnableAdbPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowContext = ShadowApplication.getInstance();
+        shadowContext.setSystemService(Context.USER_SERVICE, mUserManager);
+        mContext = spy(shadowContext.getApplicationContext());
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        mPreference = new SwitchPreference(mContext);
+        when(mScreen.findPreference(anyString())).thenReturn(mPreference);
+        mController = new ConcreteEnableAdbPreferenceController(mContext);
+        mPreference.setKey(mController.getPreferenceKey());
+    }
+
+    @Test
+    public void displayPreference_isNotAdmin_shouldRemovePreference() {
+        when(mUserManager.isAdminUser()).thenReturn(false);
+        when(mScreen.getPreferenceCount()).thenReturn(1);
+        when(mScreen.getPreference(0)).thenReturn(mPreference);
+
+        mController.displayPreference(mScreen);
+
+        verify(mScreen).removePreference(any(Preference.class));
+    }
+
+    @Test
+    public void displayPreference_isAdmin_shouldNotRemovePreference() {
+        when(mUserManager.isAdminUser()).thenReturn(true);
+
+        mController.displayPreference(mScreen);
+
+        verify(mScreen, never()).removePreference(any(Preference.class));
+    }
+
+
+    @Test
+    public void resetPreference_shouldUncheck() {
+        when(mUserManager.isAdminUser()).thenReturn(true);
+        mController.displayPreference(mScreen);
+        mPreference.setChecked(true);
+
+        mController.resetPreference();
+
+        assertThat(mPreference.isChecked()).isFalse();
+    }
+
+    @Test
+    public void handlePreferenceTreeClick_shouldUpdateSettings() {
+        when(mUserManager.isAdminUser()).thenReturn(true);
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Global.ADB_ENABLED, 1);
+        mPreference.setChecked(true);
+        mController.displayPreference(mScreen);
+
+        mController.handlePreferenceTreeClick(mPreference);
+
+        assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Global.ADB_ENABLED, 0)).isEqualTo(0);
+    }
+
+    @Test
+    public void updateState_settingsOn_shouldCheck() {
+        when(mUserManager.isAdminUser()).thenReturn(true);
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Global.ADB_ENABLED, 1);
+        mPreference.setChecked(false);
+        mController.displayPreference(mScreen);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isChecked()).isTrue();
+    }
+
+    @Test
+    public void updateState_settingsOff_shouldUncheck() {
+        when(mUserManager.isAdminUser()).thenReturn(true);
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Global.ADB_ENABLED, 0);
+        mPreference.setChecked(true);
+        mController.displayPreference(mScreen);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isChecked()).isFalse();
+    }
+
+    class ConcreteEnableAdbPreferenceController extends AbstractEnableAdbPreferenceController {
+        public ConcreteEnableAdbPreferenceController(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void showConfirmationDialog(SwitchPreference preference) {
+            // Don't show a dialog, just set setting.
+            writeAdbSetting(true);
+        }
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index 0364418..e9ca753 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -18,11 +18,12 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -59,7 +60,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
@@ -100,6 +100,7 @@
         MockitoAnnotations.initMocks(this);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         when(mPackageManager.getResourcesForApplication(anyString())).thenReturn(mResources);
+        when(mPackageManager.getResourcesForApplication((String) isNull())).thenReturn(mResources);
         when(mPackageManager.getApplicationInfo(eq("abc"), anyInt()))
                 .thenReturn(application.getApplicationInfo());
         mContentResolver = spy(application.getContentResolver());
@@ -115,7 +116,6 @@
         List<Tile> outTiles = new ArrayList<>();
         List<ResolveInfo> info = new ArrayList<>();
         info.add(newInfo(true, testCategory));
-        Map<Pair<String, String>, Tile> cache = new ArrayMap<>();
         when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
                 .thenReturn(info);
 
@@ -169,7 +169,6 @@
 
     @Test
     public void getTilesForIntent_shouldSkipFilteredApps() {
-        final String testCategory = "category1";
         Intent intent = new Intent();
         Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
         List<Tile> outTiles = new ArrayList<>();
@@ -210,11 +209,9 @@
         userHandleList.add(UserHandle.CURRENT);
         when(mUserManager.getUserProfiles()).thenReturn(userHandleList);
 
-        when(mPackageManager.queryIntentActivitiesAsUser(argThat(new ArgumentMatcher<Intent>() {
-            public boolean matches(Object event) {
-                return testAction.equals(((Intent) event).getAction());
-            }
-        }), anyInt(), anyInt())).thenReturn(info);
+        when(mPackageManager.queryIntentActivitiesAsUser(argThat(
+                event -> testAction.equals(event.getAction())), anyInt(), anyInt()))
+                .thenReturn(info);
 
         List<DashboardCategory> categoryList = TileUtils.getCategories(
                 mContext, cache, false /* categoryDefinedInManifest */, testAction,
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index e9dadc5..4007cc9 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -187,4 +187,7 @@
 
     <!--  default setting for Settings.System.END_BUTTON_BEHAVIOR : END_BUTTON_BEHAVIOR_SLEEP -->
     <integer name="def_end_button_behavior">0x2</integer>
+
+    <!-- default setting for Settings.Global.DEFAULT_RESTRICT_BACKGROUND_DATA -->
+    <bool name="def_restrict_background_data">false</bool>
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 1bd58ad..15e6e45 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -32,8 +32,6 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.database.Cursor;
@@ -61,7 +59,7 @@
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.provider.Settings;
-import android.service.notification.NotificationListenerService;
+import android.provider.Settings.Global;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -82,7 +80,6 @@
 import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
 import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
 import java.nio.ByteBuffer;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
@@ -2895,7 +2892,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 146;
+            private static final int SETTINGS_VERSION = 147;
 
             private final int mUserId;
 
@@ -3422,6 +3419,23 @@
                     currentVersion = 146;
                 }
 
+                if (currentVersion == 146) {
+                    // Version 146: Set the default value for DEFAULT_RESTRICT_BACKGROUND_DATA.
+                    if (userId == UserHandle.USER_SYSTEM) {
+                        final SettingsState globalSettings = getGlobalSettingsLocked();
+                        final Setting currentSetting = globalSettings.getSettingLocked(
+                                Global.DEFAULT_RESTRICT_BACKGROUND_DATA);
+                        if (currentSetting.isNull()) {
+                            globalSettings.insertSettingLocked(
+                                    Global.DEFAULT_RESTRICT_BACKGROUND_DATA,
+                                    getContext().getResources().getBoolean(
+                                            R.bool.def_restrict_background_data) ? "1" : "0",
+                                    null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
+                    }
+                    currentVersion = 147;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index 44fae86..ee14f3d 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -28,41 +28,46 @@
     androidprv:layout_maxWidth="@dimen/keyguard_security_width"
     androidprv:layout_maxHeight="@dimen/keyguard_security_height"
     android:gravity="center_horizontal|top">
-    <RelativeLayout
-        android:id="@+id/keyguard_clock_container"
+    <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal|top">
-        <TextClock
-            android:id="@+id/clock_view"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_horizontal"
-            android:layout_centerHorizontal="true"
-            android:layout_alignParentTop="true"
-            android:textColor="?attr/bgProtectTextColor"
-            android:singleLine="true"
-            style="@style/widget_big_thin"
-            android:format12Hour="@string/keyguard_widget_12_hours_format"
-            android:format24Hour="@string/keyguard_widget_24_hours_format"
-            android:layout_marginBottom="@dimen/bottom_text_spacing_digital" />
-        <com.android.systemui.ChargingView
-            android:id="@+id/battery_doze"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_alignTop="@id/clock_view"
-            android:layout_alignBottom="@id/clock_view"
-            android:layout_toEndOf="@id/clock_view"
-            android:visibility="invisible"
-            android:src="@drawable/ic_aod_charging_24dp"
-            android:contentDescription="@string/accessibility_ambient_display_charging"
-        />
-
-        <include layout="@layout/keyguard_status_area"
-            android:id="@+id/keyguard_status_area"
+        android:orientation="vertical">
+        <RelativeLayout
+            android:id="@+id/keyguard_clock_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_below="@id/clock_view" />
+            android:layout_gravity="center_horizontal|top">
+            <TextClock
+                android:id="@+id/clock_view"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal"
+                android:layout_centerHorizontal="true"
+                android:layout_alignParentTop="true"
+                android:textColor="?attr/bgProtectTextColor"
+                android:singleLine="true"
+                style="@style/widget_big_thin"
+                android:format12Hour="@string/keyguard_widget_12_hours_format"
+                android:format24Hour="@string/keyguard_widget_24_hours_format"
+                android:layout_marginBottom="@dimen/bottom_text_spacing_digital" />
+            <com.android.systemui.ChargingView
+                android:id="@+id/battery_doze"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignTop="@id/clock_view"
+                android:layout_alignBottom="@id/clock_view"
+                android:layout_toEndOf="@id/clock_view"
+                android:visibility="invisible"
+                android:src="@drawable/ic_aod_charging_24dp"
+                android:contentDescription="@string/accessibility_ambient_display_charging"
+            />
+
+            <include layout="@layout/keyguard_status_area"
+                android:id="@+id/keyguard_status_area"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@id/clock_view" />
+        </RelativeLayout>
 
         <TextView
             android:id="@+id/owner_info"
@@ -72,12 +77,11 @@
             android:layout_height="wrap_content"
             android:layout_marginTop="@dimen/date_owner_info_margin"
             android:layout_centerHorizontal="true"
-            android:layout_below="@id/keyguard_status_area"
             android:textColor="?attr/bgProtectSecondaryTextColor"
             android:textSize="@dimen/widget_label_font_size"
             android:letterSpacing="0.05"
             android:ellipsize="marquee"
             android:singleLine="true" />
 
-    </RelativeLayout>
+    </LinearLayout>
 </com.android.keyguard.KeyguardStatusView>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 5005f9d..820a773 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -24,6 +24,8 @@
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.PorterDuff;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.UserHandle;
 import android.support.v4.graphics.ColorUtils;
 import android.text.TextUtils;
@@ -48,6 +50,7 @@
 public class KeyguardStatusView extends GridLayout {
     private static final boolean DEBUG = KeyguardConstants.DEBUG;
     private static final String TAG = "KeyguardStatusView";
+    private static final int MARQUEE_DELAY_MS = 2000;
 
     private final LockPatternUtils mLockPatternUtils;
     private final AlarmManager mAlarmManager;
@@ -59,6 +62,8 @@
     private ViewGroup mClockContainer;
     private ChargingView mBatteryDoze;
     private View mKeyguardStatusArea;
+    private Runnable mPendingMarqueeStart;
+    private Handler mHandler;
 
     private View[] mVisibleInDoze;
     private boolean mPulsing;
@@ -112,9 +117,29 @@
         super(context, attrs, defStyle);
         mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
         mLockPatternUtils = new LockPatternUtils(getContext());
+        mHandler = new Handler(Looper.myLooper());
     }
 
     private void setEnableMarquee(boolean enabled) {
+        if (DEBUG) Log.v(TAG, "Schedule setEnableMarquee: " + (enabled ? "Enable" : "Disable"));
+        if (enabled) {
+            if (mPendingMarqueeStart == null) {
+                mPendingMarqueeStart = () -> {
+                    setEnableMarqueeImpl(true);
+                    mPendingMarqueeStart = null;
+                };
+                mHandler.postDelayed(mPendingMarqueeStart, MARQUEE_DELAY_MS);
+            }
+        } else {
+            if (mPendingMarqueeStart != null) {
+                mHandler.removeCallbacks(mPendingMarqueeStart);
+                mPendingMarqueeStart = null;
+            }
+            setEnableMarqueeImpl(false);
+        }
+    }
+
+    private void setEnableMarqueeImpl(boolean enabled) {
         if (DEBUG) Log.v(TAG, (enabled ? "Enable" : "Disable") + " transport text marquee");
         if (mAlarmStatusView != null) mAlarmStatusView.setSelected(enabled);
         if (mOwnerInfo != null) mOwnerInfo.setSelected(enabled);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
index dd3361b..686b3bb 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
@@ -29,7 +29,7 @@
  */
 public class PipTouchState {
     private static final String TAG = "PipTouchHandler";
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
 
     private ViewConfiguration mViewConfig;
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index fa16f8e..f2ea6a6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -337,7 +337,7 @@
         getWindow().getAttributes().privateFlags |=
                 WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
 
-        mLastConfig = Utilities.getAppConfiguration(this);
+        mLastConfig = new Configuration(Utilities.getAppConfiguration(this));
         mFocusTimerDuration = getResources().getInteger(R.integer.recents_auto_advance_duration);
         mIterateTrigger = new DozeTrigger(mFocusTimerDuration, new Runnable() {
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index a3e5e45..3e183b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -401,24 +401,6 @@
                 && pm.resolveActivity(PHONE_INTENT, 0) != null;
     }
 
-    private boolean isCameraDisabledByDpm() {
-        final DevicePolicyManager dpm =
-                (DevicePolicyManager) getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
-        if (dpm != null && mStatusBar != null) {
-            try {
-                final int userId = ActivityManager.getService().getCurrentUser().id;
-                final int disabledFlags = dpm.getKeyguardDisabledFeatures(null, userId);
-                final  boolean disabledBecauseKeyguardSecure =
-                        (disabledFlags & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0
-                                && mStatusBar.isKeyguardSecure();
-                return dpm.getCameraDisabled(null) || disabledBecauseKeyguardSecure;
-            } catch (RemoteException e) {
-                Log.e(TAG, "Can't get userId", e);
-            }
-        }
-        return false;
-    }
-
     private void watchForCameraPolicyChanges() {
         final IntentFilter filter = new IntentFilter();
         filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
@@ -871,7 +853,8 @@
         @Override
         public IconState getIcon() {
             ResolveInfo resolved = resolveCameraIntent();
-            mIconState.isVisible = !isCameraDisabledByDpm() && resolved != null
+            boolean isCameraDisabled = (mStatusBar != null) && !mStatusBar.isCameraAllowedByAdmin();
+            mIconState.isVisible = !isCameraDisabled && resolved != null
                     && getResources().getBoolean(R.bool.config_keyguardShowCameraAffordance)
                     && mUserSetupComplete;
             mIconState.drawable = mContext.getDrawable(R.drawable.ic_camera_alt_24dp);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 3940a15..4ffc15f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -2499,6 +2499,10 @@
      * @param keyguardIsShowing whether keyguard is being shown
      */
     public boolean canCameraGestureBeLaunched(boolean keyguardIsShowing) {
+        if (!mStatusBar.isCameraAllowedByAdmin()) {
+            return false;
+        }
+
         ResolveInfo resolveInfo = mKeyguardBottomArea.resolveCameraIntent();
         String packageToLaunch = (resolveInfo == null || resolveInfo.activityInfo == null)
                 ? null : resolveInfo.activityInfo.packageName;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 3f5f4da..2afdab8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1,5 +1,3 @@
-
-
 /*
  * Copyright (C) 2010 The Android Open Source Project
  *
@@ -104,6 +102,7 @@
 import android.os.UserManager;
 import android.os.Vibrator;
 import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.StatusBarNotification;
 import android.service.vr.IVrManager;
@@ -565,7 +564,7 @@
         }};
 
     private boolean mWaitingForKeyguardExit;
-    private boolean mDozing;
+    protected boolean mDozing;
     private boolean mDozingRequested;
     protected boolean mScrimSrcModeEnabled;
 
@@ -5280,6 +5279,18 @@
         }
     }
 
+    boolean isCameraAllowedByAdmin() {
+        if (mDevicePolicyManager.getCameraDisabled(null, mCurrentUserId)) {
+            return false;
+        } else if (isKeyguardShowing() && isKeyguardSecure()) {
+            // Check if the admin has disabled the camera specifically for the keyguard
+            return (mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUserId)
+                    & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) == 0;
+        }
+
+        return true;
+    }
+
     public void notifyFpAuthModeChanged() {
         updateDozing();
     }
@@ -5303,6 +5314,10 @@
     }
 
     public boolean isKeyguardShowing() {
+        if (mStatusBarKeyguardViewManager == null) {
+            Slog.i(TAG, "isKeyguardShowing() called before startKeyguard(), returning true");
+            return true;
+        }
         return mStatusBarKeyguardViewManager.isShowing();
     }
 
@@ -5578,6 +5593,10 @@
 
     private Set<String> mNonBlockablePkgs;
 
+    public boolean isDeviceInteractive() {
+        return mDeviceInteractive;
+    }
+
     @Override  // NotificationData.Environment
     public boolean isDeviceProvisioned() {
         return mDeviceProvisionedController.isDeviceProvisioned();
@@ -7227,7 +7246,12 @@
             return false;
         }
 
-        if (mNotificationData.shouldSuppressScreenOn(sbn.getKey())) {
+        if (!isDozing() && mNotificationData.shouldSuppressScreenOn(sbn.getKey())) {
+            if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
+            return false;
+        }
+
+        if (isDozing() && mNotificationData.shouldSuppressScreenOff(sbn.getKey())) {
             if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
             return false;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Util.java b/packages/SystemUI/src/com/android/systemui/volume/Util.java
index a46a44d..c6d6218 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/Util.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/Util.java
@@ -78,6 +78,7 @@
     }
 
     public static String mediaMetadataToString(MediaMetadata metadata) {
+        if (metadata == null) return null;
         return metadata.getDescription().toString();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index eaad2f9..103eb6e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -48,8 +48,10 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
+import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.plugins.VolumeDialogController;
 import com.android.systemui.qs.tiles.DndTile;
+import com.android.systemui.statusbar.phone.StatusBar;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -89,11 +91,12 @@
     private final W mWorker;
     private final Context mContext;
     private AudioManager mAudio;
+    protected StatusBar mStatusBar;
     private final NotificationManager mNoMan;
     private final SettingObserver mObserver;
     private final Receiver mReceiver = new Receiver();
     private final MediaSessions mMediaSessions;
-    private final C mCallbacks = new C();
+    protected C mCallbacks = new C();
     private final State mState = new State();
     private final MediaSessionsCallbacks mMediaSessionsCallbacksW = new MediaSessionsCallbacks();
     private final Vibrator mVibrator;
@@ -123,6 +126,7 @@
         mReceiver.init();
         mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
         mHasVibrator = mVibrator != null && mVibrator.hasVibrator();
+        updateStatusBar();
 
         boolean accessibilityVolumeStreamActive = context.getSystemService(
                 AccessibilityManager.class).isAccessibilityVolumeStreamActive();
@@ -326,8 +330,17 @@
         return changed;
     }
 
-    private boolean onVolumeChangedW(int stream, int flags) {
-        final boolean showUI = (flags & AudioManager.FLAG_SHOW_UI) != 0;
+    private void updateStatusBar() {
+        if (mStatusBar == null) {
+            mStatusBar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
+        }
+    }
+
+    boolean onVolumeChangedW(int stream, int flags) {
+        updateStatusBar();
+
+        final boolean showUI = (mStatusBar != null && mStatusBar.isDeviceInteractive()) &&
+                ((flags & AudioManager.FLAG_SHOW_UI) != 0);
         final boolean fromKey = (flags & AudioManager.FLAG_FROM_KEY) != 0;
         final boolean showVibrateHint = (flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0;
         final boolean showSilentHint = (flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0;
@@ -638,7 +651,7 @@
         }
     }
 
-    private final class C implements Callbacks {
+    class C implements Callbacks {
         private final HashMap<Callbacks, Handler> mCallbackMap = new HashMap<>();
 
         public void add(Callbacks callback, Handler handler) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 8a4983c..c33897e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -334,6 +334,83 @@
     }
 
     @Test
+    public void testShouldPeek_suppressedScreenOn_dozing() {
+        when(mPowerManager.isScreenOn()).thenReturn(true);
+        when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
+        when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+        when(mSystemServicesProxy.isDreaming()).thenReturn(false);
+        when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
+
+        mStatusBar.mDozing = true;
+        when(mNotificationData.shouldSuppressScreenOn(any())).thenReturn(true);
+        when(mNotificationData.shouldSuppressScreenOff(any())).thenReturn(false);
+
+        Notification n = new Notification.Builder(getContext(), "a").build();
+        StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
+                UserHandle.of(0), null, 0);
+        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+
+        assertTrue(mStatusBar.shouldPeek(entry, sbn));
+    }
+
+    @Test
+    public void testShouldPeek_suppressedScreenOn_noDoze() {
+        when(mPowerManager.isScreenOn()).thenReturn(true);
+        when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
+        when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+        when(mSystemServicesProxy.isDreaming()).thenReturn(false);
+        when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
+
+        mStatusBar.mDozing = false;
+        when(mNotificationData.shouldSuppressScreenOn(any())).thenReturn(true);
+        when(mNotificationData.shouldSuppressScreenOff(any())).thenReturn(false);
+
+        Notification n = new Notification.Builder(getContext(), "a").build();
+        StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
+                UserHandle.of(0), null, 0);
+        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        assertFalse(mStatusBar.shouldPeek(entry, sbn));
+    }
+    @Test
+    public void testShouldPeek_suppressedScreenOff_dozing() {
+        when(mPowerManager.isScreenOn()).thenReturn(true);
+        when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
+        when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+        when(mSystemServicesProxy.isDreaming()).thenReturn(false);
+        when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
+
+        mStatusBar.mDozing = true;
+        when(mNotificationData.shouldSuppressScreenOn(any())).thenReturn(false);
+        when(mNotificationData.shouldSuppressScreenOff(any())).thenReturn(true);
+
+        Notification n = new Notification.Builder(getContext(), "a").build();
+        StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
+                UserHandle.of(0), null, 0);
+        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        assertFalse(mStatusBar.shouldPeek(entry, sbn));
+    }
+
+    @Test
+    public void testShouldPeek_suppressedScreenOff_noDoze() {
+        when(mPowerManager.isScreenOn()).thenReturn(true);
+        when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
+        when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+        when(mSystemServicesProxy.isDreaming()).thenReturn(false);
+        when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
+
+        mStatusBar.mDozing = false;
+        when(mNotificationData.shouldSuppressScreenOn(any())).thenReturn(false);
+        when(mNotificationData.shouldSuppressScreenOff(any())).thenReturn(true);
+
+        Notification n = new Notification.Builder(getContext(), "a").build();
+        StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
+                UserHandle.of(0), null, 0);
+        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        assertTrue(mStatusBar.shouldPeek(entry, sbn));
+    }
+
+
+    @Test
     public void testLogHidden() {
         try {
             mStatusBar.handleVisibleToUserChanged(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/UtilTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/UtilTest.java
new file mode 100644
index 0000000..6b20a1e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/UtilTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.volume;
+
+import android.media.MediaMetadata;
+import android.support.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+@SmallTest
+public class UtilTest extends SysuiTestCase {
+
+    @Test
+    public void testMediaMetadataToString_null() {
+        Assert.assertEquals(null, Util.mediaMetadataToString(null));
+    }
+
+    @Test
+    public void testMediaMetadataToString_notNull() {
+        Assert.assertNotNull(Util.mediaMetadataToString(new MediaMetadata.Builder().build()));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
new file mode 100644
index 0000000..8060f5b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.support.test.filters.SmallTest;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@SmallTest
+public class VolumeDialogControllerImplTest extends SysuiTestCase {
+
+    TestableVolumeDialogControllerImpl mVolumeController;
+    VolumeDialogControllerImpl.C mCallback;
+    StatusBar mStatusBar;
+
+    @Before
+    public void setup() throws Exception {
+        mCallback = mock(VolumeDialogControllerImpl.C.class);
+        mStatusBar = mock(StatusBar.class);
+        mVolumeController = new TestableVolumeDialogControllerImpl(mContext, mCallback, mStatusBar);
+    }
+
+    @Test
+    public void testVolumeChangeW_deviceNotInteractiveAOD() {
+        when(mStatusBar.isDeviceInteractive()).thenReturn(false);
+        mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
+        verify(mCallback, never()).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
+    }
+
+    @Test
+    public void testVolumeChangeW_deviceInteractive() {
+        when(mStatusBar.isDeviceInteractive()).thenReturn(true);
+        mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
+        verify(mCallback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
+    }
+
+    static class TestableVolumeDialogControllerImpl extends VolumeDialogControllerImpl {
+        public TestableVolumeDialogControllerImpl(Context context, C callback, StatusBar s) {
+            super(context);
+            mCallbacks = callback;
+            mStatusBar = s;
+        }
+    }
+
+}
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index c760806..d118917 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -118,12 +118,9 @@
 import com.android.server.backup.utils.AppBackupUtils;
 import com.android.server.backup.utils.BackupManagerMonitorUtils;
 import com.android.server.backup.utils.BackupObserverUtils;
-import com.android.server.backup.utils.PasswordUtils;
 import com.android.server.backup.utils.SparseArrayUtils;
 import com.android.server.power.BatterySaverPolicy.ServiceType;
 
-import libcore.io.IoUtils;
-
 import com.google.android.collect.Sets;
 
 import java.io.BufferedInputStream;
@@ -802,8 +799,7 @@
 
         // Remember our ancestral dataset
         mTokenFile = new File(mBaseStateDir, "ancestral");
-        try {
-            RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r");
+        try (RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r")) {
             int version = tf.readInt();
             if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
                 mAncestralToken = tf.readLong();
@@ -818,7 +814,6 @@
                     }
                 }
             }
-            tf.close();
         } catch (FileNotFoundException fnf) {
             // Probably innocuous
             Slog.v(TAG, "No ancestral data");
@@ -842,13 +837,8 @@
         // If there are previous contents, parse them out then start a new
         // file to continue the recordkeeping.
         if (mEverStored.exists()) {
-            RandomAccessFile temp = null;
-            RandomAccessFile in = null;
-
-            try {
-                temp = new RandomAccessFile(tempProcessedFile, "rws");
-                in = new RandomAccessFile(mEverStored, "r");
-
+            try (RandomAccessFile temp = new RandomAccessFile(tempProcessedFile, "rws");
+                 RandomAccessFile in = new RandomAccessFile(mEverStored, "r")) {
                 // Loop until we hit EOF
                 while (true) {
                     String pkg = in.readUTF();
@@ -872,15 +862,6 @@
                 }
             } catch (IOException e) {
                 Slog.e(TAG, "Error in processed file", e);
-            } finally {
-                try {
-                    if (temp != null) temp.close();
-                } catch (IOException e) {
-                }
-                try {
-                    if (in != null) in.close();
-                } catch (IOException e) {
-                }
             }
         }
 
@@ -911,14 +892,9 @@
                 PackageManagerBackupAgent.getStorableApplications(mPackageManager);
 
         if (mFullBackupScheduleFile.exists()) {
-            FileInputStream fstream = null;
-            BufferedInputStream bufStream = null;
-            DataInputStream in = null;
-            try {
-                fstream = new FileInputStream(mFullBackupScheduleFile);
-                bufStream = new BufferedInputStream(fstream);
-                in = new DataInputStream(bufStream);
-
+            try (FileInputStream fstream = new FileInputStream(mFullBackupScheduleFile);
+                 BufferedInputStream bufStream = new BufferedInputStream(fstream);
+                 DataInputStream in = new DataInputStream(bufStream)) {
                 int version = in.readInt();
                 if (version != SCHEDULE_FILE_VERSION) {
                     Slog.e(TAG, "Unknown backup schedule version " + version);
@@ -979,10 +955,6 @@
                 Slog.e(TAG, "Unable to read backup schedule", e);
                 mFullBackupScheduleFile.delete();
                 schedule = null;
-            } finally {
-                IoUtils.closeQuietly(in);
-                IoUtils.closeQuietly(bufStream);
-                IoUtils.closeQuietly(fstream);
             }
         }
 
@@ -1443,18 +1415,11 @@
         synchronized (mEverStoredApps) {
             if (!mEverStoredApps.add(packageName)) return;
 
-            RandomAccessFile out = null;
-            try {
-                out = new RandomAccessFile(mEverStored, "rws");
+            try (RandomAccessFile out = new RandomAccessFile(mEverStored, "rws")) {
                 out.seek(out.length());
                 out.writeUTF(packageName);
             } catch (IOException e) {
                 Slog.e(TAG, "Can't log backup of " + packageName + " to " + mEverStored);
-            } finally {
-                try {
-                    if (out != null) out.close();
-                } catch (IOException e) {
-                }
             }
         }
     }
@@ -1469,16 +1434,13 @@
             // we'll recognize on initialization time that the package no longer
             // exists and fix it up then.
             File tempKnownFile = new File(mBaseStateDir, "processed.new");
-            RandomAccessFile known = null;
-            try {
-                known = new RandomAccessFile(tempKnownFile, "rws");
+            try (RandomAccessFile known = new RandomAccessFile(tempKnownFile, "rws")) {
                 mEverStoredApps.remove(packageName);
                 for (String s : mEverStoredApps) {
                     known.writeUTF(s);
                     if (MORE_DEBUG) Slog.v(TAG, "    " + s);
                 }
                 known.close();
-                known = null;
                 if (!tempKnownFile.renameTo(mEverStored)) {
                     throw new IOException("Can't rename " + tempKnownFile + " to " + mEverStored);
                 }
@@ -1491,11 +1453,6 @@
                 mEverStoredApps.clear();
                 tempKnownFile.delete();
                 mEverStored.delete();
-            } finally {
-                try {
-                    if (known != null) known.close();
-                } catch (IOException e) {
-                }
             }
         }
     }
@@ -1504,9 +1461,7 @@
     // as the set of packages with data [supposedly] available in the
     // ancestral dataset.
     public void writeRestoreTokens() {
-        try {
-            RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd");
-
+        try (RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd")) {
             // First, the version number of this record, for futureproofing
             af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
 
@@ -1525,7 +1480,6 @@
                     if (MORE_DEBUG) Slog.v(TAG, "   " + pkgName);
                 }
             }
-            af.close();
         } catch (IOException e) {
             Slog.w(TAG, "Unable to write token file:", e);
         }
@@ -2770,9 +2724,7 @@
         File base = new File(Environment.getDataDirectory(), "backup");
         File enableFile = new File(base, BACKUP_ENABLE_FILE);
         File stage = new File(base, BACKUP_ENABLE_FILE + "-stage");
-        FileOutputStream fout = null;
-        try {
-            fout = new FileOutputStream(stage);
+        try (FileOutputStream fout = new FileOutputStream(stage)) {
             fout.write(enable ? 1 : 0);
             fout.close();
             stage.renameTo(enableFile);
@@ -2788,8 +2740,6 @@
                     Settings.Secure.BACKUP_ENABLED, null, userId);
             enableFile.delete();
             stage.delete();
-        } finally {
-            IoUtils.closeQuietly(fout);
         }
     }
 
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index b88bbc1..2f9b861 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -206,7 +206,11 @@
 
         T get(int key) {
             T val = mArray.get(key);
-            val.checkOwnerOrSystemAndThrow();
+            // The value should never be null unless the resource doesn't exist
+            // (since we do not allow null resources to be added).
+            if (val != null) {
+                val.checkOwnerOrSystemAndThrow();
+            }
             return val;
         }
 
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index c68e5d6..4733840 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -48,6 +48,7 @@
 import android.os.WorkSource;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
+import android.util.DebugUtils;
 import android.util.Slog;
 import android.view.InputDevice;
 import android.media.AudioAttributes;
@@ -60,10 +61,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
 import java.util.LinkedList;
-import java.util.ListIterator;
 
 public class VibratorService extends IVibratorService.Stub
         implements InputManager.InputDeviceListener {
@@ -948,6 +946,21 @@
         }
 
         private int runVibrate() {
+            try {
+                final int zenMode = Settings.Global.getInt(mContext.getContentResolver(),
+                        Settings.Global.ZEN_MODE);
+                if (zenMode != Settings.Global.ZEN_MODE_OFF) {
+                    try (PrintWriter pw = getOutPrintWriter();) {
+                        pw.print("Ignoring because device is on DND mode ");
+                        pw.println(DebugUtils.flagsToString(Settings.Global.class, "ZEN_MODE_",
+                                zenMode));
+                        return 0;
+                    }
+                }
+            } catch (SettingNotFoundException e) {
+                // ignore
+            }
+
             final long duration = Long.parseLong(getNextArgRequired());
             if (duration > MAX_VIBRATION_MS) {
                 throw new IllegalArgumentException("maximum duration is " + MAX_VIBRATION_MS);
@@ -972,7 +985,8 @@
                 pw.println("    Prints this help text.");
                 pw.println("");
                 pw.println("  vibrate duration [description]");
-                pw.println("    Vibrates for duration milliseconds.");
+                pw.println("    Vibrates for duration milliseconds; ignored when device is on DND ");
+                pw.println("    (Do Not Disturb) mode.");
                 pw.println("");
             }
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ff387a7..97be508 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6715,7 +6715,6 @@
             mActiveUids.put(proc.uid, uidRec);
             EventLogTags.writeAmUidRunning(uidRec.uid);
             noteUidProcessState(uidRec.uid, uidRec.curProcState);
-            enqueueUidChangeLocked(uidRec, -1, UidRecord.CHANGE_ACTIVE);
         }
         proc.uidRecord = uidRec;
 
@@ -22826,8 +22825,9 @@
         for (int i=mActiveUids.size()-1; i>=0; i--) {
             final UidRecord uidRec = mActiveUids.valueAt(i);
             int uidChange = UidRecord.CHANGE_PROCSTATE;
-            if (uidRec.setProcState != uidRec.curProcState
-                    || uidRec.setWhitelist != uidRec.curWhitelist) {
+            if (uidRec.curProcState != ActivityManager.PROCESS_STATE_NONEXISTENT
+                    && (uidRec.setProcState != uidRec.curProcState
+                           || uidRec.setWhitelist != uidRec.curWhitelist)) {
                 if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
                         "Changes in " + uidRec + ": proc state from " + uidRec.setProcState
                         + " to " + uidRec.curProcState + ", whitelist from " + uidRec.setWhitelist
@@ -22848,7 +22848,7 @@
                                     mConstants.BACKGROUND_SETTLE_TIME);
                         }
                     }
-                    if (!uidRec.setIdle) {
+                    if (uidRec.idle && !uidRec.setIdle) {
                         uidChange = UidRecord.CHANGE_IDLE;
                     }
                 } else {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 83eea98..9273b3c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -23,6 +23,7 @@
 import android.app.IActivityController;
 import android.app.IActivityManager;
 import android.app.IStopUserCallback;
+import android.app.IUidObserver;
 import android.app.ProfilerInfo;
 import android.app.WaitResult;
 import android.app.usage.ConfigurationStats;
@@ -184,6 +185,8 @@
                     return runMakeIdle(pw);
                 case "monitor":
                     return runMonitor(pw);
+                case "watch-uids":
+                    return runWatchUids(pw);
                 case "hang":
                     return runHang(pw);
                 case "restart":
@@ -1294,6 +1297,141 @@
         return 0;
     }
 
+    static final class MyUidObserver extends IUidObserver.Stub {
+        final IActivityManager mInterface;
+        final PrintWriter mPw;
+        final InputStream mInput;
+
+        static final int STATE_NORMAL = 0;
+
+        int mState;
+
+        MyUidObserver(IActivityManager iam, PrintWriter pw, InputStream input) {
+            mInterface = iam;
+            mPw = pw;
+            mInput = input;
+        }
+
+        @Override
+        public void onUidStateChanged(int uid, int procState, long procStateSeq) throws RemoteException {
+            synchronized (this) {
+                mPw.print(uid);
+                mPw.print(" procstate ");
+                mPw.print(ProcessList.makeProcStateString(procState));
+                mPw.print(" seq ");
+                mPw.println(procStateSeq);
+                mPw.flush();
+            }
+        }
+
+        @Override
+        public void onUidGone(int uid, boolean disabled) throws RemoteException {
+            synchronized (this) {
+                mPw.print(uid);
+                mPw.print(" gone");
+                if (disabled) {
+                    mPw.print(" disabled");
+                }
+                mPw.println();
+                mPw.flush();
+            }
+        }
+
+        @Override
+        public void onUidActive(int uid) throws RemoteException {
+            synchronized (this) {
+                mPw.print(uid);
+                mPw.println(" active");
+                mPw.flush();
+            }
+        }
+
+        @Override
+        public void onUidIdle(int uid, boolean disabled) throws RemoteException {
+            synchronized (this) {
+                mPw.print(uid);
+                mPw.print(" idle");
+                if (disabled) {
+                    mPw.print(" disabled");
+                }
+                mPw.println();
+                mPw.flush();
+            }
+        }
+
+        @Override
+        public void onUidCachedChanged(int uid, boolean cached) throws RemoteException {
+            synchronized (this) {
+                mPw.print(uid);
+                mPw.println(cached ? " cached" : " uncached");
+                mPw.flush();
+            }
+        }
+
+        void printMessageForState() {
+            switch (mState) {
+                case STATE_NORMAL:
+                    mPw.println("Watching uid states...  available commands:");
+                    break;
+            }
+            mPw.println("(q)uit: finish watching");
+        }
+
+        void run() throws RemoteException {
+            try {
+                printMessageForState();
+                mPw.flush();
+
+                mInterface.registerUidObserver(this, ActivityManager.UID_OBSERVER_ACTIVE
+                        | ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_PROCSTATE
+                        | ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_CACHED,
+                        ActivityManager.PROCESS_STATE_UNKNOWN, null);
+                mState = STATE_NORMAL;
+
+                InputStreamReader converter = new InputStreamReader(mInput);
+                BufferedReader in = new BufferedReader(converter);
+                String line;
+
+                while ((line = in.readLine()) != null) {
+                    boolean addNewline = true;
+                    if (line.length() <= 0) {
+                        addNewline = false;
+                    } else if ("q".equals(line) || "quit".equals(line)) {
+                        break;
+                    } else {
+                        mPw.println("Invalid command: " + line);
+                    }
+
+                    synchronized (this) {
+                        if (addNewline) {
+                            mPw.println("");
+                        }
+                        printMessageForState();
+                        mPw.flush();
+                    }
+                }
+
+            } catch (IOException e) {
+                e.printStackTrace(mPw);
+                mPw.flush();
+            } finally {
+                mInterface.unregisterUidObserver(this);
+            }
+        }
+    }
+
+    int runWatchUids(PrintWriter pw) throws RemoteException {
+        String opt;
+        while ((opt=getNextOption()) != null) {
+            getErrPrintWriter().println("Error: Unknown option: " + opt);
+            return -1;
+        }
+
+        MyUidObserver controller = new MyUidObserver(mInterface, pw, getRawInputStream());
+        controller.run();
+        return 0;
+    }
+
     int runHang(PrintWriter pw) throws RemoteException {
         String opt;
         boolean allowRestart = false;
@@ -2599,6 +2737,8 @@
             pw.println("  monitor [--gdb <port>]");
             pw.println("      Start monitoring for crashes or ANRs.");
             pw.println("      --gdb: start gdbserv on the given port at crash/ANR");
+            pw.println("  watch-uids [--gdb <port>]");
+            pw.println("      Start watching for and reporting uid state changes.");
             pw.println("  hang [--allow-restart]");
             pw.println("      Hang the system.");
             pw.println("      --allow-restart: allow watchdog to perform normal system restart");
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index a31c33e..4f04066 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1640,6 +1640,16 @@
                                 REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
                                 "reparentToDisplay");
                         mMovedToFront = true;
+                    } else if (launchStack.getStackId() == StackId.HOME_STACK_ID
+                        && mTargetStack.getStackId() != StackId.HOME_STACK_ID) {
+                        // It is possible for the home activity to be in another stack initially.
+                        // For example, the activity may have been initially started with an intent
+                        // which placed it in the fullscreen stack. To ensure the proper handling of
+                        // the activity based on home stack assumptions, we must move it over.
+                        intentActivity.getTask().reparent(launchStack.mStackId, ON_TOP,
+                                REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
+                                "reparentingHome");
+                        mMovedToFront = true;
                     }
                     mOptions = null;
 
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index 372d80d..58e71df 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -143,6 +143,7 @@
             failCallback(callback);
             return;
         }
+        Slog.i(TAG, "Activity requesting to dismiss Keyguard: " + activityRecord);
 
         // If the client has requested to dismiss the keyguard and the Activity has the flag to
         // turn the screen on, wakeup the screen if it's the top Activity.
diff --git a/services/core/java/com/android/server/content/SyncJobService.java b/services/core/java/com/android/server/content/SyncJobService.java
index 1f02ebf..07f04b1 100644
--- a/services/core/java/com/android/server/content/SyncJobService.java
+++ b/services/core/java/com/android/server/content/SyncJobService.java
@@ -76,7 +76,7 @@
         m.what = SyncManager.SyncHandler.MESSAGE_START_SYNC;
         SyncOperation op = SyncOperation.maybeCreateFromJobExtras(params.getExtras());
 
-        mLogger.log("onStopJob() jobid=", params.getJobId(), " op=", op);
+        mLogger.log("onStartJob() jobid=", params.getJobId(), " op=", op);
 
         if (op == null) {
             Slog.e(TAG, "Got invalid job " + params.getJobId());
@@ -131,4 +131,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 601dd94..83bb17e 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -2516,6 +2516,7 @@
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         StringBuilder s = new StringBuilder();
+        s.append("  mStarted=").append(mStarted).append('\n');
         s.append("  mFixInterval=").append(mFixInterval).append('\n');
         s.append("  mDisableGps (battery saver mode)=").append(mDisableGps).append('\n');
         s.append("  mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities));
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 8d53447..a105c84 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1132,12 +1132,6 @@
             fixateNewestUserKeyAuth(userId);
             synchronizeUnifiedWorkChallengeForProfiles(userId, null);
             notifyActivePasswordMetricsAvailable(null, userId);
-
-            if (mStorage.getPersistentDataBlock() != null
-                    && LockPatternUtils.userOwnsFrpCredential(mUserManager.getUserInfo(userId))) {
-                // If owner, write to persistent storage for FRP
-                mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, userId, 0, null);
-            }
             return;
         }
         if (credential == null) {
@@ -1190,12 +1184,6 @@
             // Refresh the auth token
             doVerifyCredential(credential, credentialType, true, 0, userId, null /* progressCallback */);
             synchronizeUnifiedWorkChallengeForProfiles(userId, null);
-            if (mStorage.getPersistentDataBlock() != null
-                    && LockPatternUtils.userOwnsFrpCredential(mUserManager.getUserInfo(userId))) {
-                // If owner, write to persistent storage for FRP
-                mStorage.writePersistentDataBlock(PersistentData.TYPE_GATEKEEPER, userId,
-                        requestedQuality, willStore.toBytes());
-            }
         } else {
             throw new RemoteException("Failed to enroll " +
                     (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD ? "password"
@@ -1443,18 +1431,12 @@
             return response;
         }
 
-        final CredentialHash storedHash;
         if (userId == USER_FRP) {
-            PersistentData data = mStorage.readPersistentDataBlock();
-            if (data.type != PersistentData.TYPE_GATEKEEPER) {
-                Slog.wtf(TAG, "Expected PersistentData.TYPE_GATEKEEPER, but was: " + data.type);
-                return VerifyCredentialResponse.ERROR;
-            }
-            return verifyFrpCredential(credential, credentialType, data, progressCallback);
-        } else {
-            storedHash = mStorage.readCredentialHash(userId);
+            Slog.wtf(TAG, "Unexpected FRP credential type, should be SP based.");
+            return VerifyCredentialResponse.ERROR;
         }
 
+        final CredentialHash storedHash = mStorage.readCredentialHash(userId);
         if (storedHash.type != credentialType) {
             Slog.wtf(TAG, "doVerifyCredential type mismatch with stored credential??"
                     + " stored: " + storedHash.type + " passed in: " + credentialType);
@@ -1485,29 +1467,6 @@
         return response;
     }
 
-    private VerifyCredentialResponse verifyFrpCredential(String credential, int credentialType,
-            PersistentData data, ICheckCredentialProgressCallback progressCallback)
-            throws RemoteException {
-        CredentialHash storedHash = CredentialHash.fromBytes(data.payload);
-        if (storedHash.type != credentialType) {
-            Slog.wtf(TAG, "doVerifyCredential type mismatch with stored credential??"
-                    + " stored: " + storedHash.type + " passed in: " + credentialType);
-            return VerifyCredentialResponse.ERROR;
-        }
-        if (ArrayUtils.isEmpty(storedHash.hash) || TextUtils.isEmpty(credential)) {
-            Slog.e(TAG, "Stored hash or credential is empty");
-            return VerifyCredentialResponse.ERROR;
-        }
-        VerifyCredentialResponse response = VerifyCredentialResponse.fromGateKeeperResponse(
-                getGateKeeperService().verifyChallenge(data.userId, 0 /* challenge */,
-                        storedHash.hash, credential.getBytes()));
-        if (progressCallback != null
-                && response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
-            progressCallback.onCredentialVerified();
-        }
-        return response;
-    }
-
     @Override
     public VerifyCredentialResponse verifyTiedProfileChallenge(String credential, int type,
             long challenge, int userId) throws RemoteException {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 79372e48..b4c10ec 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -635,9 +635,8 @@
         static final int VERSION_1_HEADER_SIZE = 1 + 1 + 4 + 4;
 
         public static final int TYPE_NONE = 0;
-        public static final int TYPE_GATEKEEPER = 1;
-        public static final int TYPE_SP = 2;
-        public static final int TYPE_SP_WEAVER = 3;
+        public static final int TYPE_SP = 1;
+        public static final int TYPE_SP_WEAVER = 2;
 
         public static final PersistentData NONE = new PersistentData(TYPE_NONE,
                 UserHandle.USER_NULL, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, null);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 8b8811e..38f1c07 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.ACCESS_NETWORK_STATE;
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
+import static android.Manifest.permission.MANAGE_FALLBACK_SUBSCRIPTION_PLANS;
 import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
 import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
 import static android.Manifest.permission.READ_PHONE_STATE;
@@ -27,6 +28,7 @@
 import static android.content.Intent.ACTION_USER_ADDED;
 import static android.content.Intent.ACTION_USER_REMOVED;
 import static android.content.Intent.EXTRA_UID;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
@@ -53,7 +55,6 @@
 import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
-import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
 import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode;
 import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground;
 import static android.net.NetworkPolicyManager.resolveNetworkId;
@@ -118,9 +119,11 @@
 import android.net.LinkProperties;
 import android.net.NetworkIdentity;
 import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkState;
 import android.net.NetworkTemplate;
+import android.net.TrafficStats;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
 import android.os.Binder;
@@ -141,12 +144,16 @@
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.ShellCallback;
+import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.provider.Settings.Global;
 import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionPlan;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.text.format.Formatter;
@@ -198,6 +205,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.nio.charset.StandardCharsets;
+import java.time.ZonedDateTime;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
@@ -997,8 +1005,10 @@
             if (!isTemplateRelevant(policy.template)) continue;
             if (!policy.hasCycle()) continue;
 
-            final long start = computeLastCycleBoundary(currentTime, policy);
-            final long end = currentTime;
+            final Pair<ZonedDateTime, ZonedDateTime> cycle = NetworkPolicyManager
+                    .cycleIterator(policy).next();
+            final long start = cycle.first.toInstant().toEpochMilli();
+            final long end = cycle.second.toInstant().toEpochMilli();
             final long totalBytes = getTotalBytes(policy.template, start, end);
 
             if (policy.isOverLimit(totalBytes)) {
@@ -1455,8 +1465,10 @@
                 continue;
             }
 
-            final long start = computeLastCycleBoundary(currentTime, policy);
-            final long end = currentTime;
+            final Pair<ZonedDateTime, ZonedDateTime> cycle = NetworkPolicyManager
+                    .cycleIterator(policy).next();
+            final long start = cycle.first.toInstant().toEpochMilli();
+            final long end = cycle.second.toInstant().toEpochMilli();
             final long totalBytes = getTotalBytes(policy.template, start, end);
 
             // disable data connection when over limit and not snoozed
@@ -1566,15 +1578,11 @@
             final NetworkPolicy policy = mNetworkRules.keyAt(i);
             final String[] ifaces = mNetworkRules.valueAt(i);
 
-            final long start;
-            final long totalBytes;
-            if (policy.hasCycle()) {
-                start = computeLastCycleBoundary(currentTime, policy);
-                totalBytes = getTotalBytes(policy.template, start, currentTime);
-            } else {
-                start = Long.MAX_VALUE;
-                totalBytes = 0;
-            }
+            final Pair<ZonedDateTime, ZonedDateTime> cycle = NetworkPolicyManager
+                    .cycleIterator(policy).next();
+            final long start = cycle.first.toInstant().toEpochMilli();
+            final long end = cycle.second.toInstant().toEpochMilli();
+            final long totalBytes = getTotalBytes(policy.template, start, end);
 
             if (LOGD) {
                 Slog.d(TAG, "applying policy " + policy + " to ifaces " + Arrays.toString(ifaces));
@@ -1889,7 +1897,7 @@
 
         } catch (FileNotFoundException e) {
             // missing policy is okay, probably first boot
-            upgradeLegacyBackgroundDataUL();
+            upgradeDefaultBackgroundDataUL();
         } catch (IOException e) {
             Log.wtf(TAG, "problem reading network policy", e);
         } catch (XmlPullParserException e) {
@@ -1903,16 +1911,22 @@
      * Upgrade legacy background data flags, notifying listeners of one last
      * change to always-true.
      */
-    private void upgradeLegacyBackgroundDataUL() {
-        mRestrictBackground = Settings.Secure.getInt(
-                mContext.getContentResolver(), Settings.Secure.BACKGROUND_DATA, 1) != 1;
+    private void upgradeDefaultBackgroundDataUL() {
+        // This method is only called when we're unable to find the network policy flag, which
+        // usually happens on first boot of a new device and not one that has received an OTA.
 
-        // kick off one last broadcast if restricted
-        if (mRestrictBackground) {
-            final Intent broadcast = new Intent(
-                    ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
-            mContext.sendBroadcastAsUser(broadcast, UserHandle.ALL);
-        }
+        // Seed from the default value configured for this device.
+        mRestrictBackground = Settings.Global.getInt(
+                mContext.getContentResolver(), Global.DEFAULT_RESTRICT_BACKGROUND_DATA, 0) == 1;
+
+        // NOTE: We used to read the legacy setting here :
+        //
+        // final int legacyFlagValue = Settings.Secure.getInt(
+        //        mContext.getContentResolver(), Settings.Secure.BACKGROUND_DATA, ..);
+        //
+        // This is no longer necessary because we will never upgrade directly from Gingerbread
+        // to O+. Devices upgrading from ICS onwards to O will have a netpolicy.xml file that
+        // contains the correct value that we will continue to use.
     }
 
     /**
@@ -2479,6 +2493,178 @@
         return new NetworkQuotaInfo();
     }
 
+    private void enforceSubscriptionPlanAccess(int subId, int callingUid, String callingPackage) {
+        // Verify they're not lying about package name
+        mAppOps.checkPackage(callingUid, callingPackage);
+
+        // Verify they have phone permission from user
+        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, TAG);
+        if (mAppOps.checkOp(AppOpsManager.OP_READ_PHONE_STATE, callingUid,
+                callingPackage) != AppOpsManager.MODE_ALLOWED) {
+            throw new SecurityException(
+                    "Calling package " + callingPackage + " does not hold " + READ_PHONE_STATE);
+        }
+
+        final SubscriptionInfo si;
+        final long token = Binder.clearCallingIdentity();
+        try {
+            si = mContext.getSystemService(SubscriptionManager.class)
+                    .getActiveSubscriptionInfo(subId);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+
+        // First check: does caller have carrier access?
+        if (si.isEmbedded() && si.canManageSubscription(mContext, callingPackage)) {
+            Slog.v(TAG, "Granting access because " + callingPackage + " is carrier");
+            return;
+        }
+
+        // Second check: was caller first to claim this HNI?
+        // TODO: extend to support external data sources
+
+        // Final check: does caller have fallback permission?
+        if (mContext.checkCallingOrSelfPermission(
+                MANAGE_FALLBACK_SUBSCRIPTION_PLANS) == PERMISSION_GRANTED) {
+            Slog.v(TAG, "Granting access because " + callingPackage + " is fallback");
+            return;
+        }
+
+        throw new SecurityException("Calling package " + callingPackage
+                + " has no access to subscription plans for " + subId);
+    }
+
+    @Override
+    public SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage) {
+        enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
+
+        // TODO: extend to support external data sources
+        if (!"com.android.settings".equals(callingPackage)) {
+            throw new UnsupportedOperationException();
+        }
+
+        final String fake = SystemProperties.get("fw.fake_plan");
+        if (!TextUtils.isEmpty(fake)) {
+            final List<SubscriptionPlan> plans = new ArrayList<>();
+            if ("month_hard".equals(fake)) {
+                plans.add(SubscriptionPlan.Builder
+                        .createRecurringMonthly(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"))
+                        .setTitle("G-Mobile")
+                        .setDataWarning(2 * TrafficStats.GB_IN_BYTES)
+                        .setDataLimit(5 * TrafficStats.GB_IN_BYTES,
+                                SubscriptionPlan.LIMIT_BEHAVIOR_BILLED)
+                        .setDataUsage(1 * TrafficStats.GB_IN_BYTES,
+                                ZonedDateTime.now().minusHours(36).toInstant().toEpochMilli())
+                        .build());
+            } else if ("month_soft".equals(fake)) {
+                plans.add(SubscriptionPlan.Builder
+                        .createRecurringMonthly(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"))
+                        .setTitle("G-Mobile is the carriers name who this plan belongs to")
+                        .setSummary("Crazy unlimited bandwidth plan with incredibly long title "
+                                + "that should be cut off to prevent UI from looking terrible")
+                        .setDataWarning(2 * TrafficStats.GB_IN_BYTES)
+                        .setDataLimit(5 * TrafficStats.GB_IN_BYTES,
+                                SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED)
+                        .setDataUsage(1 * TrafficStats.GB_IN_BYTES,
+                                ZonedDateTime.now().minusHours(1).toInstant().toEpochMilli())
+                        .build());
+            } else if ("month_none".equals(fake)) {
+                plans.add(SubscriptionPlan.Builder
+                        .createRecurringMonthly(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"))
+                        .setTitle("G-Mobile")
+                        .build());
+            } else if ("prepaid".equals(fake)) {
+                plans.add(SubscriptionPlan.Builder
+                        .createNonrecurring(ZonedDateTime.now().minusDays(20),
+                                ZonedDateTime.now().plusDays(10))
+                        .setTitle("G-Mobile")
+                        .setDataLimit(512 * TrafficStats.MB_IN_BYTES,
+                                SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED)
+                        .setDataUsage(100 * TrafficStats.MB_IN_BYTES,
+                                ZonedDateTime.now().minusHours(3).toInstant().toEpochMilli())
+                        .build());
+            } else if ("prepaid_crazy".equals(fake)) {
+                plans.add(SubscriptionPlan.Builder
+                        .createNonrecurring(ZonedDateTime.now().minusDays(20),
+                                ZonedDateTime.now().plusDays(10))
+                        .setTitle("G-Mobile Anytime")
+                        .setDataLimit(512 * TrafficStats.MB_IN_BYTES,
+                                SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED)
+                        .setDataUsage(100 * TrafficStats.MB_IN_BYTES,
+                                ZonedDateTime.now().minusHours(3).toInstant().toEpochMilli())
+                        .build());
+                plans.add(SubscriptionPlan.Builder
+                        .createNonrecurring(ZonedDateTime.now().minusDays(10),
+                                ZonedDateTime.now().plusDays(20))
+                        .setTitle("G-Mobile Nickel Nights")
+                        .setSummary("5¢/GB between 1-5AM")
+                        .setDataUsage(15 * TrafficStats.MB_IN_BYTES,
+                                ZonedDateTime.now().minusHours(30).toInstant().toEpochMilli())
+                        .build());
+                plans.add(SubscriptionPlan.Builder
+                        .createNonrecurring(ZonedDateTime.now().minusDays(10),
+                                ZonedDateTime.now().plusDays(20))
+                        .setTitle("G-Mobile Bonus 3G")
+                        .setSummary("Unlimited 3G data")
+                        .setDataLimit(5 * TrafficStats.GB_IN_BYTES,
+                                SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED)
+                        .setDataUsage(300 * TrafficStats.MB_IN_BYTES,
+                                ZonedDateTime.now().minusHours(1).toInstant().toEpochMilli())
+                        .build());
+            }
+            return plans.toArray(new SubscriptionPlan[plans.size()]);
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+            final NetworkTemplate template = NetworkTemplate
+                    .buildTemplateMobileAll(tm.getSubscriberId(subId));
+            final NetworkPolicy policy = mNetworkPolicy.get(template);
+            if (policy != null) {
+                return new SubscriptionPlan[] { SubscriptionPlan.convert(policy) };
+            } else {
+                return new SubscriptionPlan[0];
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, String callingPackage) {
+        enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
+
+        // TODO: extend to support external data sources
+        if (!"com.android.settings".equals(callingPackage)) {
+            throw new UnsupportedOperationException();
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+            final NetworkTemplate template = NetworkTemplate
+                    .buildTemplateMobileAll(tm.getSubscriberId(subId));
+            if (ArrayUtils.isEmpty(plans)) {
+                mNetworkPolicy.remove(template);
+            } else {
+                final NetworkPolicy policy = SubscriptionPlan.convert(plans[0]);
+                policy.template = template;
+                mNetworkPolicy.put(template, policy);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public String getSubscriptionPlanOwner(int subId) {
+        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+        // TODO: extend to support external data sources
+        return "com.android.settings";
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index ab685ca..8209ade 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -18,7 +18,6 @@
 
 import static android.Manifest.permission.ACCESS_NETWORK_STATE;
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
-import static android.Manifest.permission.MODIFY_NETWORK_ACCOUNTING;
 import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
 import static android.content.Intent.ACTION_SHUTDOWN;
 import static android.content.Intent.ACTION_UID_REMOVED;
@@ -689,7 +688,8 @@
     @Override
     public void incrementOperationCount(int uid, int tag, int operationCount) {
         if (Binder.getCallingUid() != uid) {
-            mContext.enforceCallingOrSelfPermission(MODIFY_NETWORK_ACCOUNTING, TAG);
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.UPDATE_DEVICE_STATS, TAG);
         }
 
         if (operationCount < 0) {
@@ -710,7 +710,7 @@
 
     @Override
     public void setUidForeground(int uid, boolean uidForeground) {
-        mContext.enforceCallingOrSelfPermission(MODIFY_NETWORK_ACCOUNTING, TAG);
+        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
         synchronized (mStatsLock) {
             final int set = uidForeground ? SET_FOREGROUND : SET_DEFAULT;
@@ -750,7 +750,7 @@
 
     @Override
     public void advisePersistThreshold(long thresholdBytes) {
-        mContext.enforceCallingOrSelfPermission(MODIFY_NETWORK_ACCOUNTING, TAG);
+        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         assertBandwidthControlEnabled();
 
         // clamp threshold into safe range
diff --git a/services/core/java/com/android/server/notification/BadgeExtractor.java b/services/core/java/com/android/server/notification/BadgeExtractor.java
index 1bd2085..184f8b2 100644
--- a/services/core/java/com/android/server/notification/BadgeExtractor.java
+++ b/services/core/java/com/android/server/notification/BadgeExtractor.java
@@ -47,9 +47,11 @@
         if (!userWantsBadges || !appCanShowBadge) {
             record.setShowBadge(false);
         } else {
-            record.setShowBadge(mConfig.getNotificationChannel(record.sbn.getPackageName(),
-                    record.sbn.getUid(), record.getChannel().getId(), false).canShowBadge()
-                    && appCanShowBadge);
+            if (record.getChannel() != null) {
+                record.setShowBadge(record.getChannel().canShowBadge() && appCanShowBadge);
+            } else {
+                record.setShowBadge(appCanShowBadge);
+            }
         }
 
         return null;
diff --git a/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java b/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java
new file mode 100644
index 0000000..7c82845
--- /dev/null
+++ b/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java
@@ -0,0 +1,48 @@
+/**
+* Copyright (C) 2017 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.android.server.notification;
+
+import android.content.Context;
+import android.util.Slog;
+
+/**
+ * Applies adjustments from the group helper and notification assistant
+ */
+public class NotificationAdjustmentExtractor implements NotificationSignalExtractor {
+    private static final String TAG = "BadgeExtractor";
+    private static final boolean DBG = false;
+
+
+    public void initialize(Context ctx, NotificationUsageStats usageStats) {
+        if (DBG) Slog.d(TAG, "Initializing  " + getClass().getSimpleName() + ".");
+    }
+
+    public RankingReconsideration process(NotificationRecord record) {
+        if (record == null || record.getNotification() == null) {
+            if (DBG) Slog.d(TAG, "skipping empty notification");
+            return null;
+        }
+
+        record.applyAdjustments();
+
+        return null;
+    }
+
+    @Override
+    public void setConfig(RankingConfig config) {
+        // config is not used
+    }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
new file mode 100644
index 0000000..46ab556
--- /dev/null
+++ b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
@@ -0,0 +1,55 @@
+/**
+* Copyright (C) 2017 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.android.server.notification;
+
+import android.content.Context;
+import android.util.Slog;
+
+/**
+ * Stores the latest notification channel information for this notification
+ */
+public class NotificationChannelExtractor implements NotificationSignalExtractor {
+    private static final String TAG = "BadgeExtractor";
+    private static final boolean DBG = false;
+
+    private RankingConfig mConfig;
+
+    public void initialize(Context ctx, NotificationUsageStats usageStats) {
+        if (DBG) Slog.d(TAG, "Initializing  " + getClass().getSimpleName() + ".");
+    }
+
+    public RankingReconsideration process(NotificationRecord record) {
+        if (record == null || record.getNotification() == null) {
+            if (DBG) Slog.d(TAG, "skipping empty notification");
+            return null;
+        }
+
+        if (mConfig == null) {
+            if (DBG) Slog.d(TAG, "missing config");
+            return null;
+        }
+
+        record.updateNotificationChannel(mConfig.getNotificationChannel(record.sbn.getPackageName(),
+                record.sbn.getUid(), record.getChannel().getId(), false));
+
+        return null;
+    }
+
+    @Override
+    public void setConfig(RankingConfig config) {
+        mConfig = config;
+    }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
index 4981d5c..12b29cf 100644
--- a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
@@ -58,6 +58,10 @@
             }
         }
 
+        if (!record.isRecentlyIntrusive()) {
+            return null;
+        }
+
         return new RankingReconsideration(record.getKey(), HANG_TIME_MS) {
             @Override
             public void work() {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c0f9e0d..c816e7f 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -278,7 +278,7 @@
     private ICompanionDeviceManager mCompanionManager;
 
     final IBinder mForegroundToken = new Binder();
-    private Handler mHandler;
+    private WorkerHandler mHandler;
     private final HandlerThread mRankingThread = new HandlerThread("ranker",
             Process.THREAD_PRIORITY_BACKGROUND);
 
@@ -295,8 +295,8 @@
     private String mVibrateNotificationKey;
 
     private final SparseArray<ArraySet<ManagedServiceInfo>> mListenersDisablingEffects =
-            new SparseArray<ArraySet<ManagedServiceInfo>>();
-    private List<ComponentName> mEffectsSuppressors = new ArrayList<ComponentName>();
+            new SparseArray<>();
+    private List<ComponentName> mEffectsSuppressors = new ArrayList<>();
     private int mListenerHints;  // right now, all hints are global
     private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
 
@@ -312,16 +312,14 @@
     // used as a mutex for access to all active notifications & listeners
     final Object mNotificationLock = new Object();
     @GuardedBy("mNotificationLock")
-    final ArrayList<NotificationRecord> mNotificationList =
-            new ArrayList<NotificationRecord>();
+    final ArrayList<NotificationRecord> mNotificationList = new ArrayList<>();
     @GuardedBy("mNotificationLock")
-    final ArrayMap<String, NotificationRecord> mNotificationsByKey =
-            new ArrayMap<String, NotificationRecord>();
+    final ArrayMap<String, NotificationRecord> mNotificationsByKey = new ArrayMap<>();
     @GuardedBy("mNotificationLock")
     final ArrayList<NotificationRecord> mEnqueuedNotifications = new ArrayList<>();
     @GuardedBy("mNotificationLock")
     final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
-    final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
+    final ArrayList<ToastRecord> mToastQueue = new ArrayList<>();
     final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
 
     // The last key in this list owns the hardware.
@@ -1093,7 +1091,7 @@
                     count--;
                 }
                 if (posted.sbn.isGroup() && posted.getNotification().isGroupSummary()) {
-                    count --;
+                    count--;
                 }
             }
 
@@ -1101,6 +1099,13 @@
         }
     }
 
+    void clearNotifications() {
+        mEnqueuedNotifications.clear();
+        mNotificationList.clear();
+        mNotificationsByKey.clear();
+        mSummaryByGroupKey.clear();
+    }
+
     @VisibleForTesting
     void addNotification(NotificationRecord r) {
         mNotificationList.add(r);
@@ -1121,7 +1126,7 @@
     }
 
     @VisibleForTesting
-    void setHandler(Handler handler) {
+    void setHandler(WorkerHandler handler) {
         mHandler = handler;
     }
 
@@ -1141,6 +1146,11 @@
     }
 
     @VisibleForTesting
+    void setRankingHandler(RankingHandler rankingHandler) {
+        mRankingHandler = rankingHandler;
+    }
+
+    @VisibleForTesting
     void setIsTelevision(boolean isTelevision) {
         mIsTelevision = isTelevision;
     }
@@ -1152,7 +1162,8 @@
 
     // TODO: Tests should call onStart instead once the methods above are removed.
     @VisibleForTesting
-    void init(Looper looper, IPackageManager packageManager, PackageManager packageManagerClient,
+    void init(Looper looper, IPackageManager packageManager,
+            PackageManager packageManagerClient,
             LightsManager lightsManager, NotificationListeners notificationListeners,
             NotificationAssistants notificationAssistants, ConditionProviders conditionProviders,
             ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper,
@@ -1323,7 +1334,8 @@
 
         final File systemDir = new File(Environment.getDataDirectory(), "system");
 
-        init(Looper.myLooper(), AppGlobals.getPackageManager(), getContext().getPackageManager(),
+        init(Looper.myLooper(),
+                AppGlobals.getPackageManager(), getContext().getPackageManager(),
                 getLocalService(LightsManager.class),
                 new NotificationListeners(AppGlobals.getPackageManager()),
                 new NotificationAssistants(AppGlobals.getPackageManager()),
@@ -1343,7 +1355,6 @@
                 synchronized (mNotificationLock) {
                     addAutogroupKeyLocked(key);
                 }
-                mRankingHandler.requestSort(false);
             }
 
             @Override
@@ -1351,7 +1362,6 @@
                 synchronized (mNotificationLock) {
                     removeAutogroupKeyLocked(key);
                 }
-                mRankingHandler.requestSort(false);
             }
 
             @Override
@@ -1424,28 +1434,14 @@
         }
         mRankingHelper.updateNotificationChannel(pkg, uid, channel);
 
-        final NotificationChannel modifiedChannel =
-                mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
-
         if (!fromListener) {
+            final NotificationChannel modifiedChannel =
+                    mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
             mListeners.notifyNotificationChannelChanged(
                     pkg, UserHandle.getUserHandleForUid(uid),
                     modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
         }
 
-        synchronized (mNotificationLock) {
-            final int N = mNotificationList.size();
-            for (int i = N - 1; i >= 0; --i) {
-                NotificationRecord r = mNotificationList.get(i);
-                if (r.sbn.getPackageName().equals(pkg)
-                        && r.sbn.getUid() == uid
-                        && channel.getId() != null
-                        && channel.getId().equals(r.getChannel().getId())) {
-                    r.updateNotificationChannel(modifiedChannel);
-                }
-            }
-        }
-        mRankingHandler.requestSort(true);
         savePolicyFile();
     }
 
@@ -2883,7 +2879,7 @@
                     NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
                     applyAdjustment(n, adjustment);
                 }
-                mRankingHandler.requestSort(true);
+                mRankingHandler.requestSort();
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -2902,7 +2898,7 @@
                         applyAdjustment(n, adjustment);
                     }
                 }
-                mRankingHandler.requestSort(true);
+                mRankingHandler.requestSort();
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -2976,39 +2972,44 @@
         }
     };
 
-    private void applyAdjustment(NotificationRecord n, Adjustment adjustment) {
-        if (n == null) {
+    private void applyAdjustment(NotificationRecord r, Adjustment adjustment) {
+        if (r == null) {
             return;
         }
         if (adjustment.getSignals() != null) {
             Bundle.setDefusable(adjustment.getSignals(), true);
-            final ArrayList<String> people =
-                    adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
-            final ArrayList<SnoozeCriterion> snoozeCriterionList =
-                    adjustment.getSignals().getParcelableArrayList(Adjustment.KEY_SNOOZE_CRITERIA);
-            n.setPeopleOverride(people);
-            n.setSnoozeCriteria(snoozeCriterionList);
+            r.addAdjustment(adjustment);
         }
     }
 
     @GuardedBy("mNotificationLock")
-    private void addAutogroupKeyLocked(String key) {
-        NotificationRecord n = mNotificationsByKey.get(key);
-        if (n == null) {
+    void addAutogroupKeyLocked(String key) {
+        NotificationRecord r = mNotificationsByKey.get(key);
+        if (r == null) {
             return;
         }
-        n.setOverrideGroupKey(GroupHelper.AUTOGROUP_KEY);
+        addAutoGroupAdjustment(r, GroupHelper.AUTOGROUP_KEY);
         EventLogTags.writeNotificationAutogrouped(key);
+        mRankingHandler.requestSort();
     }
 
     @GuardedBy("mNotificationLock")
-    private void removeAutogroupKeyLocked(String key) {
-        NotificationRecord n = mNotificationsByKey.get(key);
-        if (n == null) {
+    void removeAutogroupKeyLocked(String key) {
+        NotificationRecord r = mNotificationsByKey.get(key);
+        if (r == null) {
             return;
         }
-        n.setOverrideGroupKey(null);
+        addAutoGroupAdjustment(r, null);
         EventLogTags.writeNotificationUnautogrouped(key);
+        mRankingHandler.requestSort();
+    }
+
+    private void addAutoGroupAdjustment(NotificationRecord r, String overrideGroupKey) {
+        Bundle signals = new Bundle();
+        signals.putString(Adjustment.KEY_GROUP_KEY, overrideGroupKey);
+        Adjustment adjustment =
+                new Adjustment(r.sbn.getPackageName(), r.getKey(), signals, "", r.sbn.getUserId());
+        r.addAdjustment(adjustment);
     }
 
     // Clears the 'fake' auto-group summary.
@@ -4267,39 +4268,44 @@
             }
         }
         if (changed) {
-            scheduleSendRankingUpdate();
+            mHandler.scheduleSendRankingUpdate();
         }
     }
 
-    private void handleRankingSort(Message msg) {
-        if (!(msg.obj instanceof Boolean)) return;
+    void handleRankingSort() {
         if (mRankingHelper == null) return;
-        boolean forceUpdate = ((Boolean) msg.obj == null) ? false : (boolean) msg.obj;
         synchronized (mNotificationLock) {
             final int N = mNotificationList.size();
-            // Any field that can change via one of the extractors or by the assistant
-            // needs to be added here.
-            ArrayList<String> orderBefore = new ArrayList<String>(N);
-            ArrayList<String> groupOverrideBefore = new ArrayList<>(N);
+            // Any field that can change via one of the extractors needs to be added here.
+            ArrayList<String> orderBefore = new ArrayList<>(N);
             int[] visibilities = new int[N];
             boolean[] showBadges = new boolean[N];
+            ArrayList<NotificationChannel> channelBefore = new ArrayList<>(N);
+            ArrayList<String> groupKeyBefore = new ArrayList<>(N);
+            ArrayList<ArrayList<String>> overridePeopleBefore = new ArrayList<>(N);
+            ArrayList<ArrayList<SnoozeCriterion>> snoozeCriteriaBefore = new ArrayList<>(N);
             for (int i = 0; i < N; i++) {
                 final NotificationRecord r = mNotificationList.get(i);
                 orderBefore.add(r.getKey());
-                groupOverrideBefore.add(r.sbn.getGroupKey());
                 visibilities[i] = r.getPackageVisibilityOverride();
                 showBadges[i] = r.canShowBadge();
+                channelBefore.add(r.getChannel());
+                groupKeyBefore.add(r.getGroupKey());
+                overridePeopleBefore.add(r.getPeopleOverride());
+                snoozeCriteriaBefore.add(r.getSnoozeCriteria());
                 mRankingHelper.extractSignals(r);
             }
             mRankingHelper.sort(mNotificationList);
             for (int i = 0; i < N; i++) {
                 final NotificationRecord r = mNotificationList.get(i);
-                if (forceUpdate
-                        || !orderBefore.get(i).equals(r.getKey())
+                if (!orderBefore.get(i).equals(r.getKey())
                         || visibilities[i] != r.getPackageVisibilityOverride()
-                        || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())
-                        || showBadges[i] != r.canShowBadge()) {
-                    scheduleSendRankingUpdate();
+                        || showBadges[i] != r.canShowBadge()
+                        || !Objects.equals(channelBefore.get(i), r.getChannel())
+                        || !Objects.equals(groupKeyBefore.get(i), r.getGroupKey())
+                        || !Objects.equals(overridePeopleBefore.get(i), r.getPeopleOverride())
+                        || !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria())) {
+                    mHandler.scheduleSendRankingUpdate();
                     return;
                 }
             }
@@ -4333,13 +4339,6 @@
         return mRankingHelper.indexOf(mNotificationList, target);
     }
 
-    private void scheduleSendRankingUpdate() {
-        if (!mHandler.hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
-            Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
-            mHandler.sendMessage(m);
-        }
-    }
-
     private void handleSendRankingUpdate() {
         synchronized (mNotificationLock) {
             mListeners.notifyRankingUpdateLocked();
@@ -4371,7 +4370,7 @@
         }
     }
 
-    private final class WorkerHandler extends Handler
+    protected class WorkerHandler extends Handler
     {
         public WorkerHandler(Looper looper) {
             super(looper);
@@ -4400,6 +4399,13 @@
             }
         }
 
+        protected void scheduleSendRankingUpdate() {
+            if (!hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
+                Message m = Message.obtain(this, MESSAGE_SEND_RANKING_UPDATE);
+                sendMessage(m);
+            }
+        }
+
     }
 
     private final class RankingHandlerWorker extends Handler implements RankingHandler
@@ -4415,16 +4421,15 @@
                     handleRankingReconsideration(msg);
                     break;
                 case MESSAGE_RANKING_SORT:
-                    handleRankingSort(msg);
+                    handleRankingSort();
                     break;
             }
         }
 
-        public void requestSort(boolean forceUpdate) {
+        public void requestSort() {
             removeMessages(MESSAGE_RANKING_SORT);
             Message msg = Message.obtain();
             msg.what = MESSAGE_RANKING_SORT;
-            msg.obj = forceUpdate;
             sendMessage(msg);
         }
 
@@ -5741,6 +5746,8 @@
         public static final String USAGE = "help\n"
                 + "allow_listener COMPONENT\n"
                 + "disallow_listener COMPONENT\n"
+                + "set_assistant COMPONENT\n"
+                + "remove_assistant COMPONENT\n"
                 + "allow_dnd PACKAGE\n"
                 + "disallow_dnd PACKAGE";
 
@@ -5781,6 +5788,24 @@
                         getBinderService().setNotificationListenerAccessGranted(cn, false);
                     }
                     break;
+                    case "allow_assistant": {
+                        ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
+                        if (cn == null) {
+                            pw.println("Invalid assistant - must be a ComponentName");
+                            return -1;
+                        }
+                        getBinderService().setNotificationAssistantAccessGranted(cn, true);
+                    }
+                    break;
+                    case "disallow_assistant": {
+                        ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
+                        if (cn == null) {
+                            pw.println("Invalid assistant - must be a ComponentName");
+                            return -1;
+                        }
+                        getBinderService().setNotificationAssistantAccessGranted(cn, false);
+                    }
+                    break;
 
                     default:
                         return handleDefaultCommands(cmd);
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 1dee71c..77bf9e3 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -35,8 +35,10 @@
 import android.metrics.LogMaker;
 import android.net.Uri;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.service.notification.Adjustment;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationRecordProto;
 import android.service.notification.SnoozeCriterion;
@@ -57,6 +59,7 @@
 import java.lang.reflect.Array;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -132,6 +135,8 @@
     private String mGroupLogTag;
     private String mChannelIdLogTag;
 
+    private final List<Adjustment> mAdjustments;
+
     @VisibleForTesting
     public NotificationRecord(Context context, StatusBarNotification sbn,
             NotificationChannel channel)
@@ -150,6 +155,7 @@
         mAttributes = calculateAttributes();
         mImportance = calculateImportance();
         mLight = calculateLights();
+        mAdjustments = new ArrayList<>();
     }
 
     private boolean isPreChannelsNotification() {
@@ -504,6 +510,7 @@
         if (getSnoozeCriteria() != null) {
             pw.println(prefix + "snoozeCriteria=" + TextUtils.join(",", getSnoozeCriteria()));
         }
+        pw.println(prefix + "mAdjustments=" + mAdjustments);
     }
 
 
@@ -539,6 +546,36 @@
                 this.sbn.getNotification());
     }
 
+    public void addAdjustment(Adjustment adjustment) {
+        synchronized (mAdjustments) {
+            mAdjustments.add(adjustment);
+        }
+    }
+
+    public void applyAdjustments() {
+        synchronized (mAdjustments) {
+            for (Adjustment adjustment: mAdjustments) {
+                Bundle signals = adjustment.getSignals();
+                if (signals.containsKey(Adjustment.KEY_PEOPLE)) {
+                    final ArrayList<String> people =
+                            adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
+                    setPeopleOverride(people);
+                }
+                if (signals.containsKey(Adjustment.KEY_SNOOZE_CRITERIA)) {
+                    final ArrayList<SnoozeCriterion> snoozeCriterionList =
+                            adjustment.getSignals().getParcelableArrayList(
+                                    Adjustment.KEY_SNOOZE_CRITERIA);
+                    setSnoozeCriteria(snoozeCriterionList);
+                }
+                if (signals.containsKey(Adjustment.KEY_GROUP_KEY)) {
+                    final String groupOverrideKey =
+                            adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY);
+                    setOverrideGroupKey(groupOverrideKey);
+                }
+            }
+        }
+    }
+
     public void setContactAffinity(float contactAffinity) {
         mContactAffinity = contactAffinity;
         if (mImportance < IMPORTANCE_DEFAULT &&
diff --git a/services/core/java/com/android/server/notification/RankingHandler.java b/services/core/java/com/android/server/notification/RankingHandler.java
index 656d727..96324d8 100644
--- a/services/core/java/com/android/server/notification/RankingHandler.java
+++ b/services/core/java/com/android/server/notification/RankingHandler.java
@@ -16,6 +16,6 @@
 package com.android.server.notification;
 
 public interface RankingHandler {
-    public void requestSort(boolean forceUpdate);
+    public void requestSort();
     public void requestReconsideration(RankingReconsideration recon);
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 108a41d..5c2c2b3 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -399,7 +399,7 @@
         for (int i = 0; i < N; i++) {
             mSignalExtractors[i].setConfig(this);
         }
-        mRankingHandler.requestSort(false);
+        mRankingHandler.requestSort();
     }
 
     public void sort(ArrayList<NotificationRecord> notificationList) {
@@ -511,7 +511,6 @@
             MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
         }
         r.groups.put(group.getId(), group);
-        updateConfig();
     }
 
     @Override
@@ -569,7 +568,6 @@
         r.channels.put(channel.getId(), channel);
         MetricsLogger.action(getChannelLog(channel, pkg).setType(
                 MetricsProto.MetricsEvent.TYPE_OPEN));
-        updateConfig();
     }
 
     void clearLockedFields(NotificationChannel channel) {
@@ -641,7 +639,6 @@
             LogMaker lm = getChannelLog(channel, pkg);
             lm.setType(MetricsProto.MetricsEvent.TYPE_CLOSE);
             MetricsLogger.action(lm);
-            updateConfig();
         }
     }
 
@@ -655,7 +652,6 @@
             return;
         }
         r.channels.remove(channelId);
-        updateConfig();
     }
 
     @Override
@@ -672,7 +668,6 @@
                 r.channels.remove(key);
             }
         }
-        updateConfig();
     }
 
     public NotificationChannelGroup getNotificationChannelGroup(String groupId, String pkg,
@@ -735,7 +730,6 @@
                 deletedChannels.add(nc);
             }
         }
-        updateConfig();
         return deletedChannels;
     }
 
@@ -1150,7 +1144,7 @@
             changed |= oldValue != newValue;
         }
         if (changed) {
-            mRankingHandler.requestSort(false);
+            mRankingHandler.requestSort();
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 6749afb..34092ad 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -18,7 +18,6 @@
 
 import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
 
-import android.app.AlarmManager;
 import android.app.job.JobInfo;
 import android.app.job.JobParameters;
 import android.app.job.JobScheduler;
@@ -40,6 +39,7 @@
 import com.android.server.PinnerService;
 
 import java.io.File;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.TimeUnit;
 
@@ -73,6 +73,9 @@
     // Optimizations should be aborted. No space left on device.
     private static final int OPTIMIZE_ABORT_NO_SPACE_LEFT = 3;
 
+    // Used for calculating space threshold for downgrading unused apps.
+    private static final int LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE = 2;
+
     /**
      * Set of failed packages remembered across job runs.
      */
@@ -92,6 +95,9 @@
 
     private final File mDataDir = Environment.getDataDirectory();
 
+    private static final long mDowngradeUnusedAppsThresholdInMillis =
+            getDowngradeUnusedAppsThresholdInMillis();
+
     public static void schedule(Context context) {
         JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
 
@@ -215,7 +221,8 @@
                     /* checkProfiles */ false,
                     PackageManagerService.REASON_BOOT,
                     /* force */ false,
-                    /* bootComplete */ true);
+                    /* bootComplete */ true,
+                    /* downgrade */ false);
             if (result == PackageDexOptimizer.DEX_OPT_PERFORMED)  {
                 updatedPackages.add(pkg);
             }
@@ -243,7 +250,8 @@
     }
 
     // Optimize the given packages and return the optimization result (one of the OPTIMIZE_* codes).
-    private int idleOptimization(PackageManagerService pm, ArraySet<String> pkgs, Context context) {
+    private int idleOptimization(PackageManagerService pm, ArraySet<String> pkgs,
+            Context context) {
         Log.i(TAG, "Performing idle optimizations");
         // If post-boot update is still running, request that it exits early.
         mExitPostBootUpdate.set(true);
@@ -274,9 +282,16 @@
             long lowStorageThreshold, boolean is_for_primary_dex,
             ArraySet<String> failedPackageNames) {
         ArraySet<String> updatedPackages = new ArraySet<>();
+        Set<String> unusedPackages = pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis);
+        // Only downgrade apps when space is low on device.
+        // Threshold is selected above the lowStorageThreshold so that we can pro-actively clean
+        // up disk before user hits the actual lowStorageThreshold.
+        final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE *
+                lowStorageThreshold;
+        boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade);
         for (String pkg : pkgs) {
             int abort_code = abortIdleOptimizations(lowStorageThreshold);
-            if (abort_code != OPTIMIZE_CONTINUE) {
+            if (abort_code == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
                 return abort_code;
             }
 
@@ -284,30 +299,57 @@
                 if (failedPackageNames.contains(pkg)) {
                     // Skip previously failing package
                     continue;
-                } else {
-                    // Conservatively add package to the list of failing ones in case performDexOpt
-                    // never returns.
-                    failedPackageNames.add(pkg);
                 }
             }
 
+            int reason;
+            boolean downgrade;
+            // Downgrade unused packages.
+            if (unusedPackages.contains(pkg) && shouldDowngrade) {
+                // This applies for system apps or if packages location is not a directory, i.e.
+                // monolithic install.
+                if (is_for_primary_dex && !pm.canHaveOatDir(pkg)) {
+                    // For apps that don't have the oat directory, instead of downgrading,
+                    // remove their compiler artifacts from dalvik cache.
+                    pm.deleteOatArtifactsOfPackage(pkg);
+                    continue;
+                } else {
+                    reason = PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE;
+                    downgrade = true;
+                }
+            } else if (abort_code != OPTIMIZE_ABORT_NO_SPACE_LEFT) {
+                reason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
+                downgrade = false;
+            } else {
+                // can't dexopt because of low space.
+                continue;
+            }
+
+            synchronized (failedPackageNames) {
+                // Conservatively add package to the list of failing ones in case
+                // performDexOpt never returns.
+                failedPackageNames.add(pkg);
+            }
+
             // Optimize package if needed. Note that there can be no race between
             // concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized.
             boolean success;
             if (is_for_primary_dex) {
                 int result = pm.performDexOptWithStatus(pkg,
                         /* checkProfiles */ true,
-                        PackageManagerService.REASON_BACKGROUND_DEXOPT,
-                        /* force */ false,
-                        /* bootComplete */ true);
+                        reason,
+                        false /* forceCompile*/,
+                        true /* bootComplete */,
+                        downgrade);
                 success = result != PackageDexOptimizer.DEX_OPT_FAILED;
                 if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) {
                     updatedPackages.add(pkg);
                 }
             } else {
                 success = pm.performDexOptSecondary(pkg,
-                        PackageManagerService.REASON_BACKGROUND_DEXOPT,
-                        /* force */ false);
+                        reason,
+                        false /* force */,
+                        downgrade);
             }
             if (success) {
                 // Dexopt succeeded, remove package from the list of failing ones.
@@ -347,6 +389,16 @@
         return OPTIMIZE_CONTINUE;
     }
 
+    // Evaluate whether apps should be downgraded.
+    private boolean shouldDowngrade(long lowStorageThresholdForDowngrade) {
+        long usableSpace = mDataDir.getUsableSpace();
+        if (usableSpace < lowStorageThresholdForDowngrade) {
+            return true;
+        }
+
+        return false;
+    }
+
     /**
      * Execute the idle optimizations immediately.
      */
@@ -415,4 +467,14 @@
             pinnerService.update(updatedPackages);
         }
     }
+
+    private static long getDowngradeUnusedAppsThresholdInMillis() {
+        final String sysPropKey = "pm.dexopt.downgrade_after_inactive_days";
+        String sysPropValue = SystemProperties.get(sysPropKey);
+        if (sysPropValue == null || sysPropValue.isEmpty()) {
+            Log.w(TAG, "SysProp " + sysPropKey + " not set");
+            return Long.MAX_VALUE;
+        }
+        return TimeUnit.DAYS.toMillis(Long.parseLong(sysPropValue));
+    }
 }
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index bd765b4..371b3ef 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -279,13 +279,13 @@
     public void dexopt(String apkPath, int uid, @Nullable String pkgName, String instructionSet,
             int dexoptNeeded, @Nullable String outputPath, int dexFlags,
             String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries,
-            @Nullable String seInfo)
+            @Nullable String seInfo, boolean downgrade)
             throws InstallerException {
         assertValidInstructionSet(instructionSet);
         if (!checkBeforeRemote()) return;
         try {
             mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
-                    dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo);
+                    dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade);
         } catch (Exception e) {
             throw InstallerException.from(e);
         }
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 2a45d15..4ff6cbf 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -30,7 +30,6 @@
 import android.os.ServiceManager;
 import android.os.ShellCallback;
 import android.os.storage.StorageManager;
-import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
 
@@ -40,7 +39,6 @@
 import java.io.File;
 import java.io.FileDescriptor;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
@@ -152,7 +150,7 @@
             Log.i(TAG, "Low on space, deleting oat files in an attempt to free up space: "
                     + PackageManagerServiceUtils.packagesToString(others));
             for (PackageParser.Package pkg : others) {
-                deleteOatArtifactsOfPackage(pkg);
+                mPackageManagerService.deleteOatArtifactsOfPackage(pkg.packageName);
             }
         }
         long spaceAvailableNow = getAvailableSpace();
@@ -242,30 +240,6 @@
         return usableSpace - lowThreshold;
     }
 
-    private static String getOatDir(PackageParser.Package pkg) {
-        if (!pkg.canHaveOatDir()) {
-            return null;
-        }
-        File codePath = new File(pkg.codePath);
-        if (codePath.isDirectory()) {
-            return PackageDexOptimizer.getOatDir(codePath).getAbsolutePath();
-        }
-        return null;
-    }
-
-    private void deleteOatArtifactsOfPackage(PackageParser.Package pkg) {
-        String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
-        for (String codePath : pkg.getAllCodePaths()) {
-            for (String isa : instructionSets) {
-                try {
-                    mPackageManagerService.mInstaller.deleteOdex(codePath, isa, getOatDir(pkg));
-                } catch (InstallerException e) {
-                    Log.e(TAG, "Failed deleting oat files for " + codePath, e);
-                }
-            }
-        }
-    }
-
     /**
      * Generate all dexopt commands for the given package.
      */
@@ -285,11 +259,12 @@
             public void dexopt(String apkPath, int uid, @Nullable String pkgName,
                     String instructionSet, int dexoptNeeded, @Nullable String outputPath,
                     int dexFlags, String compilerFilter, @Nullable String volumeUuid,
-                    @Nullable String sharedLibraries, @Nullable String seInfo) throws InstallerException {
+                    @Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade)
+                    throws InstallerException {
                 final StringBuilder builder = new StringBuilder();
 
-                // The version. Right now it's 2.
-                builder.append("2 ");
+                // The version. Right now it's 3.
+                builder.append("3 ");
 
                 builder.append("dexopt");
 
@@ -304,6 +279,7 @@
                 encodeParameter(builder, volumeUuid);
                 encodeParameter(builder, sharedLibraries);
                 encodeParameter(builder, seInfo);
+                encodeParameter(builder, downgrade);
 
                 commands.add(builder.toString());
             }
@@ -343,12 +319,14 @@
                 getCompilerFilterForReason(compilationReason),
                 null /* CompilerStats.PackageStats */,
                 mPackageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName),
-                true /* bootComplete */);
+                true /* bootComplete */,
+                false /* downgrade */);
 
         mPackageManagerService.getDexManager().dexoptSecondaryDex(pkg.packageName,
                 getCompilerFilterForReason(compilationReason),
                 false /* force */,
-                false /* compileOnlySharedDex */);
+                false /* compileOnlySharedDex */,
+                false /* downgrade */);
         return commands;
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 2b7bd34..3eaeb6d 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -114,7 +114,7 @@
     int performDexOpt(PackageParser.Package pkg, String[] sharedLibraries,
             String[] instructionSets, boolean checkProfiles, String targetCompilationFilter,
             CompilerStats.PackageStats packageStats, boolean isUsedByOtherApps,
-            boolean bootComplete) {
+            boolean bootComplete, boolean downgrade) {
         if (!canOptimizePackage(pkg)) {
             return DEX_OPT_SKIPPED;
         }
@@ -122,7 +122,8 @@
             final long acquireTime = acquireWakeLockLI(pkg.applicationInfo.uid);
             try {
                 return performDexOptLI(pkg, sharedLibraries, instructionSets, checkProfiles,
-                        targetCompilationFilter, packageStats, isUsedByOtherApps, bootComplete);
+                        targetCompilationFilter, packageStats, isUsedByOtherApps, bootComplete,
+                        downgrade);
             } finally {
                 releaseWakeLockLI(acquireTime);
             }
@@ -137,7 +138,7 @@
     private int performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries,
             String[] targetInstructionSets, boolean checkForProfileUpdates,
             String targetCompilerFilter, CompilerStats.PackageStats packageStats,
-            boolean isUsedByOtherApps, boolean bootComplete) {
+            boolean isUsedByOtherApps, boolean bootComplete, boolean downgrade) {
         final String[] instructionSets = targetInstructionSets != null ?
                 targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
         final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
@@ -174,7 +175,8 @@
             }
             for (String dexCodeIsa : dexCodeInstructionSets) {
                 int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter, profileUpdated,
-                        sharedLibrariesPathWithSplits, dexoptFlags, sharedGid, packageStats);
+                        sharedLibrariesPathWithSplits, dexoptFlags, sharedGid, packageStats,
+                        downgrade);
                 // The end result is:
                 //  - FAILED if any path failed,
                 //  - PERFORMED if at least one path needed compilation,
@@ -198,8 +200,8 @@
     @GuardedBy("mInstallLock")
     private int dexOptPath(PackageParser.Package pkg, String path, String isa,
             String compilerFilter, boolean profileUpdated, String sharedLibrariesPath,
-            int dexoptFlags, int uid, CompilerStats.PackageStats packageStats) {
-        int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, profileUpdated);
+            int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade) {
+        int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, profileUpdated, downgrade);
         if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
             return DEX_OPT_SKIPPED;
         }
@@ -218,8 +220,12 @@
         try {
             long startTime = System.currentTimeMillis();
 
+            // TODO: Consider adding 2 different APIs for primary and secondary dexopt.
+            // installd only uses downgrade flag for secondary dex files and ignores it for
+            // primary dex files.
             mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
-                    compilerFilter, pkg.volumeUuid, sharedLibrariesPath, pkg.applicationInfo.seInfo);
+                    compilerFilter, pkg.volumeUuid, sharedLibrariesPath, pkg.applicationInfo.seInfo,
+                    false /* downgrade*/);
 
             if (packageStats != null) {
                 long endTime = System.currentTimeMillis();
@@ -247,12 +253,12 @@
      * that seems wasteful.
      */
     public int dexOptSecondaryDexPath(ApplicationInfo info, String path, Set<String> isas,
-            String compilerFilter, boolean isUsedByOtherApps) {
+            String compilerFilter, boolean isUsedByOtherApps, boolean downgrade) {
         synchronized (mInstallLock) {
             final long acquireTime = acquireWakeLockLI(info.uid);
             try {
                 return dexOptSecondaryDexPathLI(info, path, isas, compilerFilter,
-                        isUsedByOtherApps);
+                        isUsedByOtherApps, downgrade);
             } finally {
                 releaseWakeLockLI(acquireTime);
             }
@@ -294,7 +300,7 @@
 
     @GuardedBy("mInstallLock")
     private int dexOptSecondaryDexPathLI(ApplicationInfo info, String path, Set<String> isas,
-            String compilerFilter, boolean isUsedByOtherApps) {
+            String compilerFilter, boolean isUsedByOtherApps, boolean downgrade) {
         compilerFilter = getRealCompilerFilter(info, compilerFilter, isUsedByOtherApps);
         // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
         // Secondary dex files are currently not compiled at boot.
@@ -324,7 +330,8 @@
                 // TODO(calin): maybe add a separate call.
                 mInstaller.dexopt(path, info.uid, info.packageName, isa, /*dexoptNeeded*/ 0,
                         /*oatDir*/ null, dexoptFlags,
-                        compilerFilter, info.volumeUuid, SKIP_SHARED_LIBRARY_CHECK, info.seInfoUser);
+                        compilerFilter, info.volumeUuid, SKIP_SHARED_LIBRARY_CHECK, info.seInfoUser,
+                        downgrade);
             }
 
             return DEX_OPT_PERFORMED;
@@ -425,10 +432,11 @@
      * configuration (isa, compiler filter, profile).
      */
     private int getDexoptNeeded(String path, String isa, String compilerFilter,
-            boolean newProfile) {
+            boolean newProfile, boolean downgrade) {
         int dexoptNeeded;
         try {
-            dexoptNeeded = DexFile.getDexOptNeeded(path, isa, compilerFilter, newProfile);
+            dexoptNeeded = DexFile.getDexOptNeeded(path, isa, compilerFilter, newProfile,
+                    downgrade);
         } catch (IOException ioe) {
             Slog.w(TAG, "IOException reading apk: " + path, ioe);
             return DEX_OPT_FAILED;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c3a40bf..1810174 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -283,6 +283,7 @@
 import com.android.server.pm.Settings.DatabaseVersion;
 import com.android.server.pm.Settings.VersionInfo;
 import com.android.server.pm.dex.DexManager;
+import com.android.server.pm.dex.PackageDexUsage;
 import com.android.server.storage.DeviceStorageMonitorInternal;
 
 import dalvik.system.CloseGuard;
@@ -561,8 +562,9 @@
     public static final int REASON_INSTALL = 2;
     public static final int REASON_BACKGROUND_DEXOPT = 3;
     public static final int REASON_AB_OTA = 4;
+    public static final int REASON_INACTIVE_PACKAGE_DOWNGRADE = 5;
 
-    public static final int REASON_LAST = REASON_AB_OTA;
+    public static final int REASON_LAST = REASON_INACTIVE_PACKAGE_DOWNGRADE;
 
     /** All dangerous permission names in the same order as the events in MetricsEvent */
     private static final List<String> ALL_DANGEROUS_PERMISSIONS = Arrays.asList(
@@ -9367,7 +9369,8 @@
                     false /* checkProfiles */,
                     compilerFilter,
                     false /* force */,
-                    bootComplete);
+                    bootComplete,
+                    false /* downgrade */);
 
             if (pkg.isSystemApp()) {
                 // Only dexopt shared secondary dex files belonging to system apps to not slow down
@@ -9375,7 +9378,8 @@
                 mDexManager.dexoptSecondaryDex(pkg.packageName,
                         compilerFilter,
                         false /* force */,
-                        true /* compileOnlySharedDex */);
+                        true /* compileOnlySharedDex */,
+                        false /* downgrade */);
             }
 
             // TODO(shubhamajmera): Record secondary dexopt stats.
@@ -9460,14 +9464,16 @@
 
     @Override
     public boolean performDexOpt(String packageName,
-            boolean checkProfiles, int compileReason, boolean force, boolean bootComplete) {
+            boolean checkProfiles, int compileReason, boolean force, boolean bootComplete,
+            boolean downgrade) {
         if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
             return false;
         } else if (isInstantApp(packageName, UserHandle.getCallingUserId())) {
             return false;
         }
         int dexoptStatus = performDexOptWithStatus(
-              packageName, checkProfiles, compileReason, force, bootComplete);
+              packageName, checkProfiles, compileReason, force, bootComplete,
+              downgrade);
         return dexoptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
     }
 
@@ -9478,9 +9484,10 @@
      *  {@link PackageDexOptimizer#DEX_OPT_FAILED}
      */
     /* package */ int performDexOptWithStatus(String packageName,
-            boolean checkProfiles, int compileReason, boolean force, boolean bootComplete) {
+            boolean checkProfiles, int compileReason, boolean force, boolean bootComplete,
+            boolean downgrade) {
         return performDexOptTraced(packageName, checkProfiles,
-                getCompilerFilterForReason(compileReason), force, bootComplete);
+                getCompilerFilterForReason(compileReason), force, bootComplete, downgrade);
     }
 
     @Override
@@ -9493,17 +9500,17 @@
             return false;
         }
         int dexOptStatus = performDexOptTraced(packageName, checkProfiles,
-                targetCompilerFilter, force, bootComplete);
+                targetCompilerFilter, force, bootComplete, false /* downgrade */);
         return dexOptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
     }
 
     private int performDexOptTraced(String packageName,
                 boolean checkProfiles, String targetCompilerFilter, boolean force,
-                boolean bootComplete) {
+                boolean bootComplete, boolean downgrade) {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
         try {
             return performDexOptInternal(packageName, checkProfiles,
-                    targetCompilerFilter, force, bootComplete);
+                    targetCompilerFilter, force, bootComplete, downgrade);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
@@ -9513,7 +9520,7 @@
     // if the package can now be considered up to date for the given filter.
     private int performDexOptInternal(String packageName,
                 boolean checkProfiles, String targetCompilerFilter, boolean force,
-                boolean bootComplete) {
+                boolean bootComplete, boolean downgrade) {
         PackageParser.Package p;
         synchronized (mPackages) {
             p = mPackages.get(packageName);
@@ -9528,7 +9535,7 @@
         try {
             synchronized (mInstallLock) {
                 return performDexOptInternalWithDependenciesLI(p, checkProfiles,
-                        targetCompilerFilter, force, bootComplete);
+                        targetCompilerFilter, force, bootComplete, downgrade);
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
@@ -9549,7 +9556,7 @@
 
     private int performDexOptInternalWithDependenciesLI(PackageParser.Package p,
             boolean checkProfiles, String targetCompilerFilter,
-            boolean force, boolean bootComplete) {
+            boolean force, boolean bootComplete, boolean downgrade) {
         // Select the dex optimizer based on the force parameter.
         // Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to
         //       allocate an object here.
@@ -9574,12 +9581,13 @@
                         targetCompilerFilter,
                         getOrCreateCompilerPackageStats(depPackage),
                         true /* isUsedByOtherApps */,
-                        bootComplete);
+                        bootComplete,
+                        downgrade);
             }
         }
         return pdo.performDexOpt(p, p.usesLibraryFiles, instructionSets, checkProfiles,
                 targetCompilerFilter, getOrCreateCompilerPackageStats(p),
-                mDexManager.isUsedByOtherApps(p.packageName), bootComplete);
+                mDexManager.isUsedByOtherApps(p.packageName), bootComplete, downgrade);
     }
 
     // Performs dexopt on the used secondary dex files belonging to the given package.
@@ -9594,12 +9602,12 @@
             return false;
         }
         return mDexManager.dexoptSecondaryDex(packageName, compilerFilter, force,
-                /* compileOnlySharedDex*/ false);
+                false /* compileOnlySharedDex */, false /* downgrade */);
     }
 
     public boolean performDexOptSecondary(String packageName, int compileReason,
-            boolean force) {
-        return mDexManager.dexoptSecondaryDex(packageName, compileReason, force);
+            boolean force, boolean downgrade) {
+        return mDexManager.dexoptSecondaryDex(packageName, compileReason, force, downgrade);
     }
 
     /**
@@ -9778,7 +9786,8 @@
             final int res = performDexOptInternalWithDependenciesLI(pkg,
                     false /* checkProfiles */, getDefaultCompilerFilter(),
                     true /* force */,
-                    true /* bootComplete */);
+                    true /* bootComplete */,
+                    false /* downgrade */);
 
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
@@ -16199,7 +16208,7 @@
         }
     }
 
-    private void removeDexFiles(List<String> allCodePaths, String[] instructionSets) {
+    void removeDexFiles(List<String> allCodePaths, String[] instructionSets) {
         if (!allCodePaths.isEmpty()) {
             if (instructionSets == null) {
                 throw new IllegalStateException("instructionSet == null");
@@ -18245,7 +18254,8 @@
                         getCompilerFilterForReason(REASON_INSTALL),
                         getOrCreateCompilerPackageStats(pkg),
                         mDexManager.isUsedByOtherApps(pkg.packageName),
-                        true /* bootComplete */);
+                        true /* bootComplete */,
+                        false /* downgrade */);
                 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             }
 
@@ -25007,6 +25017,73 @@
             return mInstantAppRegistry.getInstantAppAndroidIdLPw(packageName, userId);
         }
     }
+
+    boolean canHaveOatDir(String packageName) {
+        synchronized (mPackages) {
+            PackageParser.Package p = mPackages.get(packageName);
+            if (p == null) {
+                return false;
+            }
+            return p.canHaveOatDir();
+        }
+    }
+
+    private String getOatDir(PackageParser.Package pkg) {
+        if (!pkg.canHaveOatDir()) {
+            return null;
+        }
+        File codePath = new File(pkg.codePath);
+        if (codePath.isDirectory()) {
+            return PackageDexOptimizer.getOatDir(codePath).getAbsolutePath();
+        }
+        return null;
+    }
+
+    void deleteOatArtifactsOfPackage(String packageName) {
+        final String[] instructionSets;
+        final List<String> codePaths;
+        final String oatDir;
+        final PackageParser.Package pkg;
+        synchronized (mPackages) {
+            pkg = mPackages.get(packageName);
+        }
+        instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
+        codePaths = pkg.getAllCodePaths();
+        oatDir = getOatDir(pkg);
+
+        for (String codePath : codePaths) {
+            for (String isa : instructionSets) {
+                try {
+                    mInstaller.deleteOdex(codePath, isa, oatDir);
+                } catch (InstallerException e) {
+                    Log.e(TAG, "Failed deleting oat files for " + codePath, e);
+                }
+            }
+        }
+    }
+
+    Set<String> getUnusedPackages(long downgradeTimeThresholdMillis) {
+        Set<String> unusedPackages = new HashSet<>();
+        long currentTimeInMillis = System.currentTimeMillis();
+        synchronized (mPackages) {
+            for (PackageParser.Package pkg : mPackages.values()) {
+                PackageSetting ps =  mSettings.mPackages.get(pkg.packageName);
+                if (ps == null) {
+                    continue;
+                }
+                PackageDexUsage.PackageUseInfo packageUseInfo = getDexManager().getPackageUseInfo(
+                        pkg.packageName);
+                if (PackageManagerServiceUtils
+                        .isUnusedSinceTimeInMillis(ps.firstInstallTime, currentTimeInMillis,
+                                downgradeTimeThresholdMillis, packageUseInfo,
+                                pkg.getLatestPackageUseTimeInMills(),
+                                pkg.getLatestForegroundPackageUseTimeInMills())) {
+                    unusedPackages.add(pkg.packageName);
+                }
+            }
+        }
+        return unusedPackages;
+    }
 }
 
 interface PackageSender {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index ec248f5..1a97a72 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -26,7 +26,7 @@
 public class PackageManagerServiceCompilerMapping {
     // Names for compilation reasons.
     static final String REASON_STRINGS[] = {
-            "first-boot", "boot", "install", "bg-dexopt", "ab-ota"
+            "first-boot", "boot", "install", "bg-dexopt", "ab-ota", "inactive"
     };
 
     // Static block to ensure the strings array is of the right length.
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 9feee8c..9211af6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -16,12 +16,16 @@
 
 package com.android.server.pm;
 
+import com.android.server.pm.dex.DexManager;
+import com.android.server.pm.dex.PackageDexUsage;
+
 import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
 import static com.android.server.pm.PackageManagerService.TAG;
 
 import android.annotation.NonNull;
 import android.app.AppGlobals;
 import android.content.Intent;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageParser;
 import android.content.pm.ResolveInfo;
 import android.os.Build;
@@ -179,6 +183,41 @@
     }
 
     /**
+     * Checks if the package was inactive during since <code>thresholdTimeinMillis</code>.
+     * Package is considered active, if:
+     * 1) It was active in foreground.
+     * 2) It was active in background and also used by other apps.
+     *
+     * If it doesn't have sufficient information about the package, it return <code>false</code>.
+     */
+    static boolean isUnusedSinceTimeInMillis(long firstInstallTime, long currentTimeInMillis,
+            long thresholdTimeinMillis, PackageDexUsage.PackageUseInfo packageUseInfo,
+            long latestPackageUseTimeInMillis, long latestForegroundPackageUseTimeInMillis) {
+
+        if (currentTimeInMillis - firstInstallTime < thresholdTimeinMillis) {
+            return false;
+        }
+
+        // If the app was active in foreground during the threshold period.
+        boolean isActiveInForeground = (currentTimeInMillis
+                - latestForegroundPackageUseTimeInMillis)
+                < thresholdTimeinMillis;
+
+        if (isActiveInForeground) {
+            return false;
+        }
+
+        // If the app was active in background during the threshold period and was used
+        // by other packages.
+        boolean isActiveInBackgroundAndUsedByOtherPackages = ((currentTimeInMillis
+                - latestPackageUseTimeInMillis)
+                < thresholdTimeinMillis)
+                && packageUseInfo.isUsedByOtherApps();
+
+        return !isActiveInBackgroundAndUsedByOtherPackages;
+    }
+
+    /**
      * Returns the canonicalized path of {@code path} as per {@code realpath(3)}
      * semantics.
      */
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index db2d30f..441994d 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -304,10 +304,11 @@
      * @return true if all secondary dex files were processed successfully (compiled or skipped
      *         because they don't need to be compiled)..
      */
-    public boolean dexoptSecondaryDex(String packageName, int compilerReason, boolean force) {
+    public boolean dexoptSecondaryDex(String packageName, int compilerReason, boolean force,
+            boolean downgrade) {
         return dexoptSecondaryDex(packageName,
                 PackageManagerServiceCompilerMapping.getCompilerFilterForReason(compilerReason),
-                force, /* compileOnlySharedDex */ false);
+                force, /* compileOnlySharedDex */ false, downgrade);
     }
 
     /**
@@ -316,7 +317,7 @@
      *         because they don't need to be compiled)..
      */
     public boolean dexoptSecondaryDex(String packageName, String compilerFilter, boolean force,
-            boolean compileOnlySharedDex) {
+            boolean compileOnlySharedDex, boolean downgrade) {
         // Select the dex optimizer based on the force parameter.
         // Forced compilation is done through ForcedUpdatePackageDexOptimizer which will adjust
         // the necessary dexopt flags to make sure that compilation is not skipped. This avoid
@@ -360,7 +361,8 @@
             }
 
             int result = pdo.dexOptSecondaryDexPath(pkg.applicationInfo, dexPath,
-                    dexUseInfo.getLoaderIsas(), compilerFilter, dexUseInfo.isUsedByOtherApps());
+                    dexUseInfo.getLoaderIsas(), compilerFilter, dexUseInfo.isUsedByOtherApps(),
+                    downgrade);
             success = success && (result != PackageDexOptimizer.DEX_OPT_FAILED);
         }
         return success;
@@ -476,7 +478,7 @@
         String compilerFilter = PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
                 PackageManagerService.REASON_INSTALL);
         int result = mPackageDexOptimizer.dexOptSecondaryDexPath(info, dexPath, isas,
-                compilerFilter, isUsedByOtherApps);
+                compilerFilter, isUsedByOtherApps, /* downgrade */ false);
 
         // If we fail to optimize the package log an error but don't propagate the error
         // back to the app. The app cannot do much about it and the background job
diff --git a/services/core/java/com/android/server/radio/Tuner.java b/services/core/java/com/android/server/radio/Tuner.java
index 81128c2..3d16cac 100644
--- a/services/core/java/com/android/server/radio/Tuner.java
+++ b/services/core/java/com/android/server/radio/Tuner.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.hardware.radio.ITuner;
 import android.hardware.radio.ITunerCallback;
+import android.hardware.radio.ProgramSelector;
 import android.hardware.radio.RadioManager;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -44,12 +45,13 @@
     private int mRegion;  // TODO(b/62710330): find better solution to handle regions
     private final boolean mWithAudio;
 
-    Tuner(@NonNull ITunerCallback clientCallback, int halRev, int region, boolean withAudio) {
+    Tuner(@NonNull ITunerCallback clientCallback, int halRev,
+            int region, boolean withAudio, int band) {
         mClientCallback = clientCallback;
         mTunerCallback = new TunerCallback(this, clientCallback, halRev);
         mRegion = region;
         mWithAudio = withAudio;
-        mNativeContext = nativeInit(halRev, withAudio);
+        mNativeContext = nativeInit(halRev, withAudio, band);
         mDeathRecipient = this::close;
         try {
             mClientCallback.asBinder().linkToDeath(mDeathRecipient, 0);
@@ -64,7 +66,7 @@
         super.finalize();
     }
 
-    private native long nativeInit(int halRev, boolean withAudio);
+    private native long nativeInit(int halRev, boolean withAudio, int band);
     private native void nativeFinalize(long nativeContext);
     private native void nativeClose(long nativeContext);
 
@@ -74,9 +76,11 @@
 
     private native void nativeStep(long nativeContext, boolean directionDown, boolean skipSubChannel);
     private native void nativeScan(long nativeContext, boolean directionDown, boolean skipSubChannel);
-    private native void nativeTune(long nativeContext, int channel, int subChannel);
+    private native void nativeTune(long nativeContext, @NonNull ProgramSelector selector);
     private native void nativeCancel(long nativeContext);
 
+    private native void nativeCancelAnnouncement(long nativeContext);
+
     private native RadioManager.ProgramInfo nativeGetProgramInformation(long nativeContext);
     private native boolean nativeStartBackgroundScan(long nativeContext);
     private native List<RadioManager.ProgramInfo> nativeGetProgramList(long nativeContext,
@@ -173,10 +177,14 @@
     }
 
     @Override
-    public void tune(int channel, int subChannel) {
+    public void tune(ProgramSelector selector) {
+        if (selector == null) {
+            throw new IllegalArgumentException("The argument must not be a null pointer");
+        }
+        Slog.i(TAG, "Tuning to " + selector);
         synchronized (mLock) {
             checkNotClosedLocked();
-            nativeTune(mNativeContext, channel, subChannel);
+            nativeTune(mNativeContext, selector);
         }
     }
 
@@ -189,6 +197,14 @@
     }
 
     @Override
+    public void cancelAnnouncement() {
+        synchronized (mLock) {
+            checkNotClosedLocked();
+            nativeCancelAnnouncement(mNativeContext);
+        }
+    }
+
+    @Override
     public RadioManager.ProgramInfo getProgramInformation() {
         synchronized (mLock) {
             checkNotClosedLocked();
diff --git a/services/core/java/com/android/server/radio/TunerCallback.java b/services/core/java/com/android/server/radio/TunerCallback.java
index 9430dc9..62110cf 100644
--- a/services/core/java/com/android/server/radio/TunerCallback.java
+++ b/services/core/java/com/android/server/radio/TunerCallback.java
@@ -86,13 +86,8 @@
     }
 
     @Override
-    public void onProgramInfoChanged(RadioManager.ProgramInfo info) {
-        dispatch(() -> mClientCallback.onProgramInfoChanged(info));
-    }
-
-    @Override
-    public void onMetadataChanged(RadioMetadata metadata) {
-        dispatch(() -> mClientCallback.onMetadataChanged(metadata));
+    public void onProgramInfoChanged() {
+        dispatch(() -> mClientCallback.onProgramInfoChanged());
     }
 
     @Override
diff --git a/services/core/java/com/android/server/timezone/PackageStatusStorage.java b/services/core/java/com/android/server/timezone/PackageStatusStorage.java
index fe82dc4..cac7f7b 100644
--- a/services/core/java/com/android/server/timezone/PackageStatusStorage.java
+++ b/services/core/java/com/android/server/timezone/PackageStatusStorage.java
@@ -16,6 +16,7 @@
 
 package com.android.server.timezone;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.FastXmlSerializer;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -80,7 +81,7 @@
     private final AtomicFile mPackageStatusFile;
 
     PackageStatusStorage(File storageDir) {
-        mPackageStatusFile = new AtomicFile(new File(storageDir, "packageStatus.xml"));
+        mPackageStatusFile = new AtomicFile(new File(storageDir, "package-status.xml"));
         if (!mPackageStatusFile.getBaseFile().exists()) {
             try {
                 insertInitialPackageStatus();
@@ -103,7 +104,7 @@
     PackageStatus getPackageStatus() {
         synchronized (this) {
             try {
-                return getPackageStatusInternal();
+                return getPackageStatusLocked();
             } catch (ParseException e) {
                 // This means that data exists in the file but it was bad.
                 Slog.e(LOG_TAG, "Package status invalid, resetting and retrying", e);
@@ -111,7 +112,7 @@
                 // Reset the storage so it is in a good state again.
                 recoverFromBadData(e);
                 try {
-                    return getPackageStatusInternal();
+                    return getPackageStatusLocked();
                 } catch (ParseException e2) {
                     throw new IllegalStateException("Recovery from bad file failed", e2);
                 }
@@ -119,7 +120,8 @@
         }
     }
 
-    private PackageStatus getPackageStatusInternal() throws ParseException {
+    @GuardedBy("this")
+    private PackageStatus getPackageStatusLocked() throws ParseException {
         try (FileInputStream fis = mPackageStatusFile.openRead()) {
             XmlPullParser parser = parseToPackageStatusTag(fis);
             Integer checkStatus = getNullableIntAttribute(parser, ATTRIBUTE_CHECK_STATUS);
@@ -137,7 +139,7 @@
         }
     }
 
-    // Callers should be synchronized(this).
+    @GuardedBy("this")
     private int recoverFromBadData(Exception cause) {
         mPackageStatusFile.delete();
         try {
@@ -155,7 +157,7 @@
         // is reset to ensure that old tokens are unlikely to work.
         final int initialOptimisticLockId = (int) System.currentTimeMillis();
 
-        writePackageStatusInternal(null /* status */, initialOptimisticLockId,
+        writePackageStatusLocked(null /* status */, initialOptimisticLockId,
                 null /* packageVersions */);
         return initialOptimisticLockId;
     }
@@ -243,7 +245,7 @@
         }
     }
 
-    // Caller should be synchronized(this).
+    @GuardedBy("this")
     private int getCurrentOptimisticLockId() throws ParseException {
         try (FileInputStream fis = mPackageStatusFile.openRead()) {
             XmlPullParser parser = parseToPackageStatusTag(fis);
@@ -278,7 +280,7 @@
         }
     }
 
-    // Caller should be synchronized(this).
+    @GuardedBy("this")
     private boolean writePackageStatusWithOptimisticLockCheck(int optimisticLockId,
             int newOptimisticLockId, Integer status, PackageVersions packageVersions)
             throws IOException {
@@ -294,12 +296,12 @@
             return false;
         }
 
-        writePackageStatusInternal(status, newOptimisticLockId, packageVersions);
+        writePackageStatusLocked(status, newOptimisticLockId, packageVersions);
         return true;
     }
 
-    // Caller should be synchronized(this).
-    private void writePackageStatusInternal(Integer status, int optimisticLockId,
+    @GuardedBy("this")
+    private void writePackageStatusLocked(Integer status, int optimisticLockId,
             PackageVersions packageVersions) throws IOException {
         if ((status == null) != (packageVersions == null)) {
             throw new IllegalArgumentException(
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
index 9f02485..54a6cc0 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -80,7 +80,9 @@
             final Task task = createTask(taskId, stack, userId, bounds, overrideConfig, resizeMode,
                     supportsPictureInPicture, homeTask, taskDescription);
             final int position = toTop ? POSITION_TOP : POSITION_BOTTOM;
-            stack.addTask(task, position, showForAllUsers, true /* moveParents */);
+            // We only want to move the parents to the parents if we are creating this task at the
+            // top of its stack.
+            stack.addTask(task, position, showForAllUsers, toTop /* moveParents */);
         }
     }
 
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index c8c629f..a6065025 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -108,4 +108,6 @@
     android.frameworks.schedulerservice@1.0 \
     android.frameworks.sensorservice@1.0 \
 
-LOCAL_STATIC_LIBRARIES += libscrypt_static
+LOCAL_STATIC_LIBRARIES += \
+    android.hardware.broadcastradio@1.1-utils-lib \
+    libscrypt_static \
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index edd7d53..4fb2ae3 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -1523,6 +1523,10 @@
                           << " satellites:: " << std::endl;
         }
 
+        internalState << "constellation: 1=GPS, 2=SBAS, 3=GLO, 4=QZSS, 5=BDS, 6=GAL; "
+                      << "ephemerisType: 0=Eph, 1=Alm, 2=?; "
+                      << "ephemerisSource: 0=Demod, 1=Supl, 2=Server, 3=?; "
+                      << "ephemerisHealth: 0=Good, 1=Bad, 2=?" << std::endl;
         for (size_t i = 0; i < data.satelliteDataArray.size(); i++) {
             internalState << "svid: " << data.satelliteDataArray[i].svid
                           << ", constellation: "
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 070b808..2db7dbe 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -64,7 +64,7 @@
 static nsecs_t gLastEventTime[USER_ACTIVITY_EVENT_LAST + 1];
 
 // Throttling interval for user activity calls.
-static const nsecs_t MIN_TIME_BETWEEN_USERACTIVITIES = 500 * 1000000L; // 500ms
+static const nsecs_t MIN_TIME_BETWEEN_USERACTIVITIES = 100 * 1000000L; // 100ms
 
 // ----------------------------------------------------------------------------
 
@@ -104,19 +104,6 @@
 }
 
 void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) {
-    // Tell the power HAL when user activity occurs.
-    gPowerHalMutex.lock();
-    if (getPowerHal()) {
-        Return<void> ret;
-        if (gPowerHalV1_1 != nullptr) {
-            ret = gPowerHalV1_1->powerHintAsync(PowerHint::INTERACTION, 0);
-        } else {
-            ret = gPowerHalV1_0->powerHint(PowerHint::INTERACTION, 0);
-        }
-        processReturn(ret, "powerHint");
-    }
-    gPowerHalMutex.unlock();
-
     if (gPowerManagerServiceObj) {
         // Throttle calls into user activity by event type.
         // We're a little conservative about argument checking here in case the caller
@@ -131,6 +118,21 @@
                 return;
             }
             gLastEventTime[eventType] = eventTime;
+
+
+            // Tell the power HAL when user activity occurs.
+            gPowerHalMutex.lock();
+            if (getPowerHal()) {
+              Return<void> ret;
+              if (gPowerHalV1_1 != nullptr) {
+                ret = gPowerHalV1_1->powerHintAsync(PowerHint::INTERACTION, 0);
+              } else {
+                ret = gPowerHalV1_0->powerHint(PowerHint::INTERACTION, 0);
+              }
+              processReturn(ret, "powerHint");
+            }
+            gPowerHalMutex.unlock();
+
         }
 
         JNIEnv* env = AndroidRuntime::getJNIEnv();
diff --git a/services/core/jni/com_android_server_radio_RadioService.cpp b/services/core/jni/com_android_server_radio_RadioService.cpp
index fa64901..459c0d0 100644
--- a/services/core/jni/com_android_server_radio_RadioService.cpp
+++ b/services/core/jni/com_android_server_radio_RadioService.cpp
@@ -210,7 +210,7 @@
     BandConfig bandConfigHal = convert::BandConfigToHal(env, bandConfig, region);
 
     auto tuner = make_javaref(env, env->NewObject(gjni.Tuner.clazz, gjni.Tuner.cstor,
-            callback, halRev, region, withAudio));
+            callback, halRev, region, withAudio, bandConfigHal.type));
     if (tuner == nullptr) {
         ALOGE("Unable to create new tuner object.");
         return nullptr;
@@ -258,7 +258,7 @@
     auto tunerClass = FindClassOrDie(env, "com/android/server/radio/Tuner");
     gjni.Tuner.clazz = MakeGlobalRefOrDie(env, tunerClass);
     gjni.Tuner.cstor = GetMethodIDOrDie(env, tunerClass, "<init>",
-            "(Landroid/hardware/radio/ITunerCallback;IIZ)V");
+            "(Landroid/hardware/radio/ITunerCallback;IIZI)V");
 
     auto arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
     gjni.ArrayList.clazz = MakeGlobalRefOrDie(env, arrayListClass);
diff --git a/services/core/jni/com_android_server_radio_Tuner.cpp b/services/core/jni/com_android_server_radio_Tuner.cpp
index 4b6f30b..d984a0c 100644
--- a/services/core/jni/com_android_server_radio_Tuner.cpp
+++ b/services/core/jni/com_android_server_radio_Tuner.cpp
@@ -22,12 +22,13 @@
 #include "com_android_server_radio_convert.h"
 #include "com_android_server_radio_TunerCallback.h"
 
+#include <JNIHelp.h>
+#include <Utils.h>
 #include <android/hardware/broadcastradio/1.1/IBroadcastRadioFactory.h>
 #include <binder/IPCThreadState.h>
 #include <core_jni_helpers.h>
 #include <media/AudioSystem.h>
 #include <utils/Log.h>
-#include <JNIHelp.h>
 
 namespace android {
 namespace server {
@@ -41,6 +42,7 @@
 namespace V1_0 = hardware::broadcastradio::V1_0;
 namespace V1_1 = hardware::broadcastradio::V1_1;
 
+using V1_0::Band;
 using V1_0::BandConfig;
 using V1_0::MetaData;
 using V1_0::Result;
@@ -79,6 +81,7 @@
     bool mIsClosed = false;
     HalRevision mHalRev;
     bool mWithAudio;
+    Band mBand;
     sp<V1_0::ITuner> mHalTuner;
     sp<V1_1::ITuner> mHalTuner11;
     sp<HalDeathRecipient> mHalDeathRecipient;
@@ -100,13 +103,14 @@
     return getNativeContext(env->GetLongField(jTuner.get(), gjni.Tuner.nativeContext));
 }
 
-static jlong nativeInit(JNIEnv *env, jobject obj, jint halRev, bool withAudio) {
+static jlong nativeInit(JNIEnv *env, jobject obj, jint halRev, bool withAudio, jint band) {
     ALOGV("nativeInit()");
     AutoMutex _l(gContextMutex);
 
     auto ctx = new TunerContext();
     ctx->mHalRev = static_cast<HalRevision>(halRev);
     ctx->mWithAudio = withAudio;
+    ctx->mBand = static_cast<Band>(band);
 
     static_assert(sizeof(jlong) >= sizeof(ctx), "jlong is smaller than a pointer");
     return reinterpret_cast<jlong>(ctx);
@@ -170,13 +174,17 @@
     notifyAudioService(ctx, true);
 }
 
-sp<V1_0::ITuner> getHalTuner(jlong nativeContext) {
-    AutoMutex _l(gContextMutex);
-    auto tuner = getNativeContext(nativeContext).mHalTuner;
+static sp<V1_0::ITuner> getHalTuner(const TunerContext& ctx) {
+    auto tuner = ctx.mHalTuner;
     LOG_ALWAYS_FATAL_IF(tuner == nullptr, "HAL tuner is not open");
     return tuner;
 }
 
+sp<V1_0::ITuner> getHalTuner(jlong nativeContext) {
+    AutoMutex _l(gContextMutex);
+    return getHalTuner(getNativeContext(nativeContext));
+}
+
 sp<V1_1::ITuner> getHalTuner11(jlong nativeContext) {
     AutoMutex _l(gContextMutex);
     return getNativeContext(nativeContext).mHalTuner11;
@@ -215,13 +223,17 @@
 
 static void nativeSetConfiguration(JNIEnv *env, jobject obj, jlong nativeContext, jobject config) {
     ALOGV("nativeSetConfiguration()");
-    auto halTuner = getHalTuner(nativeContext);
+    AutoMutex _l(gContextMutex);
+    auto& ctx = getNativeContext(nativeContext);
+    auto halTuner = getHalTuner(ctx);
     if (halTuner == nullptr) return;
 
     Region region_unused;
     BandConfig bandConfigHal = convert::BandConfigToHal(env, config, region_unused);
 
-    convert::ThrowIfFailed(env, halTuner->setConfiguration(bandConfigHal));
+    if (convert::ThrowIfFailed(env, halTuner->setConfiguration(bandConfigHal))) return;
+
+    ctx.mBand = bandConfigHal.type;
 }
 
 static jobject nativeGetConfiguration(JNIEnv *env, jobject obj, jlong nativeContext,
@@ -263,13 +275,26 @@
     convert::ThrowIfFailed(env, halTuner->scan(dir, skipSubChannel));
 }
 
-static void nativeTune(JNIEnv *env, jobject obj, jlong nativeContext,
-        jint channel, jint subChannel) {
-    ALOGV("nativeTune(%d, %d)", channel, subChannel);
-    auto halTuner = getHalTuner(nativeContext);
-    if (halTuner == nullptr) return;
+static void nativeTune(JNIEnv *env, jobject obj, jlong nativeContext, jobject jSelector) {
+    ALOGV("nativeTune()");
+    AutoMutex _l(gContextMutex);
+    auto& ctx = getNativeContext(nativeContext);
+    auto halTuner10 = getHalTuner(ctx);
+    auto halTuner11 = ctx.mHalTuner11;
+    if (halTuner10 == nullptr) return;
 
-    convert::ThrowIfFailed(env, halTuner->tune(channel, subChannel));
+    auto selector = convert::ProgramSelectorToHal(env, jSelector);
+    if (halTuner11 != nullptr) {
+        convert::ThrowIfFailed(env, halTuner11->tune_1_1(selector));
+    } else {
+        uint32_t channel, subChannel;
+        if (!V1_1::utils::getLegacyChannel(selector, &channel, &subChannel)) {
+            jniThrowException(env, "java/lang/IllegalArgumentException",
+                    "Can't tune to non-AM/FM channel with HAL<1.1");
+            return;
+        }
+        convert::ThrowIfFailed(env, halTuner10->tune(channel, subChannel));
+    }
 }
 
 static void nativeCancel(JNIEnv *env, jobject obj, jlong nativeContext) {
@@ -280,10 +305,23 @@
     convert::ThrowIfFailed(env, halTuner->cancel());
 }
 
+static void nativeCancelAnnouncement(JNIEnv *env, jobject obj, jlong nativeContext) {
+    ALOGV("%s", __func__);
+    auto halTuner = getHalTuner11(nativeContext);
+    if (halTuner == nullptr) {
+        ALOGI("cancelling announcements is not supported with HAL < 1.1");
+        return;
+    }
+
+    convert::ThrowIfFailed(env, halTuner->cancelAnnouncement());
+}
+
 static jobject nativeGetProgramInformation(JNIEnv *env, jobject obj, jlong nativeContext) {
     ALOGV("nativeGetProgramInformation()");
-    auto halTuner10 = getHalTuner(nativeContext);
-    auto halTuner11 = getHalTuner11(nativeContext);
+    AutoMutex _l(gContextMutex);
+    auto& ctx = getNativeContext(nativeContext);
+    auto halTuner10 = getHalTuner(ctx);
+    auto halTuner11 = ctx.mHalTuner11;
     if (halTuner10 == nullptr) return nullptr;
 
     JavaRef<jobject> jInfo;
@@ -301,7 +339,7 @@
                 const V1_0::ProgramInfo& info) {
             halResult = result;
             if (result != Result::OK) return;
-            jInfo = convert::ProgramInfoFromHal(env, info);
+            jInfo = convert::ProgramInfoFromHal(env, info, ctx.mBand);
         });
     }
 
@@ -402,7 +440,7 @@
 }
 
 static const JNINativeMethod gTunerMethods[] = {
-    { "nativeInit", "(IZ)J", (void*)nativeInit },
+    { "nativeInit", "(IZI)J", (void*)nativeInit },
     { "nativeFinalize", "(J)V", (void*)nativeFinalize },
     { "nativeClose", "(J)V", (void*)nativeClose },
     { "nativeSetConfiguration", "(JLandroid/hardware/radio/RadioManager$BandConfig;)V",
@@ -411,8 +449,9 @@
             (void*)nativeGetConfiguration },
     { "nativeStep", "(JZZ)V", (void*)nativeStep },
     { "nativeScan", "(JZZ)V", (void*)nativeScan },
-    { "nativeTune", "(JII)V", (void*)nativeTune },
+    { "nativeTune", "(JLandroid/hardware/radio/ProgramSelector;)V", (void*)nativeTune },
     { "nativeCancel", "(J)V", (void*)nativeCancel },
+    { "nativeCancelAnnouncement", "(J)V", (void*)nativeCancelAnnouncement },
     { "nativeGetProgramInformation", "(J)Landroid/hardware/radio/RadioManager$ProgramInfo;",
             (void*)nativeGetProgramInformation },
     { "nativeStartBackgroundScan", "(J)Z", (void*)nativeStartBackgroundScan },
diff --git a/services/core/jni/com_android_server_radio_TunerCallback.cpp b/services/core/jni/com_android_server_radio_TunerCallback.cpp
index 8df92ae7..815a212 100644
--- a/services/core/jni/com_android_server_radio_TunerCallback.cpp
+++ b/services/core/jni/com_android_server_radio_TunerCallback.cpp
@@ -22,9 +22,10 @@
 #include "com_android_server_radio_convert.h"
 #include "com_android_server_radio_Tuner.h"
 
+#include <JNIHelp.h>
+#include <Utils.h>
 #include <core_jni_helpers.h>
 #include <utils/Log.h>
-#include <JNIHelp.h>
 
 namespace android {
 namespace server {
@@ -37,11 +38,13 @@
 namespace V1_0 = hardware::broadcastradio::V1_0;
 namespace V1_1 = hardware::broadcastradio::V1_1;
 
+using V1_0::Band;
 using V1_0::BandConfig;
 using V1_0::MetaData;
 using V1_0::Result;
 using V1_1::ITunerCallback;
 using V1_1::ProgramListResult;
+using V1_1::ProgramSelector;
 
 static JavaVM *gvm = nullptr;
 
@@ -53,7 +56,6 @@
         jmethodID onError;
         jmethodID onConfigurationChanged;
         jmethodID onProgramInfoChanged;
-        jmethodID onMetadataChanged;
         jmethodID onTrafficAnnouncement;
         jmethodID onEmergencyAnnouncement;
         jmethodID onAntennaState;
@@ -82,6 +84,8 @@
     NativeCallbackThread mCallbackThread;
     HalRevision mHalRev;
 
+    Band mBand;
+
     DISALLOW_COPY_AND_ASSIGN(NativeCallback);
 
 public:
@@ -99,11 +103,12 @@
     virtual Return<void> emergencyAnnouncement(bool active);
     virtual Return<void> newMetadata(uint32_t channel, uint32_t subChannel,
             const hidl_vec<MetaData>& metadata);
-    virtual Return<void> tuneComplete_1_1(Result result, const V1_1::ProgramInfo& info);
-    virtual Return<void> afSwitch_1_1(const V1_1::ProgramInfo& info);
+    virtual Return<void> tuneComplete_1_1(Result result, const ProgramSelector& selector);
+    virtual Return<void> afSwitch_1_1(const ProgramSelector& selector);
     virtual Return<void> backgroundScanAvailable(bool isAvailable);
     virtual Return<void> backgroundScanComplete(ProgramListResult result);
     virtual Return<void> programListChanged();
+    virtual Return<void> programInfoChanged();
 };
 
 struct TunerCallbackContext {
@@ -171,24 +176,20 @@
     ALOGV("tuneComplete(%d)", result);
 
     if (mHalRev > HalRevision::V1_0) {
-        ALOGD("1.0 callback was ignored");
+        ALOGW("1.0 callback was ignored");
         return Return<void>();
     }
 
-    V1_1::ProgramInfo info_1_1 {
-        .base = info,
-    };
-    return tuneComplete_1_1(result, info_1_1);
+    auto selector = V1_1::utils::make_selector(mBand, info.channel, info.subChannel);
+    return tuneComplete_1_1(result, selector);
 }
 
-Return<void> NativeCallback::tuneComplete_1_1(Result result, const V1_1::ProgramInfo& info) {
+Return<void> NativeCallback::tuneComplete_1_1(Result result, const ProgramSelector& selector) {
     ALOGV("tuneComplete_1_1(%d)", result);
 
-    mCallbackThread.enqueue([result, info, this](JNIEnv *env) {
+    mCallbackThread.enqueue([result, this](JNIEnv *env) {
         if (result == Result::OK) {
-            auto jInfo = convert::ProgramInfoFromHal(env, info);
-            if (jInfo == nullptr) return;
-            env->CallVoidMethod(mJCallback, gjni.TunerCallback.onProgramInfoChanged, jInfo.get());
+            env->CallVoidMethod(mJCallback, gjni.TunerCallback.onProgramInfoChanged);
         } else {
             TunerError cause = TunerError::CANCELLED;
             if (result == Result::TIMEOUT) cause = TunerError::SCAN_TIMEOUT;
@@ -204,9 +205,9 @@
     return tuneComplete(Result::OK, info);
 }
 
-Return<void> NativeCallback::afSwitch_1_1(const V1_1::ProgramInfo& info) {
+Return<void> NativeCallback::afSwitch_1_1(const ProgramSelector& selector) {
     ALOGV("afSwitch_1_1()");
-    return tuneComplete_1_1(Result::OK, info);
+    return tuneComplete_1_1(Result::OK, selector);
 }
 
 Return<void> NativeCallback::antennaStateChange(bool connected) {
@@ -244,10 +245,13 @@
     // channel and subChannel are not used
     ALOGV("newMetadata(%d, %d)", channel, subChannel);
 
+    if (mHalRev > HalRevision::V1_0) {
+        ALOGW("1.0 callback was ignored");
+        return Return<void>();
+    }
+
     mCallbackThread.enqueue([this, metadata](JNIEnv *env) {
-        auto jMetadata = convert::MetadataFromHal(env, metadata);
-        if (jMetadata == nullptr) return;
-        env->CallVoidMethod(mJCallback, gjni.TunerCallback.onMetadataChanged, jMetadata.get());
+        env->CallVoidMethod(mJCallback, gjni.TunerCallback.onProgramInfoChanged);
     });
 
     return Return<void>();
@@ -290,6 +294,16 @@
     return Return<void>();
 }
 
+Return<void> NativeCallback::programInfoChanged() {
+    ALOGV("programInfoChanged()");
+
+    mCallbackThread.enqueue([this](JNIEnv *env) {
+        env->CallVoidMethod(mJCallback, gjni.TunerCallback.onProgramInfoChanged);
+    });
+
+    return Return<void>();
+}
+
 static TunerCallbackContext& getNativeContext(jlong nativeContextHandle) {
     auto nativeContext = reinterpret_cast<TunerCallbackContext*>(nativeContextHandle);
     LOG_ALWAYS_FATAL_IF(nativeContext == nullptr, "Native context not initialized");
@@ -363,9 +377,7 @@
     gjni.TunerCallback.onConfigurationChanged = GetMethodIDOrDie(env, tunerCbClass,
             "onConfigurationChanged", "(Landroid/hardware/radio/RadioManager$BandConfig;)V");
     gjni.TunerCallback.onProgramInfoChanged = GetMethodIDOrDie(env, tunerCbClass,
-            "onProgramInfoChanged", "(Landroid/hardware/radio/RadioManager$ProgramInfo;)V");
-    gjni.TunerCallback.onMetadataChanged = GetMethodIDOrDie(env, tunerCbClass,
-            "onMetadataChanged", "(Landroid/hardware/radio/RadioMetadata;)V");
+            "onProgramInfoChanged", "()V");
     gjni.TunerCallback.onTrafficAnnouncement = GetMethodIDOrDie(env, tunerCbClass,
             "onTrafficAnnouncement", "(Z)V");
     gjni.TunerCallback.onEmergencyAnnouncement = GetMethodIDOrDie(env, tunerCbClass,
diff --git a/services/core/jni/com_android_server_radio_convert.cpp b/services/core/jni/com_android_server_radio_convert.cpp
index 55fb10f..6cad7f8 100644
--- a/services/core/jni/com_android_server_radio_convert.cpp
+++ b/services/core/jni/com_android_server_radio_convert.cpp
@@ -19,9 +19,10 @@
 
 #include "com_android_server_radio_convert.h"
 
+#include <JNIHelp.h>
+#include <Utils.h>
 #include <core_jni_helpers.h>
 #include <utils/Log.h>
-#include <JNIHelp.h>
 
 namespace android {
 namespace server {
@@ -37,7 +38,9 @@
 using V1_0::MetadataType;
 using V1_0::Result;
 using V1_0::Rds;
+using V1_1::ProgramIdentifier;
 using V1_1::ProgramListResult;
+using V1_1::ProgramSelector;
 
 static JavaRef<jobject> BandDescriptorFromHal(JNIEnv *env, const V1_0::BandConfig &config, Region region);
 
@@ -90,6 +93,22 @@
     struct {
         jclass clazz;
         jmethodID cstor;
+        jfieldID programType;
+        jfieldID primaryId;
+        jfieldID secondaryIds;
+        jfieldID vendorIds;
+
+        struct {
+            jclass clazz;
+            jmethodID cstor;
+            jfieldID type;
+            jfieldID value;
+        } Identifier;
+    } ProgramSelector;
+
+    struct {
+        jclass clazz;
+        jmethodID cstor;
         jmethodID putIntFromNative;
         jmethodID putStringFromNative;
         jmethodID putBitmapFromNative;
@@ -166,6 +185,44 @@
     ALOGE_IF(res != JNI_OK, "Couldn't throw parcelable runtime exception");
 }
 
+static JavaRef<jintArray> ArrayFromHal(JNIEnv *env, const hidl_vec<uint32_t>& vec) {
+    auto jArr = make_javaref(env, env->NewIntArray(vec.size()));
+    auto jArrElements = env->GetIntArrayElements(jArr.get(), nullptr);
+    for (size_t i = 0; i < vec.size(); i++) {
+        jArrElements[i] = vec[i];
+    }
+    env->ReleaseIntArrayElements(jArr.get(), jArrElements, 0);
+    return jArr;
+}
+
+static JavaRef<jlongArray> ArrayFromHal(JNIEnv *env, const hidl_vec<uint64_t>& vec) {
+    auto jArr = make_javaref(env, env->NewLongArray(vec.size()));
+    auto jArrElements = env->GetLongArrayElements(jArr.get(), nullptr);
+    for (size_t i = 0; i < vec.size(); i++) {
+        jArrElements[i] = vec[i];
+    }
+    env->ReleaseLongArrayElements(jArr.get(), jArrElements, 0);
+    return jArr;
+}
+
+template <typename T>
+static JavaRef<jobjectArray> ArrayFromHal(JNIEnv *env, const hidl_vec<T>& vec,
+        jclass jElementClass, std::function<JavaRef<jobject>(JNIEnv*, const T&)> converter) {
+    auto jArr = make_javaref(env, env->NewObjectArray(vec.size(), jElementClass, nullptr));
+    for (size_t i = 0; i < vec.size(); i++) {
+        auto jElement = converter(env, vec[i]);
+        env->SetObjectArrayElement(jArr.get(), i, jElement.get());
+    }
+    return jArr;
+}
+
+template <typename T>
+static JavaRef<jobjectArray> ArrayFromHal(JNIEnv *env, const hidl_vec<T>& vec,
+        jclass jElementClass, JavaRef<jobject>(*converter)(JNIEnv*, const T&)) {
+    return ArrayFromHal(env, vec, jElementClass,
+            std::function<JavaRef<jobject>(JNIEnv*, const T&)>(converter));
+}
+
 static Rds RdsForRegion(bool rds, Region region) {
     if (!rds) return Rds::NONE;
 
@@ -201,6 +258,7 @@
 static JavaRef<jobject> ModulePropertiesFromHal(JNIEnv *env, const V1_0::Properties &prop10,
         const V1_1::Properties *prop11, jint moduleId, const std::string& serviceName) {
     ALOGV("ModulePropertiesFromHal()");
+    using namespace std::placeholders;
 
     auto jServiceName = make_javastr(env, serviceName);
     auto jImplementor = make_javastr(env, prop10.implementor);
@@ -209,21 +267,19 @@
     auto jSerial = make_javastr(env, prop10.serial);
     bool isBgScanSupported = prop11 ? prop11->supportsBackgroundScanning : false;
     auto jVendorExtension = prop11 ? make_javastr(env, prop11->vendorExension) : nullptr;
-
-    auto jBands = make_javaref(env, env->NewObjectArray(prop10.bands.size(),
-            gjni.BandDescriptor.clazz, nullptr));
-    int i = 0;
-    for (auto &&band : prop10.bands) {
-        // ITU_1 is the default region just because its index is 0.
-        auto jBand = BandDescriptorFromHal(env, band, Region::ITU_1);
-        env->SetObjectArrayElement(jBands.get(), i++, jBand.get());
-    }
+    // ITU_1 is the default region just because its index is 0.
+    auto jBands = ArrayFromHal<V1_0::BandConfig>(env, prop10.bands, gjni.BandDescriptor.clazz,
+        std::bind(BandDescriptorFromHal, _1, _2, Region::ITU_1));
+    auto jSupportedProgramTypes =
+            prop11 ? ArrayFromHal(env, prop11->supportedProgramTypes) : nullptr;
+    auto jSupportedIdentifierTypes =
+            prop11 ? ArrayFromHal(env, prop11->supportedIdentifierTypes) : nullptr;
 
     return make_javaref(env, env->NewObject(gjni.ModuleProperties.clazz,
             gjni.ModuleProperties.cstor, moduleId, jServiceName.get(), prop10.classId,
             jImplementor.get(), jProduct.get(), jVersion.get(), jSerial.get(), prop10.numTuners,
             prop10.numAudioSources, prop10.supportsCapture, jBands.get(), isBgScanSupported,
-            jVendorExtension.get()));
+            jSupportedProgramTypes.get(), jSupportedIdentifierTypes.get(), jVendorExtension.get()));
 }
 
 JavaRef<jobject> ModulePropertiesFromHal(JNIEnv *env, const V1_0::Properties &properties,
@@ -383,25 +439,89 @@
     return jMetadata;
 }
 
+static JavaRef<jobject> ProgramIdentifierFromHal(JNIEnv *env, const ProgramIdentifier &id) {
+    ALOGV("ProgramIdentifierFromHal()");
+    return make_javaref(env, env->NewObject(gjni.ProgramSelector.Identifier.clazz,
+            gjni.ProgramSelector.Identifier.cstor, id.type, id.value));
+}
+
+static JavaRef<jobject> ProgramSelectorFromHal(JNIEnv *env, const ProgramSelector &selector) {
+    ALOGV("ProgramSelectorFromHal()");
+    auto jPrimary = ProgramIdentifierFromHal(env, selector.primaryId);
+    auto jSecondary = ArrayFromHal(env, selector.secondaryIds,
+            gjni.ProgramSelector.Identifier.clazz, ProgramIdentifierFromHal);
+    auto jVendor = ArrayFromHal(env, selector.vendorIds);
+
+    return make_javaref(env, env->NewObject(gjni.ProgramSelector.clazz, gjni.ProgramSelector.cstor,
+            selector.programType, jPrimary.get(), jSecondary.get(), jVendor.get()));
+}
+
+static ProgramIdentifier ProgramIdentifierToHal(JNIEnv *env, jobject jId) {
+    ALOGV("ProgramIdentifierToHal()");
+
+    ProgramIdentifier id = {};
+    id.type = env->GetIntField(jId, gjni.ProgramSelector.Identifier.type);
+    id.value = env->GetLongField(jId, gjni.ProgramSelector.Identifier.value);
+    return id;
+}
+
+ProgramSelector ProgramSelectorToHal(JNIEnv *env, jobject jSelector) {
+    ALOGV("ProgramSelectorToHal()");
+
+    ProgramSelector selector = {};
+
+    selector.programType = env->GetIntField(jSelector, gjni.ProgramSelector.programType);
+
+    auto jPrimary = env->GetObjectField(jSelector, gjni.ProgramSelector.primaryId);
+    auto jSecondary = reinterpret_cast<jobjectArray>(
+            env->GetObjectField(jSelector, gjni.ProgramSelector.secondaryIds));
+    auto jVendor = reinterpret_cast<jlongArray>(
+            env->GetObjectField(jSelector, gjni.ProgramSelector.vendorIds));
+
+    if (jPrimary == nullptr || jSecondary == nullptr || jVendor == nullptr) {
+        ALOGE("ProgramSelector object is incomplete");
+        return {};
+    }
+
+    selector.primaryId = ProgramIdentifierToHal(env, jPrimary);
+    auto count = env->GetArrayLength(jSecondary);
+    selector.secondaryIds.resize(count);
+    for (jsize i = 0; i < count; i++) {
+        auto jId = env->GetObjectArrayElement(jSecondary, i);
+        selector.secondaryIds[i] = ProgramIdentifierToHal(env, jId);
+    }
+
+    count = env->GetArrayLength(jVendor);
+    selector.vendorIds.resize(count);
+    auto jVendorElements = env->GetLongArrayElements(jVendor, nullptr);
+    for (jint i = 0; i < count; i++) {
+        selector.vendorIds[i] = jVendorElements[i];
+    }
+    env->ReleaseLongArrayElements(jVendor, jVendorElements, 0);
+
+    return selector;
+}
+
 static JavaRef<jobject> ProgramInfoFromHal(JNIEnv *env, const V1_0::ProgramInfo &info10,
-        const V1_1::ProgramInfo *info11) {
+        const V1_1::ProgramInfo *info11, const ProgramSelector &selector) {
     ALOGV("ProgramInfoFromHal()");
 
     auto jMetadata = MetadataFromHal(env, info10.metadata);
     auto jVendorExtension = info11 ? make_javastr(env, info11->vendorExension) : nullptr;
+    auto jSelector = ProgramSelectorFromHal(env, selector);
 
     return make_javaref(env, env->NewObject(gjni.ProgramInfo.clazz, gjni.ProgramInfo.cstor,
-            info10.channel, info10.subChannel, info10.tuned, info10.stereo, info10.digital,
-            info10.signalStrength, jMetadata.get(), info11 ? info11->flags : 0,
-            jVendorExtension.get()));
+            jSelector.get(), info10.tuned, info10.stereo, info10.digital, info10.signalStrength,
+            jMetadata.get(), info11 ? info11->flags : 0, jVendorExtension.get()));
 }
 
-JavaRef<jobject> ProgramInfoFromHal(JNIEnv *env, const V1_0::ProgramInfo &info) {
-    return ProgramInfoFromHal(env, info, nullptr);
+JavaRef<jobject> ProgramInfoFromHal(JNIEnv *env, const V1_0::ProgramInfo &info, V1_0::Band band) {
+    auto selector = V1_1::utils::make_selector(band, info.channel, info.subChannel);
+    return ProgramInfoFromHal(env, info, nullptr, selector);
 }
 
 JavaRef<jobject> ProgramInfoFromHal(JNIEnv *env, const V1_1::ProgramInfo &info) {
-    return ProgramInfoFromHal(env, info.base, &info);
+    return ProgramInfoFromHal(env, info.base, &info, info.selector);
 }
 
 } // namespace convert
@@ -460,12 +580,36 @@
     gjni.ModuleProperties.cstor = GetMethodIDOrDie(env, modulePropertiesClass, "<init>",
             "(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;"
             "Ljava/lang/String;IIZ[Landroid/hardware/radio/RadioManager$BandDescriptor;Z"
-            "Ljava/lang/String;)V");
+            "[I[ILjava/lang/String;)V");
 
     auto programInfoClass = FindClassOrDie(env, "android/hardware/radio/RadioManager$ProgramInfo");
     gjni.ProgramInfo.clazz = MakeGlobalRefOrDie(env, programInfoClass);
     gjni.ProgramInfo.cstor = GetMethodIDOrDie(env, programInfoClass, "<init>",
-            "(IIZZZILandroid/hardware/radio/RadioMetadata;ILjava/lang/String;)V");
+            "(Landroid/hardware/radio/ProgramSelector;ZZZILandroid/hardware/radio/RadioMetadata;I"
+            "Ljava/lang/String;)V");
+
+    auto programSelectorClass = FindClassOrDie(env, "android/hardware/radio/ProgramSelector");
+    gjni.ProgramSelector.clazz = MakeGlobalRefOrDie(env, programSelectorClass);
+    gjni.ProgramSelector.cstor = GetMethodIDOrDie(env, programSelectorClass, "<init>",
+            "(ILandroid/hardware/radio/ProgramSelector$Identifier;"
+            "[Landroid/hardware/radio/ProgramSelector$Identifier;[J)V");
+    gjni.ProgramSelector.programType = GetFieldIDOrDie(env, programSelectorClass,
+            "mProgramType", "I");
+    gjni.ProgramSelector.primaryId = GetFieldIDOrDie(env, programSelectorClass,
+            "mPrimaryId", "Landroid/hardware/radio/ProgramSelector$Identifier;");
+    gjni.ProgramSelector.secondaryIds = GetFieldIDOrDie(env, programSelectorClass,
+            "mSecondaryIds", "[Landroid/hardware/radio/ProgramSelector$Identifier;");
+    gjni.ProgramSelector.vendorIds = GetFieldIDOrDie(env, programSelectorClass,
+            "mVendorIds", "[J");
+
+    auto progSelIdClass = FindClassOrDie(env, "android/hardware/radio/ProgramSelector$Identifier");
+    gjni.ProgramSelector.Identifier.clazz = MakeGlobalRefOrDie(env, progSelIdClass);
+    gjni.ProgramSelector.Identifier.cstor = GetMethodIDOrDie(env, progSelIdClass,
+            "<init>", "(IJ)V");
+    gjni.ProgramSelector.Identifier.type = GetFieldIDOrDie(env, progSelIdClass,
+            "mType", "I");
+    gjni.ProgramSelector.Identifier.value = GetFieldIDOrDie(env, progSelIdClass,
+            "mValue", "J");
 
     auto radioMetadataClass = FindClassOrDie(env, "android/hardware/radio/RadioMetadata");
     gjni.RadioMetadata.clazz = MakeGlobalRefOrDie(env, radioMetadataClass);
diff --git a/services/core/jni/com_android_server_radio_convert.h b/services/core/jni/com_android_server_radio_convert.h
index 3ba6fed..8b91ced 100644
--- a/services/core/jni/com_android_server_radio_convert.h
+++ b/services/core/jni/com_android_server_radio_convert.h
@@ -45,9 +45,10 @@
 V1_0::Direction DirectionToHal(bool directionDown);
 
 JavaRef<jobject> MetadataFromHal(JNIEnv *env, const hardware::hidl_vec<V1_0::MetaData> &metadata);
-JavaRef<jobject> ProgramInfoFromHal(JNIEnv *env, const V1_0::ProgramInfo &info);
+JavaRef<jobject> ProgramInfoFromHal(JNIEnv *env, const V1_0::ProgramInfo &info, V1_0::Band band);
 JavaRef<jobject> ProgramInfoFromHal(JNIEnv *env, const V1_1::ProgramInfo &info);
 
+V1_1::ProgramSelector ProgramSelectorToHal(JNIEnv *env, jobject jSelector);
 
 void ThrowParcelableRuntimeException(JNIEnv *env, const std::string& msg);
 
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 3ec8380..6c417a9 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -263,6 +263,8 @@
             Preconditions.checkFlagsArgument(selectionFlags,
                     PrintManager.DISABLED_SERVICES | PrintManager.ENABLED_SERVICES);
 
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.READ_PRINT_SERVICES, null);
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
@@ -316,6 +318,8 @@
 
         @Override
         public List<RecommendationInfo> getPrintServiceRecommendations(int userId) {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS, null);
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
@@ -538,6 +542,8 @@
                 int userId) throws RemoteException {
             listener = Preconditions.checkNotNull(listener);
 
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PRINT_SERVICES,
+                    null);
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
@@ -560,6 +566,8 @@
                 int userId) {
             listener = Preconditions.checkNotNull(listener);
 
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PRINT_SERVICES,
+                    null);
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
@@ -583,6 +591,8 @@
                 throws RemoteException {
             listener = Preconditions.checkNotNull(listener);
 
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS, null);
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
@@ -605,6 +615,8 @@
                 IRecommendationsChangeListener listener, int userId) {
             listener = Preconditions.checkNotNull(listener);
 
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS, null);
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
@@ -888,12 +900,12 @@
 
         private int resolveCallingAppEnforcingPermissions(int appId) {
             final int callingUid = Binder.getCallingUid();
-            if (callingUid == 0 || callingUid == Process.SYSTEM_UID
-                    || callingUid == Process.SHELL_UID) {
+            if (callingUid == 0) {
                 return appId;
             }
             final int callingAppId = UserHandle.getAppId(callingUid);
-            if (appId == callingAppId) {
+            if (appId == callingAppId || callingAppId == Process.SHELL_UID
+                    || callingAppId == Process.SYSTEM_UID) {
                 return appId;
             }
             if (mContext.checkCallingPermission(
diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 807703b..f7d2a02 100644
--- a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -76,7 +76,8 @@
     @Mock Vibrator mVibrator;
     @Mock android.media.IRingtonePlayer mRingtonePlayer;
     @Mock Light mLight;
-    @Mock Handler mHandler;
+    @Mock
+    NotificationManagerService.WorkerHandler mHandler;
     @Mock
     NotificationUsageStats mUsageStats;
 
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java b/services/tests/notification/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
new file mode 100644
index 0000000..e527644
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.service.notification.Adjustment;
+import android.service.notification.SnoozeCriterion;
+import android.service.notification.StatusBarNotification;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Objects;
+
+public class NotificationAdjustmentExtractorTest extends NotificationTestCase {
+
+    @Test
+    public void testExtractsAdjustment() {
+        NotificationAdjustmentExtractor extractor = new NotificationAdjustmentExtractor();
+
+        NotificationRecord r = generateRecord();
+
+        Bundle signals = new Bundle();
+        signals.putString(Adjustment.KEY_GROUP_KEY, GroupHelper.AUTOGROUP_KEY);
+        ArrayList<SnoozeCriterion> snoozeCriteria = new ArrayList<>();
+        snoozeCriteria.add(new SnoozeCriterion("n", "n", "n"));
+        signals.putParcelableArrayList(Adjustment.KEY_SNOOZE_CRITERIA, snoozeCriteria);
+        ArrayList<String> people = new ArrayList<>();
+        people.add("you");
+        signals.putStringArrayList(Adjustment.KEY_PEOPLE, people);
+        Adjustment adjustment = new Adjustment("pkg", r.getKey(), signals, "", 0);
+        r.addAdjustment(adjustment);
+
+        assertFalse(r.getGroupKey().contains(GroupHelper.AUTOGROUP_KEY));
+        assertFalse(Objects.equals(people, r.getPeopleOverride()));
+        assertFalse(Objects.equals(snoozeCriteria, r.getSnoozeCriteria()));
+
+        assertNull(extractor.process(r));
+
+        assertTrue(r.getGroupKey().contains(GroupHelper.AUTOGROUP_KEY));
+        assertEquals(people, r.getPeopleOverride());
+        assertEquals(snoozeCriteria, r.getSnoozeCriteria());
+    }
+
+    @Test
+    public void testExtractsAdjustments() {
+        NotificationAdjustmentExtractor extractor = new NotificationAdjustmentExtractor();
+
+        NotificationRecord r = generateRecord();
+
+        Bundle pSignals = new Bundle();
+        ArrayList<String> people = new ArrayList<>();
+        people.add("you");
+        pSignals.putStringArrayList(Adjustment.KEY_PEOPLE, people);
+        Adjustment pAdjustment = new Adjustment("pkg", r.getKey(), pSignals, "", 0);
+        r.addAdjustment(pAdjustment);
+
+        Bundle sSignals = new Bundle();
+        ArrayList<SnoozeCriterion> snoozeCriteria = new ArrayList<>();
+        snoozeCriteria.add(new SnoozeCriterion("n", "n", "n"));
+        sSignals.putParcelableArrayList(Adjustment.KEY_SNOOZE_CRITERIA, snoozeCriteria);
+        Adjustment sAdjustment = new Adjustment("pkg", r.getKey(), sSignals, "", 0);
+        r.addAdjustment(sAdjustment);
+
+        Bundle gSignals = new Bundle();
+        gSignals.putString(Adjustment.KEY_GROUP_KEY, GroupHelper.AUTOGROUP_KEY);
+        Adjustment gAdjustment = new Adjustment("pkg", r.getKey(), gSignals, "", 0);
+        r.addAdjustment(gAdjustment);
+
+        assertFalse(r.getGroupKey().contains(GroupHelper.AUTOGROUP_KEY));
+        assertFalse(Objects.equals(people, r.getPeopleOverride()));
+        assertFalse(Objects.equals(snoozeCriteria, r.getSnoozeCriteria()));
+
+        assertNull(extractor.process(r));
+
+        assertTrue(r.getGroupKey().contains(GroupHelper.AUTOGROUP_KEY));
+        assertEquals(people, r.getPeopleOverride());
+        assertEquals(snoozeCriteria, r.getSnoozeCriteria());
+    }
+
+    private NotificationRecord generateRecord() {
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
+        final Notification.Builder builder = new Notification.Builder(getContext())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+        Notification n = builder.build();
+        StatusBarNotification sbn = new StatusBarNotification("", "", 0, "", 0,
+                0, n, UserHandle.ALL, null, System.currentTimeMillis());
+       return new NotificationRecord(getContext(), sbn, channel);
+    }
+}
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/notification/src/com/android/server/notification/NotificationChannelExtractorTest.java
new file mode 100644
index 0000000..d75213c
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/NotificationChannelExtractorTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class NotificationChannelExtractorTest extends NotificationTestCase {
+
+    @Mock RankingConfig mConfig;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testExtractsUpdatedChannel() {
+        NotificationChannelExtractor extractor = new NotificationChannelExtractor();
+        extractor.setConfig(mConfig);
+
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
+        final Notification.Builder builder = new Notification.Builder(getContext())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+        Notification n = builder.build();
+        StatusBarNotification sbn = new StatusBarNotification("", "", 0, "", 0,
+                0, n, UserHandle.ALL, null, System.currentTimeMillis());
+        NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+
+        NotificationChannel updatedChannel =
+                new NotificationChannel("a", "", IMPORTANCE_HIGH);
+        when(mConfig.getNotificationChannel(any(), anyInt(), eq("a"), eq(false)))
+                .thenReturn(updatedChannel);
+
+        assertNull(extractor.process(r));
+        assertEquals(updatedChannel, r.getChannel());
+    }
+
+}
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java b/services/tests/notification/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java
new file mode 100644
index 0000000..d2f608e
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+
+import org.junit.Test;
+
+public class NotificationIntrusivenessExtractorTest extends NotificationTestCase {
+
+    @Test
+    public void testNonIntrusive() {
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
+        final Notification.Builder builder = new Notification.Builder(getContext())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+        Notification n = builder.build();
+        StatusBarNotification sbn = new StatusBarNotification("", "", 0, "", 0,
+                0, n, UserHandle.ALL, null, System.currentTimeMillis());
+        NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+
+        assertNull(new NotificationIntrusivenessExtractor().process(r));
+    }
+
+    @Test
+    public void testIntrusive_fillScreen() {
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+        final Notification.Builder builder = new Notification.Builder(getContext())
+                .setContentTitle("foo")
+                .setFullScreenIntent(PendingIntent.getActivity(
+                        getContext(), 0, new Intent(""), 0), true)
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+        Notification n = builder.build();
+        StatusBarNotification sbn = new StatusBarNotification("", "", 0, "", 0,
+                0, n, UserHandle.ALL, null, System.currentTimeMillis());
+        NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+
+        assertNotNull(new NotificationIntrusivenessExtractor().process(r));
+    }
+}
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index 1a5814b..ae2253e 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -20,6 +20,9 @@
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 
+import static com.android.server.notification.NotificationManagerService
+        .MESSAGE_SEND_RANKING_UPDATE;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
@@ -31,9 +34,12 @@
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -54,6 +60,9 @@
 import android.graphics.Color;
 import android.media.AudioManager;
 import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
 import android.os.Process;
 import android.os.UserHandle;
 import android.provider.Settings.Secure;
@@ -63,7 +72,22 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
+import android.util.ArrayMap;
 import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.server.lights.Light;
+import com.android.server.lights.LightsManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -71,16 +95,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import com.android.server.lights.Light;
-import com.android.server.lights.LightsManager;
+import java.util.Map;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -109,6 +124,7 @@
     private AudioManager mAudioManager;
     @Mock
     ActivityManager mActivityManager;
+    NotificationManagerService.WorkerHandler mHandler;
 
     private NotificationChannel mTestNotificationChannel = new NotificationChannel(
             TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
@@ -152,6 +168,9 @@
 
         mNotificationManagerService = new TestableNotificationManagerService(mContext);
 
+        // Use this testable looper.
+        mTestableLooper = TestableLooper.get(this);
+        mHandler = mNotificationManagerService.new WorkerHandler(mTestableLooper.getLooper());
         // MockPackageManager - default returns ApplicationInfo with matching calling UID
         final ApplicationInfo applicationInfo = new ApplicationInfo();
         applicationInfo.uid = uid;
@@ -162,8 +181,6 @@
         final LightsManager mockLightsManager = mock(LightsManager.class);
         when(mockLightsManager.getLight(anyInt())).thenReturn(mock(Light.class));
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
-        // Use this testable looper.
-        mTestableLooper = TestableLooper.get(this);
 
         mFile = new File(mContext.getCacheDir(), "test.xml");
         mFile.createNewFile();
@@ -174,10 +191,11 @@
                 null, new ComponentName(PKG, "test_class"), uid, true, null, 0);
         when(mNotificationListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
         try {
-            mNotificationManagerService.init(mTestableLooper.getLooper(), mPackageManager,
-                    mPackageManagerClient, mockLightsManager, mNotificationListeners,
-                    mNotificationAssistants, mConditionProviders, mCompanionMgr,
-                    mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager, mGroupHelper);
+            mNotificationManagerService.init(mTestableLooper.getLooper(),
+                    mPackageManager, mPackageManagerClient, mockLightsManager,
+                    mNotificationListeners, mNotificationAssistants, mConditionProviders,
+                    mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
+                    mGroupHelper);
         } catch (SecurityException e) {
             if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
                 throw e;
@@ -234,6 +252,43 @@
         return new NotificationRecord(mContext, sbn, channel);
     }
 
+    private Map<String, Answer> getSignalExtractorSideEffects() {
+        Map<String, Answer> answers = new ArrayMap<>();
+
+        answers.put("override group key", invocationOnMock -> {
+            ((NotificationRecord) invocationOnMock.getArguments()[0])
+                    .setOverrideGroupKey("bananas");
+            return null;
+        });
+        answers.put("override people", invocationOnMock -> {
+            ((NotificationRecord) invocationOnMock.getArguments()[0])
+                    .setPeopleOverride(new ArrayList<>());
+            return null;
+        });
+        answers.put("snooze criteria", invocationOnMock -> {
+            ((NotificationRecord) invocationOnMock.getArguments()[0])
+                    .setSnoozeCriteria(new ArrayList<>());
+            return null;
+        });
+        answers.put("notification channel", invocationOnMock -> {
+            ((NotificationRecord) invocationOnMock.getArguments()[0])
+                    .updateNotificationChannel(new NotificationChannel("a", "", IMPORTANCE_LOW));
+            return null;
+        });
+        answers.put("badging", invocationOnMock -> {
+            NotificationRecord r = (NotificationRecord) invocationOnMock.getArguments()[0];
+            r.setShowBadge(!r.canShowBadge());
+            return null;
+        });
+        answers.put("package visibility", invocationOnMock -> {
+            ((NotificationRecord) invocationOnMock.getArguments()[0]).setPackageVisibilityOverride(
+                    Notification.VISIBILITY_SECRET);
+            return null;
+        });
+
+        return answers;
+    }
+
     @Test
     public void testCreateNotificationChannels_SingleChannel() throws Exception {
         final NotificationChannel channel =
@@ -1317,11 +1372,59 @@
         mNotificationManagerService.addNotification(otherPackage);
 
         // Same notifications are enqueued as posted, everything counts b/c id and tag don't match
-        assertEquals(40, mNotificationManagerService.getNotificationCountLocked(PKG, new UserHandle(uid).getIdentifier(), 0, null));
-        assertEquals(40, mNotificationManagerService.getNotificationCountLocked(PKG, new UserHandle(uid).getIdentifier(), 0, "tag2"));
-        assertEquals(2, mNotificationManagerService.getNotificationCountLocked("a", new UserHandle(uid).getIdentifier(), 0, "banana"));
+        int userId = new UserHandle(uid).getIdentifier();
+        assertEquals(40, mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, null));
+        assertEquals(40, mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, "tag2"));
+        assertEquals(2, mNotificationManagerService.getNotificationCountLocked("a", userId, 0, "banana"));
 
         // exclude a known notification - it's excluded from only the posted list, not enqueued
-        assertEquals(39, mNotificationManagerService.getNotificationCountLocked(PKG, new UserHandle(uid).getIdentifier(), 0, "tag"));
+        assertEquals(39, mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, "tag"));
+    }
+
+    @Test
+    public void testModifyAutogroup_requestsSort() throws Exception {
+        RankingHandler rh = mock(RankingHandler.class);
+        mNotificationManagerService.setRankingHandler(rh);
+
+        final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mNotificationManagerService.addNotification(r);
+        mNotificationManagerService.addAutogroupKeyLocked(r.getKey());
+        mNotificationManagerService.removeAutogroupKeyLocked(r.getKey());
+
+        verify(rh, times(2)).requestSort();
+    }
+
+    @Test
+    public void testHandleRankingSort_sendsUpdateOnSignalExtractorChange() throws Exception {
+        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        NotificationManagerService.WorkerHandler handler = mock(
+                NotificationManagerService.WorkerHandler.class);
+        mNotificationManagerService.setHandler(handler);
+
+        Map<String, Answer> answers = getSignalExtractorSideEffects();
+        for (String message : answers.keySet()) {
+            mNotificationManagerService.clearNotifications();
+            final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+            mNotificationManagerService.addNotification(r);
+
+            doAnswer(answers.get(message)).when(mRankingHelper).extractSignals(r);
+
+            mNotificationManagerService.handleRankingSort();
+        }
+        verify(handler, times(answers.size())).scheduleSendRankingUpdate();
+    }
+
+    @Test
+    public void testHandleRankingSort_noUpdateWhenNoSignalChange() throws Exception {
+        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        NotificationManagerService.WorkerHandler handler = mock(
+                NotificationManagerService.WorkerHandler.class);
+        mNotificationManagerService.setHandler(handler);
+
+        final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mNotificationManagerService.addNotification(r);
+
+        mNotificationManagerService.handleRankingSort();
+        verify(handler, never()).scheduleSendRankingUpdate();
     }
 }
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index 5a72e6b..801479b 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -78,6 +78,9 @@
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 @SmallTest
@@ -630,6 +633,8 @@
 
         // all fields should be changed
         assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
+
+        verify(mHandler, times(1)).requestSort();
     }
 
     @Test
@@ -712,6 +717,8 @@
         assertFalse(savedChannel.canBypassDnd());
         assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
         assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
+
+        verify(mHandler, never()).requestSort();
     }
 
     @Test
@@ -1058,6 +1065,8 @@
 
         // notDeleted
         assertEquals(1, mHelper.getNotificationChannelGroups(PKG, UID).size());
+
+        verify(mHandler, never()).requestSort();
     }
 
     @Test
@@ -1159,6 +1168,7 @@
         NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
         mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
         assertEquals(ncg, mHelper.getNotificationChannelGroups(PKG, UID).iterator().next());
+        verify(mHandler, never()).requestSort();
     }
 
     @Test
@@ -1275,6 +1285,8 @@
         actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
         assertEquals("goodbye", actual.getName());
         assertEquals(IMPORTANCE_DEFAULT, actual.getImportance());
+
+        verify(mHandler, times(1)).requestSort();
     }
 
     @Test
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index e12032d..59d205e 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -35,7 +35,6 @@
     <uses-permission android:name="android.permission.REORDER_TASKS" />
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
-    <uses-permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.MANAGE_USERS" />
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index ad8303a..11e383c 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -24,15 +24,13 @@
 import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
-import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
-import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
 import static android.net.NetworkPolicyManager.uidPoliciesToString;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
 import static android.net.TrafficStats.KB_IN_BYTES;
 import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
-import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT;
 import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED;
+import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT;
 import static android.telephony.CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG;
 import static android.telephony.CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG;
 import static android.telephony.CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT;
@@ -41,10 +39,10 @@
 import static android.text.format.Time.TIMEZONE_UTC;
 
 import static com.android.server.net.NetworkPolicyManagerService.MAX_PROC_STATE_SEQ_HISTORY;
-import static com.android.server.net.NetworkPolicyManagerService.ProcStateSeqHistory;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
@@ -53,22 +51,19 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.isA;
-import static org.mockito.Matchers.isNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -97,6 +92,7 @@
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
 import android.net.NetworkState;
 import android.net.NetworkStats;
 import android.net.NetworkTemplate;
@@ -112,18 +108,21 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionPlan;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.text.format.Time;
 import android.util.Log;
+import android.util.Pair;
 import android.util.TrustedTime;
 
-import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent;
 import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.net.NetworkPolicyManagerService;
+import com.android.server.net.NetworkPolicyManagerService.ProcStateSeqHistory;
 
 import libcore.io.IoUtils;
 import libcore.io.Streams;
@@ -132,7 +131,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.MethodRule;
@@ -156,8 +154,11 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.time.Instant;
+import java.time.ZonedDateTime;
 import java.util.Arrays;
 import java.util.Calendar;
+import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
@@ -393,6 +394,11 @@
         LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
     }
 
+    @After
+    public void resetClock() throws Exception {
+        SubscriptionPlan.sNowOverride = -1;
+    }
+
     @Test
     public void testTurnRestrictBackgroundOn() throws Exception {
         assertRestrictBackgroundOff(); // Sanity check.
@@ -778,6 +784,25 @@
         assertTrue(mService.isUidForeground(UID_B));
     }
 
+    private static long computeLastCycleBoundary(long currentTime, NetworkPolicy policy) {
+        SubscriptionPlan.sNowOverride = currentTime;
+        final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = NetworkPolicyManager
+                .cycleIterator(policy);
+        while (it.hasNext()) {
+            final Pair<ZonedDateTime, ZonedDateTime> cycle = it.next();
+            if (cycle.first.toInstant().toEpochMilli() < currentTime) {
+                return cycle.first.toInstant().toEpochMilli();
+            }
+        }
+        throw new IllegalStateException(
+                "Failed to find current cycle for " + policy + " at " + currentTime);
+    }
+
+    private static long computeNextCycleBoundary(long currentTime, NetworkPolicy policy) {
+        SubscriptionPlan.sNowOverride = currentTime;
+        return NetworkPolicyManager.cycleIterator(policy).next().second.toInstant().toEpochMilli();
+    }
+
     @Test
     public void testLastCycleBoundaryThisMonth() throws Exception {
         // assume cycle day of "5th", which should be in same month
@@ -818,7 +843,7 @@
     public void testLastCycleBoundaryLastMonthFebruary() throws Exception {
         // assume cycle day of "30th" in february, which should clamp
         final long currentTime = parseTime("2007-03-14T00:00:00.000Z");
-        final long expectedCycle = parseTime("2007-02-28T23:59:59.000Z");
+        final long expectedCycle = parseTime("2007-02-28T23:59:59.999Z");
 
         final NetworkPolicy policy = new NetworkPolicy(
                 sTemplateWifi, 30, TIMEZONE_UTC, 1024L, 1024L, false);
@@ -842,9 +867,9 @@
 
         assertTimeEquals(parseTime("2007-01-29T00:00:00.000Z"),
                 computeNextCycleBoundary(parseTime("2007-01-14T00:00:00.000Z"), policy));
-        assertTimeEquals(parseTime("2007-02-28T23:59:59.000Z"),
+        assertTimeEquals(parseTime("2007-02-28T23:59:59.999Z"),
                 computeNextCycleBoundary(parseTime("2007-02-14T00:00:00.000Z"), policy));
-        assertTimeEquals(parseTime("2007-02-28T23:59:59.000Z"),
+        assertTimeEquals(parseTime("2007-02-28T23:59:59.999Z"),
                 computeLastCycleBoundary(parseTime("2007-03-14T00:00:00.000Z"), policy));
         assertTimeEquals(parseTime("2007-03-29T00:00:00.000Z"),
                 computeNextCycleBoundary(parseTime("2007-03-14T00:00:00.000Z"), policy));
@@ -922,7 +947,7 @@
 
     @Test
     public void testLastCycleBoundaryDST() throws Exception {
-        final long currentTime = parseTime("1989-01-02T07:30:00.000");
+        final long currentTime = parseTime("1989-01-02T07:30:00.000Z");
         final long expectedCycle = parseTime("1988-12-03T02:00:00.000Z");
 
         final NetworkPolicy policy = new NetworkPolicy(
@@ -932,26 +957,16 @@
     }
 
     @Test
-    public void testLastCycleBoundaryJanuaryDST() throws Exception {
-        final long currentTime = parseTime("1989-01-26T21:00:00.000Z");
-        final long expectedCycle = parseTime("1989-01-01T01:59:59.000Z");
-
-        final NetworkPolicy policy = new NetworkPolicy(
-                sTemplateWifi, 32, "America/Argentina/Buenos_Aires", 1024L, 1024L, false);
-        final long actualCycle = computeLastCycleBoundary(currentTime, policy);
-        assertTimeEquals(expectedCycle, actualCycle);
-    }
-
-    @Test
     public void testNetworkPolicyAppliedCycleLastMonth() throws Exception {
         NetworkState[] state = null;
         NetworkStats stats = null;
 
-        final long TIME_FEB_15 = 1171497600000L;
-        final long TIME_MAR_10 = 1173484800000L;
         final int CYCLE_DAY = 15;
+        final long NOW = parseTime("2007-03-10T00:00Z");
+        final long CYCLE_START = parseTime("2007-02-15T00:00Z");
+        final long CYCLE_END = parseTime("2007-03-15T00:00Z");
 
-        setCurrentTimeMillis(TIME_MAR_10);
+        setCurrentTimeMillis(NOW);
 
         // first, pretend that wifi network comes online. no policy active,
         // which means we shouldn't push limit to interface.
@@ -971,7 +986,7 @@
         // pretend that 512 bytes total have happened
         stats = new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 256L, 2L, 256L, 2L);
-        when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15, TIME_MAR_10))
+        when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START, CYCLE_END))
                 .thenReturn(stats.getTotalBytes());
 
         mPolicyListener.expect().onMeteredIfacesChanged(any());
@@ -991,11 +1006,12 @@
         NetworkStats stats = null;
         Future<String> tagFuture = null;
 
-        final long TIME_FEB_15 = 1171497600000L;
-        final long TIME_MAR_10 = 1173484800000L;
         final int CYCLE_DAY = 15;
+        final long NOW = parseTime("2007-03-10T00:00Z");
+        final long CYCLE_START = parseTime("2007-02-15T00:00Z");
+        final long CYCLE_END = parseTime("2007-03-15T00:00Z");
 
-        setCurrentTimeMillis(TIME_MAR_10);
+        setCurrentTimeMillis(NOW);
 
         // assign wifi policy
         state = new NetworkState[] {};
@@ -1005,8 +1021,8 @@
         {
             expectCurrentTime();
             when(mConnManager.getAllNetworkState()).thenReturn(state);
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
-                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
+            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START,
+                    CYCLE_END)).thenReturn(stats.getTotalBytes());
 
             mPolicyListener.expect().onMeteredIfacesChanged(any());
             setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, 1
@@ -1024,8 +1040,8 @@
         {
             expectCurrentTime();
             when(mConnManager.getAllNetworkState()).thenReturn(state);
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
-                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
+            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START,
+                    CYCLE_END)).thenReturn(stats.getTotalBytes());
 
             mPolicyListener.expect().onMeteredIfacesChanged(any());
             mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
@@ -1043,8 +1059,8 @@
 
         {
             expectCurrentTime();
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
-                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
+            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START,
+                    CYCLE_END)).thenReturn(stats.getTotalBytes());
             tagFuture = expectEnqueueNotification();
 
             mNetworkObserver.limitReached(null, TEST_IFACE);
@@ -1061,8 +1077,8 @@
 
         {
             expectCurrentTime();
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
-                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
+            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START,
+                    CYCLE_END)).thenReturn(stats.getTotalBytes());
             tagFuture = expectEnqueueNotification();
 
             mNetworkObserver.limitReached(null, TEST_IFACE);
@@ -1077,8 +1093,8 @@
         {
             expectCurrentTime();
             when(mConnManager.getAllNetworkState()).thenReturn(state);
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
-                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
+            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START,
+                    CYCLE_END)).thenReturn(stats.getTotalBytes());
             tagFuture = expectEnqueueNotification();
 
             mPolicyListener.expect().onMeteredIfacesChanged(any());
@@ -1129,6 +1145,15 @@
     }
 
     @Test
+    public void testConversion() throws Exception {
+        NetworkTemplate template = NetworkTemplate.buildTemplateMobileWildcard();
+        NetworkPolicy before = new NetworkPolicy(template, 12, "Israel", 123, 456, true);
+        NetworkPolicy after = SubscriptionPlan.convert(SubscriptionPlan.convert(before));
+        after.template = before.template;
+        assertEquals(before, after);
+    }
+
+    @Test
     public void testOnUidStateChanged_notifyAMS() throws Exception {
         final long procStateSeq = 222;
         callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE, procStateSeq);
@@ -1470,9 +1495,7 @@
     }
 
     private static long parseTime(String time) {
-        final Time result = new Time();
-        result.parse3339(time);
-        return result.toMillis(true);
+        return ZonedDateTime.parse(time).toInstant().toEpochMilli();
     }
 
     private void setNetworkPolicies(NetworkPolicy... policies) {
@@ -1559,16 +1582,15 @@
     }
 
     private static String formatTime(long millis) {
-        final Time time = new Time(Time.TIMEZONE_UTC);
-        time.set(millis);
-        return time.format3339(false);
+        return Instant.ofEpochMilli(millis) + " [" + millis + "]";
     }
 
     private static void assertEqualsFuzzy(long expected, long actual, long fuzzy) {
         final long low = expected - fuzzy;
         final long high = expected + fuzzy;
         if (actual < low || actual > high) {
-            fail("value " + actual + " is outside [" + low + "," + high + "]");
+            fail("value " + formatTime(actual) + " is outside [" + formatTime(low) + ","
+                    + formatTime(high) + "]");
         }
     }
 
@@ -1643,6 +1665,7 @@
     }
 
     private void setCurrentTimeMillis(long currentTimeMillis) {
+        SubscriptionPlan.sNowOverride = currentTimeMillis;
         mStartTime = currentTimeMillis;
         mElapsedRealtime = 0L;
     }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
index 4c77f62..b0325cb 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
@@ -347,11 +347,11 @@
     }
 
     public void testPersistentData_serializeUnserialize() {
-        byte[] serialized = PersistentData.toBytes(PersistentData.TYPE_GATEKEEPER, SOME_USER_ID,
+        byte[] serialized = PersistentData.toBytes(PersistentData.TYPE_SP, SOME_USER_ID,
                 DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, PAYLOAD);
         PersistentData deserialized = PersistentData.fromBytes(serialized);
 
-        assertEquals(PersistentData.TYPE_GATEKEEPER, deserialized.type);
+        assertEquals(PersistentData.TYPE_SP, deserialized.type);
         assertEquals(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, deserialized.qualityForUi);
         assertArrayEquals(PAYLOAD, deserialized.payload);
     }
@@ -371,7 +371,7 @@
         // the wire format in the future.
         byte[] serializedVersion1 = new byte[] {
                 1, /* PersistentData.VERSION_1 */
-                2, /* PersistentData.TYPE_SP */
+                1, /* PersistentData.TYPE_SP */
                 0x00, 0x00, 0x04, 0x0A,  /* SOME_USER_ID */
                 0x00, 0x03, 0x00, 0x00,  /* PASSWORD_NUMERIC_COMPLEX */
                 1, 2, -1, -2, 33, /* PAYLOAD */
@@ -385,9 +385,8 @@
 
         // Make sure the constants we use on the wire do not change.
         assertEquals(0, PersistentData.TYPE_NONE);
-        assertEquals(1, PersistentData.TYPE_GATEKEEPER);
-        assertEquals(2, PersistentData.TYPE_SP);
-        assertEquals(3, PersistentData.TYPE_SP_WEAVER);
+        assertEquals(1, PersistentData.TYPE_SP);
+        assertEquals(2, PersistentData.TYPE_SP_WEAVER);
     }
 
     public void testCredentialHash_serializeUnserialize() {
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 0d1764b..89c9134 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -24,6 +24,7 @@
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.net.INetworkPolicyManager;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Message;
@@ -37,6 +38,7 @@
 import com.android.internal.telephony.PhoneConstants;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -1539,4 +1541,39 @@
         }
         return false;
     }
+
+    /** {@pending} */
+    public @NonNull List<SubscriptionPlan> getSubscriptionPlans(int subId) {
+        final INetworkPolicyManager npm = INetworkPolicyManager.Stub
+                .asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
+        try {
+            return Arrays.asList(npm.getSubscriptionPlans(subId,
+                    mContext.getOpPackageName()));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@pending} */
+    public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) {
+        final INetworkPolicyManager npm = INetworkPolicyManager.Stub
+                .asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
+        try {
+            npm.setSubscriptionPlans(subId, plans.toArray(new SubscriptionPlan[plans.size()]),
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    public String getSubscriptionPlanOwner(int subId) {
+        final INetworkPolicyManager npm = INetworkPolicyManager.Stub
+                .asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
+        try {
+            return npm.getSubscriptionPlanOwner(subId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/SubscriptionPlan.aidl b/telephony/java/android/telephony/SubscriptionPlan.aidl
new file mode 100755
index 0000000..655df3a
--- /dev/null
+++ b/telephony/java/android/telephony/SubscriptionPlan.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+parcelable SubscriptionPlan;
diff --git a/telephony/java/android/telephony/SubscriptionPlan.java b/telephony/java/android/telephony/SubscriptionPlan.java
new file mode 100644
index 0000000..da7661ae
--- /dev/null
+++ b/telephony/java/android/telephony/SubscriptionPlan.java
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.BytesLong;
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.net.NetworkPolicy;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalUnit;
+import java.util.Iterator;
+
+/** {@pending} */
+public final class SubscriptionPlan implements Parcelable {
+    private static final String TAG = "SubscriptionPlan";
+    private static final boolean DEBUG = false;
+
+    /** {@hide} */
+    @IntDef(prefix = "TYPE_", value = {
+            TYPE_NONRECURRING,
+            TYPE_RECURRING_WEEKLY,
+            TYPE_RECURRING_MONTHLY,
+            TYPE_RECURRING_DAILY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    public static final int TYPE_NONRECURRING = 0;
+    public static final int TYPE_RECURRING_MONTHLY = 1;
+    public static final int TYPE_RECURRING_WEEKLY = 2;
+    public static final int TYPE_RECURRING_DAILY = 3;
+
+    /** {@hide} */
+    @IntDef(prefix = "LIMIT_BEHAVIOR_", value = {
+            LIMIT_BEHAVIOR_UNKNOWN,
+            LIMIT_BEHAVIOR_DISABLED,
+            LIMIT_BEHAVIOR_BILLED,
+            LIMIT_BEHAVIOR_THROTTLED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface LimitBehavior {}
+
+    public static final int LIMIT_BEHAVIOR_UNKNOWN = -1;
+    public static final int LIMIT_BEHAVIOR_DISABLED = 0;
+    public static final int LIMIT_BEHAVIOR_BILLED = 1;
+    public static final int LIMIT_BEHAVIOR_THROTTLED = 2;
+
+    public static final long BYTES_UNKNOWN = -1;
+    public static final long TIME_UNKNOWN = -1;
+
+    private final int type;
+    private final ZonedDateTime start;
+    private final ZonedDateTime end;
+    private CharSequence title;
+    private CharSequence summary;
+    private long dataWarningBytes = BYTES_UNKNOWN;
+    private long dataWarningSnoozeTime = TIME_UNKNOWN;
+    private long dataLimitBytes = BYTES_UNKNOWN;
+    private long dataLimitSnoozeTime = TIME_UNKNOWN;
+    private int dataLimitBehavior = LIMIT_BEHAVIOR_UNKNOWN;
+    private long dataUsageBytes = BYTES_UNKNOWN;
+    private long dataUsageTime = TIME_UNKNOWN;
+
+    private SubscriptionPlan(@Type int type, ZonedDateTime start, ZonedDateTime end) {
+        this.type = type;
+        this.start = start;
+        this.end = end;
+    }
+
+    private SubscriptionPlan(Parcel source) {
+        type = source.readInt();
+        if (source.readInt() != 0) {
+            start = ZonedDateTime.parse(source.readString());
+        } else {
+            start = null;
+        }
+        if (source.readInt() != 0) {
+            end = ZonedDateTime.parse(source.readString());
+        } else {
+            end = null;
+        }
+        title = source.readCharSequence();
+        summary = source.readCharSequence();
+        dataWarningBytes = source.readLong();
+        dataWarningSnoozeTime = source.readLong();
+        dataLimitBytes = source.readLong();
+        dataLimitSnoozeTime = source.readLong();
+        dataLimitBehavior = source.readInt();
+        dataUsageBytes = source.readLong();
+        dataUsageTime = source.readLong();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(type);
+        if (start != null) {
+            dest.writeInt(1);
+            dest.writeString(start.toString());
+        } else {
+            dest.writeInt(0);
+        }
+        if (end != null) {
+            dest.writeInt(1);
+            dest.writeString(end.toString());
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeCharSequence(title);
+        dest.writeCharSequence(summary);
+        dest.writeLong(dataWarningBytes);
+        dest.writeLong(dataWarningSnoozeTime);
+        dest.writeLong(dataLimitBytes);
+        dest.writeLong(dataLimitSnoozeTime);
+        dest.writeInt(dataLimitBehavior);
+        dest.writeLong(dataUsageBytes);
+        dest.writeLong(dataUsageTime);
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder("SubscriptionPlan:")
+                .append(" type=").append(type)
+                .append(" start=").append(start)
+                .append(" end=").append(end)
+                .append(" title=").append(title)
+                .append(" summary=").append(summary)
+                .append(" dataWarningBytes=").append(dataWarningBytes)
+                .append(" dataWarningSnoozeTime=").append(dataWarningSnoozeTime)
+                .append(" dataLimitBytes=").append(dataLimitBytes)
+                .append(" dataLimitSnoozeTime=").append(dataLimitSnoozeTime)
+                .append(" dataLimitBehavior=").append(dataLimitBehavior)
+                .append(" dataUsageBytes=").append(dataUsageBytes)
+                .append(" dataUsageTime=").append(dataUsageTime)
+                .toString();
+    }
+
+    public static final Parcelable.Creator<SubscriptionPlan> CREATOR = new Parcelable.Creator<SubscriptionPlan>() {
+        @Override
+        public SubscriptionPlan createFromParcel(Parcel source) {
+            return new SubscriptionPlan(source);
+        }
+
+        @Override
+        public SubscriptionPlan[] newArray(int size) {
+            return new SubscriptionPlan[size];
+        }
+    };
+
+    public @Type int getType() {
+        return type;
+    }
+
+    public ZonedDateTime getStart() {
+        return start;
+    }
+
+    public ZonedDateTime getEnd() {
+        return end;
+    }
+
+    public @Nullable CharSequence getTitle() {
+        return title;
+    }
+
+    public @Nullable CharSequence getSummary() {
+        return summary;
+    }
+
+    public @BytesLong long getDataWarningBytes() {
+        return dataWarningBytes;
+    }
+
+    public @BytesLong long getDataLimitBytes() {
+        return dataLimitBytes;
+    }
+
+    public @LimitBehavior int getDataLimitBehavior() {
+        return dataLimitBehavior;
+    }
+
+    public @BytesLong long getDataUsageBytes() {
+        return dataUsageBytes;
+    }
+
+    public @CurrentTimeMillisLong long getDataUsageTime() {
+        return dataUsageTime;
+    }
+
+    /** {@hide} */
+    @VisibleForTesting
+    public static long sNowOverride = -1;
+
+    private static ZonedDateTime now(ZoneId zone) {
+        return (sNowOverride != -1)
+                ? ZonedDateTime.ofInstant(Instant.ofEpochMilli(sNowOverride), zone)
+                : ZonedDateTime.now(zone);
+    }
+
+    /** {@hide} */
+    public static SubscriptionPlan convert(NetworkPolicy policy) {
+        final ZoneId zone = ZoneId.of(policy.cycleTimezone);
+        final ZonedDateTime now = now(zone);
+        final Builder builder;
+        if (policy.cycleDay != NetworkPolicy.CYCLE_NONE) {
+            // Assume we started last January, since it has all possible days
+            ZonedDateTime start = ZonedDateTime.of(
+                    now.toLocalDate().minusYears(1).withMonth(1).withDayOfMonth(policy.cycleDay),
+                    LocalTime.MIDNIGHT, zone);
+            builder = Builder.createRecurringMonthly(start);
+        } else {
+            Log.w(TAG, "Cycle not defined; assuming last 4 weeks non-recurring");
+            ZonedDateTime end = now;
+            ZonedDateTime start = end.minusWeeks(4);
+            builder = Builder.createNonrecurring(start, end);
+        }
+        if (policy.warningBytes != NetworkPolicy.WARNING_DISABLED) {
+            builder.setDataWarning(policy.warningBytes);
+        }
+        if (policy.lastWarningSnooze != NetworkPolicy.SNOOZE_NEVER) {
+            builder.setDataWarningSnooze(policy.lastWarningSnooze);
+        }
+        if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) {
+            builder.setDataLimit(policy.limitBytes, LIMIT_BEHAVIOR_DISABLED);
+        }
+        if (policy.lastLimitSnooze != NetworkPolicy.SNOOZE_NEVER) {
+            builder.setDataLimitSnooze(policy.lastLimitSnooze);
+        }
+        return builder.build();
+    }
+
+    /** {@hide} */
+    public static NetworkPolicy convert(SubscriptionPlan plan) {
+        final NetworkPolicy policy = new NetworkPolicy();
+        switch (plan.type) {
+            case TYPE_RECURRING_MONTHLY:
+                policy.cycleDay = plan.start.getDayOfMonth();
+                policy.cycleTimezone = plan.start.getZone().getId();
+                break;
+            default:
+                policy.cycleDay = NetworkPolicy.CYCLE_NONE;
+                policy.cycleTimezone = "UTC";
+                break;
+        }
+        policy.warningBytes = plan.dataWarningBytes;
+        policy.limitBytes = plan.dataLimitBytes;
+        policy.lastWarningSnooze = plan.dataWarningSnoozeTime;
+        policy.lastLimitSnooze = plan.dataLimitSnoozeTime;
+        policy.metered = true;
+        policy.inferred = false;
+        return policy;
+    }
+
+    /** {@hide} */
+    public TemporalUnit getTemporalUnit() {
+        switch (type) {
+            case TYPE_RECURRING_DAILY: return ChronoUnit.DAYS;
+            case TYPE_RECURRING_WEEKLY: return ChronoUnit.WEEKS;
+            case TYPE_RECURRING_MONTHLY: return ChronoUnit.MONTHS;
+            default: throw new IllegalArgumentException();
+        }
+    }
+
+    /**
+     * Return an iterator that returns data usage cycles.
+     * <p>
+     * For recurring plans, it starts at the currently active cycle, and then
+     * walks backwards in time through each previous cycle, back to the defined
+     * starting point and no further.
+     * <p>
+     * For non-recurring plans, it returns one single cycle.
+     */
+    public Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator() {
+        switch (type) {
+            case TYPE_NONRECURRING:
+                return new NonrecurringIterator();
+            case TYPE_RECURRING_WEEKLY:
+            case TYPE_RECURRING_MONTHLY:
+            case TYPE_RECURRING_DAILY:
+                return new RecurringIterator();
+            default:
+                throw new IllegalStateException("Unknown type: " + type);
+        }
+    }
+
+    private class NonrecurringIterator implements Iterator<Pair<ZonedDateTime, ZonedDateTime>> {
+        boolean hasNext = true;
+
+        @Override
+        public boolean hasNext() {
+            return hasNext;
+        }
+
+        @Override
+        public Pair<ZonedDateTime, ZonedDateTime> next() {
+            hasNext = false;
+            return new Pair<>(start, end);
+        }
+    }
+
+    private class RecurringIterator implements Iterator<Pair<ZonedDateTime, ZonedDateTime>> {
+        TemporalUnit unit;
+        long i;
+        ZonedDateTime cycleStart;
+        ZonedDateTime cycleEnd;
+
+        public RecurringIterator() {
+            final ZonedDateTime now = now(start.getZone());
+            if (DEBUG) Log.d(TAG, "Resolving using now " + now);
+
+            unit = getTemporalUnit();
+            i = unit.between(start, now);
+            updateCycle();
+
+            // Walk forwards until we find first cycle after now
+            while (cycleEnd.toEpochSecond() <= now.toEpochSecond()) {
+                i++;
+                updateCycle();
+            }
+
+            // Walk backwards until we find first cycle before now
+            while (cycleStart.toEpochSecond() > now.toEpochSecond()) {
+                i--;
+                updateCycle();
+            }
+        }
+
+        private void updateCycle() {
+            cycleStart = roundBoundaryTime(start.plus(i, unit));
+            cycleEnd = roundBoundaryTime(start.plus(i + 1, unit));
+        }
+
+        private ZonedDateTime roundBoundaryTime(ZonedDateTime boundary) {
+            if ((type == TYPE_RECURRING_MONTHLY)
+                    && (boundary.getDayOfMonth() < start.getDayOfMonth())) {
+                // When forced to end a monthly cycle early, we want to count
+                // that entire day against the boundary.
+                return ZonedDateTime.of(boundary.toLocalDate(), LocalTime.MAX, start.getZone());
+            } else {
+                return boundary;
+            }
+        }
+
+        @Override
+        public boolean hasNext() {
+            return cycleStart.toEpochSecond() >= start.toEpochSecond();
+        }
+
+        @Override
+        public Pair<ZonedDateTime, ZonedDateTime> next() {
+            if (DEBUG) Log.d(TAG, "Cycle " + i + " from " + cycleStart + " to " + cycleEnd);
+            Pair<ZonedDateTime, ZonedDateTime> p = new Pair<>(cycleStart, cycleEnd);
+            i--;
+            updateCycle();
+            return p;
+        }
+    }
+
+    public static class Builder {
+        private final SubscriptionPlan plan;
+
+        private Builder(@Type int type, ZonedDateTime start, ZonedDateTime end) {
+            plan = new SubscriptionPlan(type, start, end);
+        }
+
+        public static Builder createNonrecurring(ZonedDateTime start, ZonedDateTime end) {
+            if (!end.isAfter(start)) {
+                throw new IllegalArgumentException(
+                        "End " + end + " isn't after start " + start);
+            }
+            return new Builder(TYPE_NONRECURRING, start, end);
+        }
+
+        public static Builder createRecurringMonthly(ZonedDateTime start) {
+            return new Builder(TYPE_RECURRING_MONTHLY, start, null);
+        }
+
+        public static Builder createRecurringWeekly(ZonedDateTime start) {
+            return new Builder(TYPE_RECURRING_WEEKLY, start, null);
+        }
+
+        public static Builder createRecurringDaily(ZonedDateTime start) {
+            return new Builder(TYPE_RECURRING_DAILY, start, null);
+        }
+
+        public SubscriptionPlan build() {
+            return plan;
+        }
+
+        public Builder setTitle(@Nullable CharSequence title) {
+            plan.title = title;
+            return this;
+        }
+
+        public Builder setSummary(@Nullable CharSequence summary) {
+            plan.summary = summary;
+            return this;
+        }
+
+        public Builder setDataWarning(@BytesLong long dataWarningBytes) {
+            if (dataWarningBytes < BYTES_UNKNOWN) {
+                throw new IllegalArgumentException("Warning must be positive or BYTES_UNKNOWN");
+            }
+            plan.dataWarningBytes = dataWarningBytes;
+            return this;
+        }
+
+        /** {@hide} */
+        public Builder setDataWarningSnooze(@CurrentTimeMillisLong long dataWarningSnoozeTime) {
+            plan.dataWarningSnoozeTime = dataWarningSnoozeTime;
+            return this;
+        }
+
+        public Builder setDataLimit(@BytesLong long dataLimitBytes,
+                @LimitBehavior int dataLimitBehavior) {
+            if (dataLimitBytes < BYTES_UNKNOWN) {
+                throw new IllegalArgumentException("Limit must be positive or BYTES_UNKNOWN");
+            }
+            plan.dataLimitBytes = dataLimitBytes;
+            plan.dataLimitBehavior = dataLimitBehavior;
+            return this;
+        }
+
+        /** {@hide} */
+        public Builder setDataLimitSnooze(@CurrentTimeMillisLong long dataLimitSnoozeTime) {
+            plan.dataLimitSnoozeTime = dataLimitSnoozeTime;
+            return this;
+        }
+
+        public Builder setDataUsage(@BytesLong long dataUsageBytes,
+                @CurrentTimeMillisLong long dataUsageTime) {
+            if (dataUsageBytes < BYTES_UNKNOWN) {
+                throw new IllegalArgumentException("Usage must be positive or BYTES_UNKNOWN");
+            }
+            if (dataUsageTime < TIME_UNKNOWN) {
+                throw new IllegalArgumentException("Time must be positive or TIME_UNKNOWN");
+            }
+            if ((dataUsageBytes == BYTES_UNKNOWN) != (dataUsageTime == TIME_UNKNOWN)) {
+                throw new IllegalArgumentException("Must provide both usage and time or neither");
+            }
+            plan.dataUsageBytes = dataUsageBytes;
+            plan.dataUsageTime = dataUsageTime;
+            return this;
+        }
+    }
+}
diff --git a/test-runner/Android.mk b/test-runner/Android.mk
index 9053b23..3c3718a 100644
--- a/test-runner/Android.mk
+++ b/test-runner/Android.mk
@@ -22,9 +22,15 @@
 # =====================================
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := \
+    $(filter-out $(android_test_mock_source_files), $(call all-java-files-under, src))
 
-LOCAL_JAVA_LIBRARIES := core-oj core-libart framework legacy-test
+LOCAL_JAVA_LIBRARIES := \
+    core-oj \
+    core-libart \
+    framework \
+    legacy-test \
+    android.test.mock \
 
 LOCAL_MODULE:= android.test.runner
 
diff --git a/tests/net/AndroidManifest.xml b/tests/net/AndroidManifest.xml
index e069dd0..ba1a2ea 100644
--- a/tests/net/AndroidManifest.xml
+++ b/tests/net/AndroidManifest.xml
@@ -32,7 +32,6 @@
     <uses-permission android:name="android.permission.GET_DETAILED_TASKS" />
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
-    <uses-permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.MANAGE_USERS" />
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index d11565a..eff04ab 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -201,9 +201,14 @@
                 "  time_ms: 1",
                 "  transports: 0",
                 "  default_network_event <",
+                "    default_network_duration_ms: 0",
+                "    final_score: 0",
+                "    initial_score: 0",
+                "    ip_support: 0",
                 "    network_id <",
                 "      network_id: 102",
                 "    >",
+                "    no_default_network_duration_ms: 0",
                 "    previous_network_id <",
                 "      network_id: 101",
                 "    >",
@@ -442,6 +447,8 @@
                 "    program_updates_all: 7",
                 "    program_updates_allowing_multicast: 3",
                 "    received_ras: 10",
+                "    total_packet_dropped: 0",
+                "    total_packet_processed: 0",
                 "    zero_lifetime_ras: 1",
                 "  >",
                 ">",
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index e01469b..cc18b7f 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -256,9 +256,14 @@
                 "  time_ms: 300",
                 "  transports: 0",
                 "  default_network_event <",
+                "    default_network_duration_ms: 0",
+                "    final_score: 0",
+                "    initial_score: 0",
+                "    ip_support: 0",
                 "    network_id <",
                 "      network_id: 102",
                 "    >",
+                "    no_default_network_duration_ms: 0",
                 "    previous_network_id <",
                 "      network_id: 101",
                 "    >",
@@ -308,6 +313,8 @@
                 "    program_updates_all: 7",
                 "    program_updates_allowing_multicast: 3",
                 "    received_ras: 10",
+                "    total_packet_dropped: 0",
+                "    total_packet_processed: 0",
                 "    zero_lifetime_ras: 1",
                 "  >",
                 ">",
@@ -367,6 +374,10 @@
                 "    event_types: 1",
                 "    event_types: 1",
                 "    event_types: 2",
+                "    getaddrinfo_error_count: 0",
+                "    getaddrinfo_query_count: 0",
+                "    gethostbyname_error_count: 0",
+                "    gethostbyname_query_count: 0",
                 "    latencies_ms: 3456",
                 "    latencies_ms: 45",
                 "    latencies_ms: 638",
@@ -384,6 +395,10 @@
                 "  dns_lookup_batch <",
                 "    event_types: 1",
                 "    event_types: 2",
+                "    getaddrinfo_error_count: 0",
+                "    getaddrinfo_query_count: 0",
+                "    gethostbyname_error_count: 0",
+                "    gethostbyname_query_count: 0",
                 "    latencies_ms: 56",
                 "    latencies_ms: 34",
                 "    return_codes: 0",
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
index f98ab3d..46f395e 100644
--- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -111,6 +111,10 @@
                 "    event_types: 1",
                 "    event_types: 2",
                 "    event_types: 2",
+                "    getaddrinfo_error_count: 0",
+                "    getaddrinfo_query_count: 0",
+                "    gethostbyname_error_count: 0",
+                "    gethostbyname_query_count: 0",
                 "    latencies_ms: 3456",
                 "    latencies_ms: 267",
                 "    latencies_ms: 1230",
@@ -142,6 +146,10 @@
                 "    event_types: 2",
                 "    event_types: 1",
                 "    event_types: 1",
+                "    getaddrinfo_error_count: 0",
+                "    getaddrinfo_query_count: 0",
+                "    gethostbyname_error_count: 0",
+                "    gethostbyname_query_count: 0",
                 "    latencies_ms: 56",
                 "    latencies_ms: 78",
                 "    latencies_ms: 14",
diff --git a/tests/radio/src/android/hardware/radio/tests/RadioTest.java b/tests/radio/src/android/hardware/radio/tests/RadioTest.java
index e354a34..aa5780a 100644
--- a/tests/radio/src/android/hardware/radio/tests/RadioTest.java
+++ b/tests/radio/src/android/hardware/radio/tests/RadioTest.java
@@ -18,6 +18,7 @@
 import android.Manifest;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.hardware.radio.ProgramSelector;
 import android.hardware.radio.RadioManager;
 import android.hardware.radio.RadioTuner;
 import android.support.test.InstrumentationRegistry;
@@ -43,6 +44,7 @@
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.atMost;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.timeout;
@@ -64,6 +66,7 @@
     private final int kConfigCallbackTimeoutMs = 10000;
     private final int kCancelTimeoutMs = 1000;
     private final int kTuneCallbackTimeoutMs = 30000;
+    private final int kFullScanTimeoutMs = 60000;
 
     private RadioManager mRadioManager;
     private RadioTuner mRadioTuner;
@@ -108,7 +111,7 @@
             mRadioTuner.close();
             mRadioTuner = null;
         }
-        verifyNoMoreInteractions(mCallback);
+        resetCallback();
     }
 
     private void openTuner() {
@@ -116,6 +119,7 @@
     }
 
     private void resetCallback() {
+        verify(mCallback, atLeast(0)).onMetadataChanged(any());
         verifyNoMoreInteractions(mCallback);
         Mockito.reset(mCallback);
     }
@@ -144,11 +148,9 @@
         assertNotNull(mRadioTuner);
         verify(mCallback, timeout(kConfigCallbackTimeoutMs)).onConfigurationChanged(any());
         resetCallback();
-    }
 
-    private void checkAntenna() {
-        boolean isConnected = mRadioTuner.isAntennaConnected();
-        assertTrue(isConnected);
+        boolean isAntennaConnected = mRadioTuner.isAntennaConnected();
+        assertTrue(isAntennaConnected);
     }
 
     @Test
@@ -256,7 +258,6 @@
     @Test
     public void testStep() {
         openTuner();
-        checkAntenna();
 
         int ret = mRadioTuner.step(RadioTuner.DIRECTION_DOWN, true);
         assertEquals(RadioManager.STATUS_OK, ret);
@@ -272,7 +273,6 @@
     @Test
     public void testTuneAndGetPI() {
         openTuner();
-        checkAntenna();
 
         int channel = mFmBandConfig.getLowerLimit() + mFmBandConfig.getSpacing();
 
@@ -304,7 +304,6 @@
     @Test
     public void testLateCancel() {
         openTuner();
-        checkAntenna();
 
         int ret = mRadioTuner.step(RadioTuner.DIRECTION_DOWN, false);
         assertEquals(RadioManager.STATUS_OK, ret);
@@ -317,7 +316,6 @@
     @Test
     public void testScanAndCancel() {
         openTuner();
-        checkAntenna();
 
         /* There is a possible race condition between scan and cancel commands - the scan may finish
          * before cancel command is issued. Thus we accept both outcomes in this test.
@@ -335,7 +333,6 @@
     @Test
     public void testStartBackgroundScan() {
         openTuner();
-        checkAntenna();
 
         boolean ret = mRadioTuner.startBackgroundScan();
         boolean isSupported = mModule.isBackgroundScanningSupported();
@@ -345,7 +342,6 @@
     @Test
     public void testGetProgramList() {
         openTuner();
-        checkAntenna();
 
         try {
             List<RadioManager.ProgramInfo> list = mRadioTuner.getProgramList(null);
@@ -357,6 +353,39 @@
     }
 
     @Test
+    public void testTuneFromProgramList() {
+        openTuner();
+
+        List<RadioManager.ProgramInfo> list;
+
+        try {
+            list = mRadioTuner.getProgramList(null);
+            assertNotNull(list);
+        } catch (IllegalStateException e) {
+            Log.i(TAG, "Background list is not ready, trying to fix it");
+
+            boolean success = mRadioTuner.startBackgroundScan();
+            assertTrue(success);
+            verify(mCallback, timeout(kFullScanTimeoutMs)).onBackgroundScanComplete();
+
+            list = mRadioTuner.getProgramList(null);
+            assertNotNull(list);
+        }
+
+        if (list.isEmpty()) {
+            Log.i(TAG, "Program list is empty, can't test tune");
+            return;
+        }
+
+        ProgramSelector sel = list.get(0).getSelector();
+        mRadioTuner.tune(sel);
+        ArgumentCaptor<RadioManager.ProgramInfo> infoc =
+                ArgumentCaptor.forClass(RadioManager.ProgramInfo.class);
+        verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(infoc.capture());
+        assertEquals(sel, infoc.getValue().getSelector());
+    }
+
+    @Test
     public void testForcedAnalog() {
         openTuner();
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7173775..598360c 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1837,7 +1837,7 @@
     }
 
     /**
-     * This call will be deprecated and removed in an upcoming release.  It is no longer used to
+     * This call is deprecated and removed.  It is no longer used to
      * start WiFi Tethering.  Please use {@link ConnectivityManager#startTethering(int, boolean,
      * ConnectivityManager#OnStartTetheringCallback)} if
      * the caller has proper permissions.  Callers can also use the LocalOnlyHotspot feature for a
@@ -1849,8 +1849,11 @@
      * @return {@code false}
      *
      * @hide
+     * @deprecated This API is nolonger supported.
+     * @removed
      */
     @SystemApi
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
     public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
         String packageName = mContext.getOpPackageName();