Merge "Unify permissions under UPDATE_DEVICE_STATS."
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 b77d3e6..37cb728 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -49080,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 22e4c1f..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 {
diff --git a/api/system-current.txt b/api/system-current.txt
index 2fc6bac..861de2e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -191,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";
@@ -17213,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);
@@ -17220,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
@@ -17255,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();
@@ -17329,10 +17392,11 @@
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();
@@ -17404,7 +17468,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
@@ -17425,7 +17490,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);
@@ -29184,7 +29249,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);
@@ -38564,6 +38629,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";
@@ -52748,7 +52829,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);
@@ -52950,7 +53031,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/test-current.txt b/api/test-current.txt
index 5a6680f..b9f9be5d2 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -32733,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 {
@@ -32865,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>);
@@ -49511,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 22e4c1f..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 {
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/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/hardware/radio/ITuner.aidl b/core/java/android/hardware/radio/ITuner.aidl
index 69e135d..2932c3e 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,7 +53,7 @@
* @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
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..1de80b17 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,6 +30,8 @@
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;
@@ -70,7 +73,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 +86,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.
@@ -1258,8 +1270,7 @@
private final static int FLAG_LIVE = 1 << 0;
private final static int FLAG_MUTED = 1 << 1;
- 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 +1279,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 +1292,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.
*/
@@ -1362,8 +1398,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 +1425,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 +1447,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 +1458,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 +1476,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 +1560,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 +1571,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..8e0b0f6 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}.
@@ -318,20 +330,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 +390,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..503bb55 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();
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/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/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/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/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/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/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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 885a54a..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.
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 94b988e..a088a46 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -106,12 +106,6 @@
<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>
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/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/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/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/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/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 606b229..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
*
@@ -5281,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();
}
@@ -5304,6 +5314,10 @@
}
public boolean isKeyguardShowing() {
+ if (mStatusBarKeyguardViewManager == null) {
+ Slog.i(TAG, "isKeyguardShowing() called before startKeyguard(), returning true");
+ return true;
+ }
return mStatusBarKeyguardViewManager.isShowing();
}
@@ -5579,6 +5593,10 @@
private Set<String> mNonBlockablePkgs;
+ public boolean isDeviceInteractive() {
+ return mDeviceInteractive;
+ }
+
@Override // NotificationData.Environment
public boolean isDeviceProvisioned() {
return mDeviceProvisionedController.isDeviceProvisioned();
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/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/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/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/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 9b032e2..38f1c07 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -149,6 +149,7 @@
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;
@@ -1896,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) {
@@ -1910,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.
}
/**
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/radio/Tuner.java b/services/core/java/com/android/server/radio/Tuner.java
index 81128c2..209febc 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,7 +76,7 @@
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 RadioManager.ProgramInfo nativeGetProgramInformation(long nativeContext);
@@ -173,10 +175,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);
}
}
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/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_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..13bfb57 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) {
@@ -282,8 +307,10 @@
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 +328,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 +429,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,7 +438,7 @@
(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 },
{ "nativeGetProgramInformation", "(J)Landroid/hardware/radio/RadioManager$ProgramInfo;",
(void*)nativeGetProgramInformation },
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..af000bd 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;
@@ -383,25 +402,100 @@
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 = make_javaref(env, env->NewObjectArray(selector.secondaryIds.size(),
+ gjni.ProgramSelector.Identifier.clazz, nullptr));
+ for (size_t i = 0; i < selector.secondaryIds.size(); i++) {
+ auto jId = ProgramIdentifierFromHal(env, selector.secondaryIds[i]);
+ env->SetObjectArrayElement(jSecondary.get(), i, jId.get());
+ }
+
+ auto jVendor = make_javaref(env, env->NewLongArray(selector.vendorIds.size()));
+ auto jVendorElements = env->GetLongArrayElements(jVendor.get(), nullptr);
+ for (size_t i = 0; i < selector.vendorIds.size(); i++) {
+ jVendorElements[i] = selector.vendorIds[i];
+ }
+ env->ReleaseLongArrayElements(jVendor.get(), jVendorElements, 0);
+
+ 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
@@ -465,7 +559,31 @@
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/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/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/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();