Merge "Add @TestApi to SystemApis in SystemProperties Test: build and flash Android Bug: 143620678"
diff --git a/Android.bp b/Android.bp
index b26b373..9497085 100644
--- a/Android.bp
+++ b/Android.bp
@@ -307,6 +307,7 @@
"android.hardware.thermal-V1.1-java",
"android.hardware.thermal-V2.0-java",
"android.hardware.tv.input-V1.0-java-constants",
+ "android.hardware.tv.tuner-V1.0-java-constants",
"android.hardware.usb-V1.0-java-constants",
"android.hardware.usb-V1.1-java-constants",
"android.hardware.usb-V1.2-java-constants",
@@ -935,6 +936,7 @@
]
metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " +
+ "--ignore-classes-on-classpath " +
"--hide-package com.android.okhttp " +
"--hide-package com.android.org.conscrypt --hide-package com.android.server " +
"--error UnhiddenSystemApi " +
diff --git a/api/current.txt b/api/current.txt
index 57bbd77..e31aed0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9467,6 +9467,7 @@
method @NonNull public final android.content.ContentProvider.CallingIdentity clearCallingIdentity();
method public abstract int delete(@NonNull android.net.Uri, @Nullable String, @Nullable String[]);
method public void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
+ method @Nullable public final String getCallingFeatureId();
method @Nullable public final String getCallingPackage();
method @Nullable public final android.content.Context getContext();
method @Nullable public final android.content.pm.PathPermission[] getPathPermissions();
@@ -30036,7 +30037,7 @@
method public void setTdlsEnabled(java.net.InetAddress, boolean);
method public void setTdlsEnabledWithMacAddress(String, boolean);
method @Deprecated public boolean setWifiEnabled(boolean);
- method public void startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, @Nullable android.os.Handler);
+ method @RequiresPermission(allOf={android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, @Nullable android.os.Handler);
method @Deprecated public boolean startScan();
method @Deprecated public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback);
method @Deprecated public int updateNetwork(android.net.wifi.WifiConfiguration);
@@ -44380,6 +44381,7 @@
field public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT = "opportunistic_network_exit_threshold_rsrp_int";
field public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT = "opportunistic_network_exit_threshold_rssnr_int";
field public static final String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
+ field public static final String KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL = "prevent_clir_activation_and_deactivation_code_bool";
field public static final String KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY = "radio_restart_failure_causes_int_array";
field public static final String KEY_RCS_CONFIG_SERVER_URL_STRING = "rcs_config_server_url_string";
field public static final String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool";
diff --git a/api/system-current.txt b/api/system-current.txt
index e6a3e9b..92872e9 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3480,7 +3480,8 @@
method @NonNull public static android.location.LocationRequest createFromDeprecatedCriteria(@NonNull android.location.Criteria, long, float, boolean);
method @NonNull public static android.location.LocationRequest createFromDeprecatedProvider(@NonNull String, long, float, boolean);
method public int describeContents();
- method public long getExpireAt();
+ method @Deprecated public long getExpireAt();
+ method public long getExpireIn();
method public long getFastestInterval();
method public boolean getHideFromAppOps();
method public long getInterval();
@@ -3491,7 +3492,7 @@
method @Nullable public android.os.WorkSource getWorkSource();
method public boolean isLocationSettingsIgnored();
method public boolean isLowPowerMode();
- method @NonNull public android.location.LocationRequest setExpireAt(long);
+ method @Deprecated @NonNull public android.location.LocationRequest setExpireAt(long);
method @NonNull public android.location.LocationRequest setExpireIn(long);
method @NonNull public android.location.LocationRequest setFastestInterval(long);
method public void setHideFromAppOps(boolean);
@@ -4734,6 +4735,24 @@
field @Deprecated public byte id;
}
+ public final class SoftApConfiguration implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.net.MacAddress getBssid();
+ method @Nullable public String getSsid();
+ method @Nullable public String getWpa2Passphrase();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApConfiguration> CREATOR;
+ }
+
+ public static final class SoftApConfiguration.Builder {
+ ctor public SoftApConfiguration.Builder();
+ ctor public SoftApConfiguration.Builder(@NonNull android.net.wifi.SoftApConfiguration);
+ method @NonNull public android.net.wifi.SoftApConfiguration build();
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBssid(@Nullable android.net.MacAddress);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setSsid(@Nullable String);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setWpa2Passphrase(@Nullable String);
+ }
+
public final class WifiClient implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.net.MacAddress getMacAddress();
@@ -4746,6 +4765,7 @@
method @Deprecated public boolean isEphemeral();
method @Deprecated public boolean isNoInternetAccessExpected();
field @Deprecated public boolean allowAutojoin;
+ field @Deprecated public int carrierId;
field @Deprecated public String creatorName;
field @Deprecated public int creatorUid;
field @Deprecated public String lastUpdateName;
@@ -4789,6 +4809,7 @@
method @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsConfiguratorInitiator(@NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsEnrolleeInitiator(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
+ method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startLocalOnlyHotspot(@NonNull android.net.wifi.SoftApConfiguration, @Nullable java.util.concurrent.Executor, @Nullable android.net.wifi.WifiManager.LocalOnlyHotspotCallback);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public boolean startScan(android.os.WorkSource);
method @RequiresPermission(anyOf={"android.permission.NETWORK_STACK", android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean startSoftAp(@Nullable android.net.wifi.WifiConfiguration);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startSubscriptionProvisioning(@NonNull android.net.wifi.hotspot2.OsuProvider, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.hotspot2.ProvisioningCallback);
@@ -4852,6 +4873,10 @@
field public int numUsage;
}
+ public static final class WifiNetworkSuggestion.Builder {
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) public android.net.wifi.WifiNetworkSuggestion.Builder setCarrierId(int);
+ }
+
public class WifiScanner {
method @Deprecated public void configureWifiChange(int, int, int, int, int, android.net.wifi.WifiScanner.BssidInfo[]);
method @Deprecated public void configureWifiChange(android.net.wifi.WifiScanner.WifiChangeSettings);
@@ -6246,15 +6271,21 @@
field public static final String DELIVERY_TIME = "date";
field public static final String ETWS_WARNING_TYPE = "etws_warning_type";
field public static final String GEOGRAPHICAL_SCOPE = "geo_scope";
+ field public static final String GEOMETRIES = "geometries";
field public static final String LAC = "lac";
field public static final String LANGUAGE_CODE = "language";
+ field public static final String MAXIMUM_WAIT_TIME = "maximum_wait_time";
field public static final String MESSAGE_BODY = "body";
+ field public static final String MESSAGE_BROADCASTED = "message_broadcasted";
field public static final String MESSAGE_FORMAT = "format";
+ field @RequiresPermission(android.Manifest.permission.READ_CELL_BROADCASTS) @NonNull public static final android.net.Uri MESSAGE_HISTORY_URI;
field public static final String MESSAGE_PRIORITY = "priority";
field public static final String MESSAGE_READ = "read";
field public static final String PLMN = "plmn";
+ field public static final String RECEIVED_TIME = "received_time";
field public static final String SERIAL_NUMBER = "serial_number";
field public static final String SERVICE_CATEGORY = "service_category";
+ field public static final String SLOT_INDEX = "slot_index";
}
public final class TimeZoneRulesDataContract {
@@ -8343,6 +8374,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean enableDataConnectivity();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean enableModemForSlot(int, boolean);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void enableVideoCalling(boolean);
+ method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void factoryReset(int);
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getAidForAppType(int);
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
@@ -9012,11 +9044,14 @@
public class ImsMmTelManager {
method @NonNull public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull java.util.concurrent.Executor) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiModeSetting();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiRoamingModeSetting();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAdvancedCallingSettingEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void isSupported(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, @NonNull java.util.function.Consumer<java.lang.Boolean>, @NonNull java.util.concurrent.Executor) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isTtyOverVolteEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVoWiFiRoamingSettingEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVoWiFiSettingEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVtSettingEnabled();
@@ -9543,6 +9578,8 @@
method public final android.telephony.ims.feature.MmTelFeature.MmTelCapabilities queryCapabilityStatus();
method public void setUiTtyMode(int, @Nullable android.os.Message);
method @android.telephony.ims.feature.MmTelFeature.ProcessCallResult public int shouldProcessCall(@NonNull String[]);
+ field public static final String EXTRA_IS_UNKNOWN_CALL = "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL";
+ field public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD";
field public static final int PROCESS_CALL_CSFB = 1; // 0x1
field public static final int PROCESS_CALL_IMS = 0; // 0x0
}
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index 57a853a..a907fa6 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -257,6 +257,8 @@
SamShouldBeLast: android.net.ConnectivityManager#createSocketKeepalive(android.net.Network, android.net.IpSecManager.UdpEncapsulationSocket, java.net.InetAddress, java.net.InetAddress, java.util.concurrent.Executor, android.net.SocketKeepalive.Callback):
+SamShouldBeLast: android.net.wifi.WifiManager#startLocalOnlyHotspot(android.net.wifi.SoftApConfiguration, java.util.concurrent.Executor, android.net.wifi.WifiManager.LocalOnlyHotspotCallback):
+
SamShouldBeLast: android.net.wifi.rtt.WifiRttManager#startRanging(android.net.wifi.rtt.RangingRequest, java.util.concurrent.Executor, android.net.wifi.rtt.RangingResultCallback):
SamShouldBeLast: android.nfc.NfcAdapter#enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle):
diff --git a/api/test-current.txt b/api/test-current.txt
index 0205cc5..5b16743 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1136,17 +1136,20 @@
public final class LocationRequest implements android.os.Parcelable {
method @NonNull public static android.location.LocationRequest create();
method public int describeContents();
- method public long getExpireAt();
+ method @Deprecated public long getExpireAt();
+ method public long getExpireIn();
method public long getFastestInterval();
method public long getInterval();
method public int getNumUpdates();
method public int getQuality();
method public boolean isLocationSettingsIgnored();
- method @NonNull public android.location.LocationRequest setExpireAt(long);
+ method public boolean isLowPowerMode();
+ method @Deprecated @NonNull public android.location.LocationRequest setExpireAt(long);
method @NonNull public android.location.LocationRequest setExpireIn(long);
method @NonNull public android.location.LocationRequest setFastestInterval(long);
method @NonNull public android.location.LocationRequest setInterval(long);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @NonNull public android.location.LocationRequest setLocationSettingsIgnored(boolean);
+ method @NonNull public android.location.LocationRequest setLowPowerMode(boolean);
method @NonNull public android.location.LocationRequest setNumUpdates(int);
method @NonNull public android.location.LocationRequest setProvider(@NonNull String);
method @NonNull public android.location.LocationRequest setQuality(int);
@@ -2458,6 +2461,36 @@
field public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
}
+ public static final class Telephony.CellBroadcasts implements android.provider.BaseColumns {
+ field public static final String CID = "cid";
+ field public static final String CMAS_CATEGORY = "cmas_category";
+ field public static final String CMAS_CERTAINTY = "cmas_certainty";
+ field public static final String CMAS_MESSAGE_CLASS = "cmas_message_class";
+ field public static final String CMAS_RESPONSE_TYPE = "cmas_response_type";
+ field public static final String CMAS_SEVERITY = "cmas_severity";
+ field public static final String CMAS_URGENCY = "cmas_urgency";
+ field @NonNull public static final android.net.Uri CONTENT_URI;
+ field public static final String DEFAULT_SORT_ORDER = "date DESC";
+ field public static final String DELIVERY_TIME = "date";
+ field public static final String ETWS_WARNING_TYPE = "etws_warning_type";
+ field public static final String GEOGRAPHICAL_SCOPE = "geo_scope";
+ field public static final String GEOMETRIES = "geometries";
+ field public static final String LAC = "lac";
+ field public static final String LANGUAGE_CODE = "language";
+ field public static final String MAXIMUM_WAIT_TIME = "maximum_wait_time";
+ field public static final String MESSAGE_BODY = "body";
+ field public static final String MESSAGE_BROADCASTED = "message_broadcasted";
+ field public static final String MESSAGE_FORMAT = "format";
+ field @RequiresPermission(android.Manifest.permission.READ_CELL_BROADCASTS) @NonNull public static final android.net.Uri MESSAGE_HISTORY_URI;
+ field public static final String MESSAGE_PRIORITY = "priority";
+ field public static final String MESSAGE_READ = "read";
+ field public static final String PLMN = "plmn";
+ field public static final String RECEIVED_TIME = "received_time";
+ field public static final String SERIAL_NUMBER = "serial_number";
+ field public static final String SERVICE_CATEGORY = "service_category";
+ field public static final String SLOT_INDEX = "slot_index";
+ }
+
public static final class Telephony.Sms.Intents {
field public static final String SMS_CARRIER_PROVISION_ACTION = "android.provider.Telephony.SMS_CARRIER_PROVISION";
}
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 55dbc17..7e278e9 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -508,7 +508,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.insert(resolveCallingPackage(), mUri, mContentValues);
+ provider.insert(resolveCallingPackage(), null, mUri, mContentValues);
}
}
@@ -522,7 +522,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.delete(resolveCallingPackage(), mUri, mWhere, null);
+ provider.delete(resolveCallingPackage(), null, mUri, mWhere, null);
}
}
@@ -557,7 +557,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- Bundle result = provider.call(null, mUri.getAuthority(), mMethod, mArg, mExtras);
+ Bundle result = provider.call(null, null, mUri.getAuthority(), mMethod, mArg, mExtras);
if (result != null) {
result.size(); // unpack
}
@@ -584,7 +584,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "r", null, null)) {
+ try (ParcelFileDescriptor fd = provider.openFile(null, null, mUri, "r", null, null)) {
FileUtils.copy(fd.getFileDescriptor(), FileDescriptor.out);
}
}
@@ -597,7 +597,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "w", null, null)) {
+ try (ParcelFileDescriptor fd = provider.openFile(null, null, mUri, "w", null, null)) {
FileUtils.copy(FileDescriptor.in, fd.getFileDescriptor());
}
}
@@ -616,7 +616,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- Cursor cursor = provider.query(resolveCallingPackage(), mUri, mProjection,
+ Cursor cursor = provider.query(resolveCallingPackage(), null, mUri, mProjection,
ContentResolver.createSqlQueryBundle(mWhere, null, mSortOrder), null);
if (cursor == null) {
System.out.println("No result found.");
@@ -679,7 +679,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.update(resolveCallingPackage(), mUri, mContentValues, mWhere, null);
+ provider.update(resolveCallingPackage(), null, mUri, mContentValues, mWhere, null);
}
}
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 6c3dff2..91cadc9 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -200,6 +200,10 @@
}
void StatsLogProcessor::OnLogEvent(LogEvent* event) {
+ OnLogEvent(event, getElapsedRealtimeNs());
+}
+
+void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
#ifdef VERY_VERBOSE_PRINTING
@@ -207,9 +211,9 @@
ALOGI("%s", event->ToString().c_str());
}
#endif
- const int64_t currentTimestampNs = event->GetElapsedTimestampNs();
+ const int64_t eventElapsedTimeNs = event->GetElapsedTimestampNs();
- resetIfConfigTtlExpiredLocked(currentTimestampNs);
+ resetIfConfigTtlExpiredLocked(eventElapsedTimeNs);
StatsdStats::getInstance().noteAtomLogged(
event->GetTagId(), event->GetElapsedTimestampNs() / NS_PER_SEC);
@@ -264,15 +268,16 @@
uidsWithActiveConfigsChanged.insert(uid);
StatsdStats::getInstance().noteActiveStatusChanged(pair.first, isCurActive);
}
- flushIfNecessaryLocked(event->GetElapsedTimestampNs(), pair.first, *(pair.second));
+ flushIfNecessaryLocked(pair.first, *(pair.second));
}
+ // Don't use the event timestamp for the guardrail.
for (int uid : uidsWithActiveConfigsChanged) {
// Send broadcast so that receivers can pull data.
auto lastBroadcastTime = mLastActivationBroadcastTimes.find(uid);
if (lastBroadcastTime != mLastActivationBroadcastTimes.end()) {
- if (currentTimestampNs - lastBroadcastTime->second <
- StatsdStats::kMinActivationBroadcastPeriodNs) {
+ if (elapsedRealtimeNs - lastBroadcastTime->second <
+ StatsdStats::kMinActivationBroadcastPeriodNs) {
StatsdStats::getInstance().noteActivationBroadcastGuardrailHit(uid);
VLOG("StatsD would've sent an activation broadcast but the rate limit stopped us.");
return;
@@ -282,13 +287,13 @@
if (activeConfigs != activeConfigsPerUid.end()) {
if (mSendActivationBroadcast(uid, activeConfigs->second)) {
VLOG("StatsD sent activation notice for uid %d", uid);
- mLastActivationBroadcastTimes[uid] = currentTimestampNs;
+ mLastActivationBroadcastTimes[uid] = elapsedRealtimeNs;
}
} else {
std::vector<int64_t> emptyActiveConfigs;
if (mSendActivationBroadcast(uid, emptyActiveConfigs)) {
VLOG("StatsD sent EMPTY activation notice for uid %d", uid);
- mLastActivationBroadcastTimes[uid] = currentTimestampNs;
+ mLastActivationBroadcastTimes[uid] = elapsedRealtimeNs;
}
}
}
@@ -550,22 +555,23 @@
}
}
-void StatsLogProcessor::flushIfNecessaryLocked(
- int64_t timestampNs, const ConfigKey& key, MetricsManager& metricsManager) {
+void StatsLogProcessor::flushIfNecessaryLocked(const ConfigKey& key,
+ MetricsManager& metricsManager) {
+ int64_t elapsedRealtimeNs = getElapsedRealtimeNs();
auto lastCheckTime = mLastByteSizeTimes.find(key);
if (lastCheckTime != mLastByteSizeTimes.end()) {
- if (timestampNs - lastCheckTime->second < StatsdStats::kMinByteSizeCheckPeriodNs) {
+ if (elapsedRealtimeNs - lastCheckTime->second < StatsdStats::kMinByteSizeCheckPeriodNs) {
return;
}
}
// We suspect that the byteSize() computation is expensive, so we set a rate limit.
size_t totalBytes = metricsManager.byteSize();
- mLastByteSizeTimes[key] = timestampNs;
+ mLastByteSizeTimes[key] = elapsedRealtimeNs;
bool requestDump = false;
- if (totalBytes >
- StatsdStats::kMaxMetricsBytesPerConfig) { // Too late. We need to start clearing data.
- metricsManager.dropData(timestampNs);
+ if (totalBytes > StatsdStats::kMaxMetricsBytesPerConfig) {
+ // Too late. We need to start clearing data.
+ metricsManager.dropData(elapsedRealtimeNs);
StatsdStats::getInstance().noteDataDropped(key, totalBytes);
VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str());
} else if ((totalBytes > StatsdStats::kBytesPerConfigTriggerGetData) ||
@@ -580,7 +586,8 @@
// Send broadcast so that receivers can pull data.
auto lastBroadcastTime = mLastBroadcastTimes.find(key);
if (lastBroadcastTime != mLastBroadcastTimes.end()) {
- if (timestampNs - lastBroadcastTime->second < StatsdStats::kMinBroadcastPeriodNs) {
+ if (elapsedRealtimeNs - lastBroadcastTime->second <
+ StatsdStats::kMinBroadcastPeriodNs) {
VLOG("StatsD would've sent a broadcast but the rate limit stopped us.");
return;
}
@@ -588,7 +595,7 @@
if (mSendBroadcast(key)) {
mOnDiskDataConfigs.erase(key);
VLOG("StatsD triggered data fetch for %s", key.ToString().c_str());
- mLastBroadcastTimes[key] = timestampNs;
+ mLastBroadcastTimes[key] = elapsedRealtimeNs;
StatsdStats::getInstance().noteBroadcastSent(key);
}
}
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 8292a3a..68b1218 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -147,6 +147,8 @@
sp<AlarmMonitor> mPeriodicAlarmMonitor;
+ void OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs);
+
void resetIfConfigTtlExpiredLocked(const int64_t timestampNs);
void OnConfigUpdatedLocked(
@@ -176,8 +178,7 @@
/* Check if we should send a broadcast if approaching memory limits and if we're over, we
* actually delete the data. */
- void flushIfNecessaryLocked(int64_t timestampNs, const ConfigKey& key,
- MetricsManager& metricsManager);
+ void flushIfNecessaryLocked(const ConfigKey& key, MetricsManager& metricsManager);
// Maps the isolated uid in the log event to host uid if the log event contains uid fields.
void mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const;
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 460b9e0..69e11ed 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -76,9 +76,9 @@
// Expect only the first flush to trigger a check for byte size since the last two are
// rate-limited.
EXPECT_CALL(mockMetricsManager, byteSize()).Times(1);
- p.flushIfNecessaryLocked(99, key, mockMetricsManager);
- p.flushIfNecessaryLocked(100, key, mockMetricsManager);
- p.flushIfNecessaryLocked(101, key, mockMetricsManager);
+ p.flushIfNecessaryLocked(key, mockMetricsManager);
+ p.flushIfNecessaryLocked(key, mockMetricsManager);
+ p.flushIfNecessaryLocked(key, mockMetricsManager);
}
TEST(StatsLogProcessorTest, TestRateLimitBroadcast) {
@@ -103,7 +103,7 @@
StatsdStats::kMaxMetricsBytesPerConfig * .95)));
// Expect only one broadcast despite always returning a size that should trigger broadcast.
- p.flushIfNecessaryLocked(1, key, mockMetricsManager);
+ p.flushIfNecessaryLocked(key, mockMetricsManager);
EXPECT_EQ(1, broadcastCount);
// b/73089712
@@ -136,7 +136,7 @@
EXPECT_CALL(mockMetricsManager, dropData(_)).Times(1);
// Expect to call the onDumpReport and skip the broadcast.
- p.flushIfNecessaryLocked(1, key, mockMetricsManager);
+ p.flushIfNecessaryLocked(key, mockMetricsManager);
EXPECT_EQ(0, broadcastCount);
}
diff --git a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp
index 5da0fca..9093155 100644
--- a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp
@@ -271,19 +271,19 @@
// Turn screen off.
event = CreateScreenStateChangedEvent(
android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 2 * NS_PER_SEC); // 0:02
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 2 * NS_PER_SEC);
// Turn screen on.
const int64_t durationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:05
event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, durationStartNs);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), durationStartNs);
// Activate metric.
const int64_t activationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:10
const int64_t activationEndNs =
activationStartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 0:40
event = CreateAppCrashEvent(111, activationStartNs);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), activationStartNs);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 1);
@@ -296,7 +296,7 @@
// Expire activation.
const int64_t expirationNs = activationEndNs + 7 * NS_PER_SEC;
event = CreateScreenBrightnessChangedEvent(64, expirationNs); // 0:47
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), expirationNs);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 2);
@@ -310,24 +310,24 @@
// Turn off screen 10 seconds after activation expiration.
const int64_t durationEndNs = activationEndNs + 10 * NS_PER_SEC; // 0:50
event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, durationEndNs);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(),durationEndNs);
// Turn screen on.
const int64_t duration2StartNs = durationEndNs + 5 * NS_PER_SEC; // 0:55
event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, duration2StartNs);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), duration2StartNs);
// Turn off screen.
const int64_t duration2EndNs = duration2StartNs + 10 * NS_PER_SEC; // 1:05
event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, duration2EndNs);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), duration2EndNs);
// Activate metric.
const int64_t activation2StartNs = duration2EndNs + 5 * NS_PER_SEC; // 1:10
const int64_t activation2EndNs =
activation2StartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 1:40
event = CreateAppCrashEvent(211, activation2StartNs);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), activation2StartNs);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 3);
diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
index f1b6029..b6a6492 100644
--- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
@@ -290,14 +290,14 @@
std::unique_ptr<LogEvent> event;
event = CreateAppCrashEvent(111, bucketStartTimeNs + 5);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 0);
// Activated by battery save mode.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 1);
@@ -312,12 +312,12 @@
// First processed event.
event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
// Activated by screen on event.
event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + 20);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 20);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
@@ -330,7 +330,7 @@
// 2nd processed event.
// The activation by screen_on event expires, but the one by battery save mode is still active.
event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
@@ -344,11 +344,11 @@
// 3rd processed event.
event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
// All activations expired.
event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
// New broadcast since the config is no longer active.
@@ -364,7 +364,7 @@
// Re-activate metric via screen on.
event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 3);
@@ -379,7 +379,7 @@
// 4th processed event.
event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
@@ -509,14 +509,14 @@
std::unique_ptr<LogEvent> event;
event = CreateAppCrashEvent(111, bucketStartTimeNs + 5);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 0);
// Activated by battery save mode.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 1);
@@ -532,12 +532,12 @@
// First processed event.
event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
// Activated by screen on event.
event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + 20);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 20);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
@@ -551,7 +551,7 @@
// 2nd processed event.
// The activation by screen_on event expires, but the one by battery save mode is still active.
event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(),bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
@@ -566,11 +566,11 @@
// 3rd processed event.
event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
// All activations expired.
event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
// New broadcast since the config is no longer active.
@@ -587,7 +587,7 @@
// Re-activate metric via screen on.
event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 3);
@@ -603,11 +603,11 @@
// 4th processed event.
event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
// Re-enable battery saver mode activation.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 3);
@@ -623,11 +623,11 @@
// 5th processed event.
event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
// Cancel battery saver mode activation.
event = CreateScreenBrightnessChangedEvent(64, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 3);
@@ -643,7 +643,7 @@
// Screen-on activation expired.
event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
// New broadcast since the config is no longer active.
@@ -658,11 +658,11 @@
EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
// Re-enable battery saver mode activation.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 5);
@@ -678,7 +678,7 @@
// Cancel battery saver mode activation.
event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 6);
@@ -835,14 +835,14 @@
std::unique_ptr<LogEvent> event;
event = CreateAppCrashEvent(111, bucketStartTimeNs + 5);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 0);
// Activated by battery save mode.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 1);
@@ -859,12 +859,12 @@
// First processed event.
event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
// Activated by screen on event.
event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + 20);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 20);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
@@ -879,7 +879,7 @@
// 2nd processed event.
// The activation by screen_on event expires, but the one by battery save mode is still active.
event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
@@ -895,11 +895,11 @@
// 3rd processed event.
event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
// All activations expired.
event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
// New broadcast since the config is no longer active.
@@ -917,7 +917,7 @@
// Re-activate metric via screen on.
event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 3);
@@ -934,11 +934,11 @@
// 4th processed event.
event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
// Re-enable battery saver mode activation.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 3);
@@ -955,11 +955,11 @@
// 5th processed event.
event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
// Cancel battery saver mode and screen on activation.
event = CreateScreenBrightnessChangedEvent(64, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
// New broadcast since the config is no longer active.
@@ -976,7 +976,7 @@
// Screen-on activation expired.
event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 4);
@@ -991,11 +991,11 @@
EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
// Re-enable battery saver mode activation.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 5);
@@ -1012,7 +1012,7 @@
// Cancel battery saver mode and screen on activation.
event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 6);
@@ -1170,11 +1170,11 @@
// Event that should be ignored.
event = CreateAppCrashEvent(111, bucketStartTimeNs + 1);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 1);
// Activate metric via screen on for 2 minutes.
event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 1);
@@ -1186,11 +1186,11 @@
// 1st processed event.
event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
// Enable battery saver mode activation for 5 minutes.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 + 10);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 + 10);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 1);
@@ -1201,12 +1201,12 @@
// 2nd processed event.
event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 + 40);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 + 40);
// Cancel battery saver mode and screen on activation.
int64_t firstDeactivation = bucketStartTimeNs + NS_PER_SEC * 61;
event = CreateScreenBrightnessChangedEvent(64, firstDeactivation);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), firstDeactivation);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
// New broadcast since the config is no longer active.
@@ -1217,11 +1217,11 @@
// Should be ignored
event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 61 + 80);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 61 + 80);
// Re-enable battery saver mode activation.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 3);
@@ -1233,12 +1233,12 @@
// 3rd processed event.
event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80);
// Cancel battery saver mode activation.
int64_t secondDeactivation = bucketStartTimeNs + NS_PER_SEC * 60 * 13;
event = CreateScreenBrightnessChangedEvent(140, secondDeactivation);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), secondDeactivation);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 4);
@@ -1248,7 +1248,7 @@
// Should be ignored.
event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80);
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
@@ -1388,9 +1388,9 @@
std::unique_ptr<LogEvent> event;
event = CreateAppCrashEvent(111, bucketStartTimeNs + 5);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
event = CreateMoveToForegroundEvent(1111, bucketStartTimeNs + 5);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_FALSE(metricProducer2->mIsActive);
@@ -1398,7 +1398,7 @@
// Activated by battery save mode.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_EQ(broadcastCount, 1);
EXPECT_EQ(activeConfigsBroadcast.size(), 1);
@@ -1424,14 +1424,14 @@
// First processed event.
event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
event = CreateMoveToForegroundEvent(2222, bucketStartTimeNs + 15);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
// Activated by screen on event.
event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + 20);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 20);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
@@ -1455,9 +1455,9 @@
// 2nd processed event.
// The activation by screen_on event expires, but the one by battery save mode is still active.
event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
event = CreateMoveToForegroundEvent(3333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
@@ -1482,15 +1482,15 @@
// 3rd processed event.
event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
event = CreateMoveToForegroundEvent(4444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
// All activations expired.
event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
event = CreateMoveToForegroundEvent(5555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
EXPECT_FALSE(metricsManager->isActive());
// New broadcast since the config is no longer active.
EXPECT_EQ(broadcastCount, 2);
@@ -1517,7 +1517,7 @@
// Re-activate metric via screen on.
event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_EQ(broadcastCount, 3);
EXPECT_EQ(activeConfigsBroadcast.size(), 1);
@@ -1543,13 +1543,13 @@
// 4th processed event.
event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
event = CreateMoveToForegroundEvent(6666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
// Re-enable battery saver mode activation.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_EQ(broadcastCount, 3);
EXPECT_EQ(activeConfigsBroadcast.size(), 1);
@@ -1575,13 +1575,13 @@
// 5th processed event.
event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
event = CreateMoveToForegroundEvent(7777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
// Cancel battery saver mode and screen on activation.
event = CreateScreenBrightnessChangedEvent(64, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(),bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
EXPECT_FALSE(metricsManager->isActive());
// New broadcast since the config is no longer active.
EXPECT_EQ(broadcastCount, 4);
@@ -1607,9 +1607,9 @@
// Screen-on activation expired.
event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13);
event = CreateMoveToForegroundEvent(8888, bucketStartTimeNs + NS_PER_SEC * 60 * 13);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_EQ(broadcastCount, 4);
EXPECT_EQ(activeConfigsBroadcast.size(), 0);
@@ -1633,13 +1633,13 @@
EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
event = CreateMoveToForegroundEvent(9999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
// Re-enable battery saver mode activation.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_EQ(broadcastCount, 5);
EXPECT_EQ(activeConfigsBroadcast.size(), 1);
@@ -1665,7 +1665,7 @@
// Cancel battery saver mode and screen on activation.
event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(),bucketStartTimeNs + NS_PER_SEC * 60 * 16);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_EQ(broadcastCount, 6);
EXPECT_EQ(activeConfigsBroadcast.size(), 0);
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
index 455e4bb..b23bf5d 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
@@ -67,7 +67,7 @@
throw new IllegalStateException("Could not find provider: " + providerName);
}
provider = holder.provider;
- cursor = provider.query(null, Settings.Secure.CONTENT_URI,
+ cursor = provider.query(null, null, Settings.Secure.CONTENT_URI,
new String[] {
Settings.Secure.VALUE
},
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 7de8793..17f1a07 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -53,6 +53,7 @@
import android.os.storage.StorageManager;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
@@ -136,7 +137,7 @@
private boolean mNoPerms;
private boolean mSingleUser;
- private ThreadLocal<String> mCallingPackage;
+ private ThreadLocal<Pair<String, String>> mCallingPackage;
private Transport mTransport = new Transport();
@@ -226,11 +227,13 @@
}
@Override
- public Cursor query(String callingPkg, Uri uri, @Nullable String[] projection,
- @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) {
+ public Cursor query(String callingPkg, @Nullable String featureId, Uri uri,
+ @Nullable String[] projection, @Nullable Bundle queryArgs,
+ @Nullable ICancellationSignal cancellationSignal) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(callingPkg, featureId, uri, null)
+ != AppOpsManager.MODE_ALLOWED) {
// The caller has no access to the data, so return an empty cursor with
// the columns in the requested order. The caller may ask for an invalid
// column and we would not catch that but this is not a problem in practice.
@@ -246,7 +249,8 @@
// we have to execute the query as if allowed to get a cursor with the
// columns. We then use the column names to return an empty cursor.
Cursor cursor;
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
cursor = mInterface.query(
uri, projection, queryArgs,
@@ -264,7 +268,8 @@
return new MatrixCursor(cursor.getColumnNames(), 0);
}
Trace.traceBegin(TRACE_TAG_DATABASE, "query");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return mInterface.query(
uri, projection, queryArgs,
@@ -293,12 +298,15 @@
}
@Override
- public Uri insert(String callingPkg, Uri uri, ContentValues initialValues) {
+ public Uri insert(String callingPkg, @Nullable String featureId, Uri uri,
+ ContentValues initialValues) {
uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
- final String original = setCallingPackage(callingPkg);
+ if (enforceWritePermission(callingPkg, featureId, uri, null)
+ != AppOpsManager.MODE_ALLOWED) {
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return rejectInsert(uri, initialValues);
} finally {
@@ -306,7 +314,8 @@
}
}
Trace.traceBegin(TRACE_TAG_DATABASE, "insert");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return maybeAddUserId(mInterface.insert(uri, initialValues), userId);
} catch (RemoteException e) {
@@ -318,14 +327,17 @@
}
@Override
- public int bulkInsert(String callingPkg, Uri uri, ContentValues[] initialValues) {
+ public int bulkInsert(String callingPkg, @Nullable String featureId, Uri uri,
+ ContentValues[] initialValues) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceWritePermission(callingPkg, featureId, uri, null)
+ != AppOpsManager.MODE_ALLOWED) {
return 0;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "bulkInsert");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return mInterface.bulkInsert(uri, initialValues);
} catch (RemoteException e) {
@@ -337,8 +349,8 @@
}
@Override
- public ContentProviderResult[] applyBatch(String callingPkg, String authority,
- ArrayList<ContentProviderOperation> operations)
+ public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String featureId,
+ String authority, ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
validateIncomingAuthority(authority);
int numOperations = operations.size();
@@ -355,20 +367,21 @@
operations.set(i, operation);
}
if (operation.isReadOperation()) {
- if (enforceReadPermission(callingPkg, uri, null)
+ if (enforceReadPermission(callingPkg, featureId, uri, null)
!= AppOpsManager.MODE_ALLOWED) {
throw new OperationApplicationException("App op not allowed", 0);
}
}
if (operation.isWriteOperation()) {
- if (enforceWritePermission(callingPkg, uri, null)
+ if (enforceWritePermission(callingPkg, featureId, uri, null)
!= AppOpsManager.MODE_ALLOWED) {
throw new OperationApplicationException("App op not allowed", 0);
}
}
}
Trace.traceBegin(TRACE_TAG_DATABASE, "applyBatch");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
ContentProviderResult[] results = mInterface.applyBatch(authority,
operations);
@@ -390,14 +403,17 @@
}
@Override
- public int delete(String callingPkg, Uri uri, String selection, String[] selectionArgs) {
+ public int delete(String callingPkg, @Nullable String featureId, Uri uri, String selection,
+ String[] selectionArgs) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceWritePermission(callingPkg, featureId, uri, null)
+ != AppOpsManager.MODE_ALLOWED) {
return 0;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "delete");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return mInterface.delete(uri, selection, selectionArgs);
} catch (RemoteException e) {
@@ -409,15 +425,17 @@
}
@Override
- public int update(String callingPkg, Uri uri, ContentValues values, String selection,
- String[] selectionArgs) {
+ public int update(String callingPkg, @Nullable String featureId, Uri uri,
+ ContentValues values, String selection, String[] selectionArgs) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceWritePermission(callingPkg, featureId, uri, null)
+ != AppOpsManager.MODE_ALLOWED) {
return 0;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "update");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return mInterface.update(uri, values, selection, selectionArgs);
} catch (RemoteException e) {
@@ -429,14 +447,15 @@
}
@Override
- public ParcelFileDescriptor openFile(
- String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal,
- IBinder callerToken) throws FileNotFoundException {
+ public ParcelFileDescriptor openFile(String callingPkg, @Nullable String featureId,
+ Uri uri, String mode, ICancellationSignal cancellationSignal, IBinder callerToken)
+ throws FileNotFoundException {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- enforceFilePermission(callingPkg, uri, mode, callerToken);
+ enforceFilePermission(callingPkg, featureId, uri, mode, callerToken);
Trace.traceBegin(TRACE_TAG_DATABASE, "openFile");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return mInterface.openFile(
uri, mode, CancellationSignal.fromTransport(cancellationSignal));
@@ -449,14 +468,15 @@
}
@Override
- public AssetFileDescriptor openAssetFile(
- String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal)
+ public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String featureId,
+ Uri uri, String mode, ICancellationSignal cancellationSignal)
throws FileNotFoundException {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- enforceFilePermission(callingPkg, uri, mode, null);
+ enforceFilePermission(callingPkg, featureId, uri, mode, null);
Trace.traceBegin(TRACE_TAG_DATABASE, "openAssetFile");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return mInterface.openAssetFile(
uri, mode, CancellationSignal.fromTransport(cancellationSignal));
@@ -469,12 +489,13 @@
}
@Override
- public Bundle call(String callingPkg, String authority, String method, @Nullable String arg,
- @Nullable Bundle extras) {
+ public Bundle call(String callingPkg, @Nullable String featureId, String authority,
+ String method, @Nullable String arg, @Nullable Bundle extras) {
validateIncomingAuthority(authority);
Bundle.setDefusable(extras, true);
Trace.traceBegin(TRACE_TAG_DATABASE, "call");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return mInterface.call(authority, method, arg, extras);
} catch (RemoteException e) {
@@ -501,14 +522,16 @@
}
@Override
- public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri uri, String mimeType,
- Bundle opts, ICancellationSignal cancellationSignal) throws FileNotFoundException {
+ public AssetFileDescriptor openTypedAssetFile(String callingPkg,
+ @Nullable String featureId, Uri uri, String mimeType, Bundle opts,
+ ICancellationSignal cancellationSignal) throws FileNotFoundException {
Bundle.setDefusable(opts, true);
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- enforceFilePermission(callingPkg, uri, "r", null);
+ enforceFilePermission(callingPkg, featureId, uri, "r", null);
Trace.traceBegin(TRACE_TAG_DATABASE, "openTypedAssetFile");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return mInterface.openTypedAssetFile(
uri, mimeType, opts, CancellationSignal.fromTransport(cancellationSignal));
@@ -526,15 +549,17 @@
}
@Override
- public Uri canonicalize(String callingPkg, Uri uri) {
+ public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri) {
uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = getUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(callingPkg, featureId, uri, null)
+ != AppOpsManager.MODE_ALLOWED) {
return null;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "canonicalize");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return maybeAddUserId(mInterface.canonicalize(uri), userId);
} catch (RemoteException e) {
@@ -546,15 +571,17 @@
}
@Override
- public Uri uncanonicalize(String callingPkg, Uri uri) {
+ public Uri uncanonicalize(String callingPkg, String featureId, Uri uri) {
uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = getUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(callingPkg, featureId, uri, null)
+ != AppOpsManager.MODE_ALLOWED) {
return null;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "uncanonicalize");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return maybeAddUserId(mInterface.uncanonicalize(uri), userId);
} catch (RemoteException e) {
@@ -566,15 +593,17 @@
}
@Override
- public boolean refresh(String callingPkg, Uri uri, Bundle args,
+ public boolean refresh(String callingPkg, String featureId, Uri uri, Bundle args,
ICancellationSignal cancellationSignal) throws RemoteException {
uri = validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
- if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
+ if (enforceReadPermission(callingPkg, featureId, uri, null)
+ != AppOpsManager.MODE_ALLOWED) {
return false;
}
Trace.traceBegin(TRACE_TAG_DATABASE, "refresh");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return mInterface.refresh(uri, args,
CancellationSignal.fromTransport(cancellationSignal));
@@ -585,11 +614,13 @@
}
@Override
- public int checkUriPermission(String callingPkg, Uri uri, int uid, int modeFlags) {
+ public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri uri,
+ int uid, int modeFlags) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
Trace.traceBegin(TRACE_TAG_DATABASE, "checkUriPermission");
- final String original = setCallingPackage(callingPkg);
+ final Pair<String, String> original = setCallingPackage(
+ new Pair<>(callingPkg, featureId));
try {
return mInterface.checkUriPermission(uri, uid, modeFlags);
} catch (RemoteException e) {
@@ -600,44 +631,47 @@
}
}
- private void enforceFilePermission(String callingPkg, Uri uri, String mode,
- IBinder callerToken) throws FileNotFoundException, SecurityException {
+ private void enforceFilePermission(String callingPkg, @Nullable String featureId, Uri uri,
+ String mode, IBinder callerToken) throws FileNotFoundException, SecurityException {
if (mode != null && mode.indexOf('w') != -1) {
- if (enforceWritePermission(callingPkg, uri, callerToken)
+ if (enforceWritePermission(callingPkg, featureId, uri, callerToken)
!= AppOpsManager.MODE_ALLOWED) {
throw new FileNotFoundException("App op not allowed");
}
} else {
- if (enforceReadPermission(callingPkg, uri, callerToken)
+ if (enforceReadPermission(callingPkg, featureId, uri, callerToken)
!= AppOpsManager.MODE_ALLOWED) {
throw new FileNotFoundException("App op not allowed");
}
}
}
- private int enforceReadPermission(String callingPkg, Uri uri, IBinder callerToken)
+ private int enforceReadPermission(String callingPkg, @Nullable String featureId, Uri uri,
+ IBinder callerToken)
throws SecurityException {
- final int mode = enforceReadPermissionInner(uri, callingPkg, callerToken);
+ final int mode = enforceReadPermissionInner(uri, callingPkg, featureId, callerToken);
if (mode != MODE_ALLOWED) {
return mode;
}
- return noteProxyOp(callingPkg, mReadOp);
+ return noteProxyOp(callingPkg, featureId, mReadOp);
}
- private int enforceWritePermission(String callingPkg, Uri uri, IBinder callerToken)
+ private int enforceWritePermission(String callingPkg, String featureId, Uri uri,
+ IBinder callerToken)
throws SecurityException {
- final int mode = enforceWritePermissionInner(uri, callingPkg, callerToken);
+ final int mode = enforceWritePermissionInner(uri, callingPkg, featureId, callerToken);
if (mode != MODE_ALLOWED) {
return mode;
}
- return noteProxyOp(callingPkg, mWriteOp);
+ return noteProxyOp(callingPkg, featureId, mWriteOp);
}
- private int noteProxyOp(String callingPkg, int op) {
+ private int noteProxyOp(String callingPkg, String featureId, int op) {
if (op != AppOpsManager.OP_NONE) {
- int mode = mAppOpsManager.noteProxyOp(op, callingPkg);
+ int mode = mAppOpsManager.noteProxyOp(op, callingPkg, Binder.getCallingUid(),
+ featureId, null);
return mode == MODE_DEFAULT ? MODE_IGNORED : mode;
}
@@ -659,18 +693,19 @@
* associated with that permission.
*/
private int checkPermissionAndAppOp(String permission, String callingPkg,
- IBinder callerToken) {
+ @Nullable String featureId, IBinder callerToken) {
if (getContext().checkPermission(permission, Binder.getCallingPid(), Binder.getCallingUid(),
callerToken) != PERMISSION_GRANTED) {
return MODE_ERRORED;
}
- return mTransport.noteProxyOp(callingPkg, AppOpsManager.permissionToOpCode(permission));
+ return mTransport.noteProxyOp(callingPkg, featureId,
+ AppOpsManager.permissionToOpCode(permission));
}
/** {@hide} */
- protected int enforceReadPermissionInner(Uri uri, String callingPkg, IBinder callerToken)
- throws SecurityException {
+ protected int enforceReadPermissionInner(Uri uri, String callingPkg,
+ @Nullable String featureId, IBinder callerToken) throws SecurityException {
final Context context = getContext();
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
@@ -684,7 +719,8 @@
if (mExported && checkUser(pid, uid, context)) {
final String componentPerm = getReadPermission();
if (componentPerm != null) {
- final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, callerToken);
+ final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, featureId,
+ callerToken);
if (mode == MODE_ALLOWED) {
return MODE_ALLOWED;
} else {
@@ -703,7 +739,8 @@
for (PathPermission pp : pps) {
final String pathPerm = pp.getReadPermission();
if (pathPerm != null && pp.match(path)) {
- final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, callerToken);
+ final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, featureId,
+ callerToken);
if (mode == MODE_ALLOWED) {
return MODE_ALLOWED;
} else {
@@ -751,8 +788,8 @@
}
/** {@hide} */
- protected int enforceWritePermissionInner(Uri uri, String callingPkg, IBinder callerToken)
- throws SecurityException {
+ protected int enforceWritePermissionInner(Uri uri, String callingPkg,
+ @Nullable String featureId, IBinder callerToken) throws SecurityException {
final Context context = getContext();
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
@@ -766,7 +803,8 @@
if (mExported && checkUser(pid, uid, context)) {
final String componentPerm = getWritePermission();
if (componentPerm != null) {
- final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, callerToken);
+ final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, featureId,
+ callerToken);
if (mode == MODE_ALLOWED) {
return MODE_ALLOWED;
} else {
@@ -785,7 +823,8 @@
for (PathPermission pp : pps) {
final String pathPerm = pp.getWritePermission();
if (pathPerm != null && pp.match(path)) {
- final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, callerToken);
+ final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, featureId,
+ callerToken);
if (mode == MODE_ALLOWED) {
return MODE_ALLOWED;
} else {
@@ -851,11 +890,11 @@
}
/**
- * Set the calling package, returning the current value (or {@code null})
+ * Set the calling package/feature, returning the current value (or {@code null})
* which can be used later to restore the previous state.
*/
- private String setCallingPackage(String callingPackage) {
- final String original = mCallingPackage.get();
+ private Pair<String, String> setCallingPackage(Pair<String, String> callingPackage) {
+ final Pair<String, String> original = mCallingPackage.get();
mCallingPackage.set(callingPackage);
onCallingPackageChanged();
return original;
@@ -876,16 +915,42 @@
* calling UID.
*/
public final @Nullable String getCallingPackage() {
- final String pkg = mCallingPackage.get();
+ final Pair<String, String> pkg = mCallingPackage.get();
if (pkg != null) {
- mTransport.mAppOpsManager.checkPackage(Binder.getCallingUid(), pkg);
+ mTransport.mAppOpsManager.checkPackage(Binder.getCallingUid(), pkg.first);
+ return pkg.first;
}
- return pkg;
+
+ return null;
+ }
+
+ /**
+ * Return the feature in the package of the caller that initiated the request being
+ * processed on the current thread. Returns {@code null} if not currently processing
+ * a request of the request is for the default feature.
+ * <p>
+ * This will always return {@code null} when processing
+ * {@link #getType(Uri)} or {@link #getStreamTypes(Uri, String)} requests.
+ *
+ * @see #getCallingPackage
+ */
+ public final @Nullable String getCallingFeatureId() {
+ final Pair<String, String> pkg = mCallingPackage.get();
+ if (pkg != null) {
+ return pkg.second;
+ }
+
+ return null;
}
/** {@hide} */
public final @Nullable String getCallingPackageUnchecked() {
- return mCallingPackage.get();
+ final Pair<String, String> pkg = mCallingPackage.get();
+ if (pkg != null) {
+ return pkg.first;
+ }
+
+ return null;
}
/** {@hide} */
@@ -899,10 +964,10 @@
/** {@hide} */
public final long binderToken;
/** {@hide} */
- public final String callingPackage;
+ public final Pair<String, String> callingPackage;
/** {@hide} */
- public CallingIdentity(long binderToken, String callingPackage) {
+ public CallingIdentity(long binderToken, Pair<String, String> callingPackage) {
this.binderToken = binderToken;
this.callingPackage = callingPackage;
}
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 8a4330e..d2632e7 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -80,6 +80,7 @@
private final IContentProvider mContentProvider;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final String mPackageName;
+ private final @Nullable String mFeatureId;
private final String mAuthority;
private final boolean mStable;
@@ -103,6 +104,7 @@
mContentResolver = contentResolver;
mContentProvider = contentProvider;
mPackageName = contentResolver.mPackageName;
+ mFeatureId = contentResolver.mFeatureId;
mAuthority = authority;
mStable = stable;
@@ -193,7 +195,7 @@
cancellationSignal.setRemote(remoteCancellationSignal);
}
final Cursor cursor = mContentProvider.query(
- mPackageName, uri, projection, queryArgs, remoteCancellationSignal);
+ mPackageName, mFeatureId, uri, projection, queryArgs, remoteCancellationSignal);
if (cursor == null) {
return null;
}
@@ -253,7 +255,7 @@
beforeRemote();
try {
- return mContentProvider.canonicalize(mPackageName, url);
+ return mContentProvider.canonicalize(mPackageName, mFeatureId, url);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -271,7 +273,7 @@
beforeRemote();
try {
- return mContentProvider.uncanonicalize(mPackageName, url);
+ return mContentProvider.uncanonicalize(mPackageName, mFeatureId, url);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -296,7 +298,8 @@
remoteCancellationSignal = mContentProvider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
- return mContentProvider.refresh(mPackageName, url, args, remoteCancellationSignal);
+ return mContentProvider.refresh(mPackageName, mFeatureId, url, args,
+ remoteCancellationSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -315,7 +318,8 @@
beforeRemote();
try {
- return mContentProvider.checkUriPermission(mPackageName, uri, uid, modeFlags);
+ return mContentProvider.checkUriPermission(mPackageName, mFeatureId, uri, uid,
+ modeFlags);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -334,7 +338,7 @@
beforeRemote();
try {
- return mContentProvider.insert(mPackageName, url, initialValues);
+ return mContentProvider.insert(mPackageName, mFeatureId, url, initialValues);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -354,7 +358,7 @@
beforeRemote();
try {
- return mContentProvider.bulkInsert(mPackageName, url, initialValues);
+ return mContentProvider.bulkInsert(mPackageName, mFeatureId, url, initialValues);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -373,7 +377,8 @@
beforeRemote();
try {
- return mContentProvider.delete(mPackageName, url, selection, selectionArgs);
+ return mContentProvider.delete(mPackageName, mFeatureId, url, selection,
+ selectionArgs);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -392,7 +397,8 @@
beforeRemote();
try {
- return mContentProvider.update(mPackageName, url, values, selection, selectionArgs);
+ return mContentProvider.update(mPackageName, mFeatureId, url, values, selection,
+ selectionArgs);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -436,7 +442,8 @@
remoteSignal = mContentProvider.createCancellationSignal();
signal.setRemote(remoteSignal);
}
- return mContentProvider.openFile(mPackageName, url, mode, remoteSignal, null);
+ return mContentProvider.openFile(mPackageName, mFeatureId, url, mode, remoteSignal,
+ null);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -480,7 +487,8 @@
remoteSignal = mContentProvider.createCancellationSignal();
signal.setRemote(remoteSignal);
}
- return mContentProvider.openAssetFile(mPackageName, url, mode, remoteSignal);
+ return mContentProvider.openAssetFile(mPackageName, mFeatureId, url, mode,
+ remoteSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -521,7 +529,7 @@
signal.setRemote(remoteSignal);
}
return mContentProvider.openTypedAssetFile(
- mPackageName, uri, mimeTypeFilter, opts, remoteSignal);
+ mPackageName, mFeatureId, uri, mimeTypeFilter, opts, remoteSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -548,7 +556,7 @@
beforeRemote();
try {
- return mContentProvider.applyBatch(mPackageName, authority, operations);
+ return mContentProvider.applyBatch(mPackageName, mFeatureId, authority, operations);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -574,7 +582,7 @@
beforeRemote();
try {
- return mContentProvider.call(mPackageName, authority, method, arg, extras);
+ return mContentProvider.call(mPackageName, mFeatureId, authority, method, arg, extras);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index cd735d4..f082690 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -83,6 +83,7 @@
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String callingFeatureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
// String[] projection
@@ -101,7 +102,8 @@
ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(
data.readStrongBinder());
- Cursor cursor = query(callingPkg, url, projection, queryArgs, cancellationSignal);
+ Cursor cursor = query(callingPkg, callingFeatureId, url, projection, queryArgs,
+ cancellationSignal);
if (cursor != null) {
CursorToBulkCursorAdaptor adaptor = null;
@@ -148,10 +150,11 @@
{
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
ContentValues values = ContentValues.CREATOR.createFromParcel(data);
- Uri out = insert(callingPkg, url, values);
+ Uri out = insert(callingPkg, featureId, url, values);
reply.writeNoException();
Uri.writeToParcel(reply, out);
return true;
@@ -161,10 +164,11 @@
{
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
ContentValues[] values = data.createTypedArray(ContentValues.CREATOR);
- int count = bulkInsert(callingPkg, url, values);
+ int count = bulkInsert(callingPkg, featureId, url, values);
reply.writeNoException();
reply.writeInt(count);
return true;
@@ -174,6 +178,7 @@
{
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
String authority = data.readString();
final int numOperations = data.readInt();
final ArrayList<ContentProviderOperation> operations =
@@ -181,8 +186,8 @@
for (int i = 0; i < numOperations; i++) {
operations.add(i, ContentProviderOperation.CREATOR.createFromParcel(data));
}
- final ContentProviderResult[] results = applyBatch(callingPkg, authority,
- operations);
+ final ContentProviderResult[] results = applyBatch(callingPkg, featureId,
+ authority, operations);
reply.writeNoException();
reply.writeTypedArray(results, 0);
return true;
@@ -192,11 +197,12 @@
{
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
String selection = data.readString();
String[] selectionArgs = data.readStringArray();
- int count = delete(callingPkg, url, selection, selectionArgs);
+ int count = delete(callingPkg, featureId, url, selection, selectionArgs);
reply.writeNoException();
reply.writeInt(count);
@@ -207,12 +213,14 @@
{
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
ContentValues values = ContentValues.CREATOR.createFromParcel(data);
String selection = data.readString();
String[] selectionArgs = data.readStringArray();
- int count = update(callingPkg, url, values, selection, selectionArgs);
+ int count = update(callingPkg, featureId, url, values, selection,
+ selectionArgs);
reply.writeNoException();
reply.writeInt(count);
@@ -223,6 +231,7 @@
{
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
String mode = data.readString();
ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
@@ -230,7 +239,7 @@
IBinder callerToken = data.readStrongBinder();
ParcelFileDescriptor fd;
- fd = openFile(callingPkg, url, mode, signal, callerToken);
+ fd = openFile(callingPkg, featureId, url, mode, signal, callerToken);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
@@ -246,13 +255,14 @@
{
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
String mode = data.readString();
ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
data.readStrongBinder());
AssetFileDescriptor fd;
- fd = openAssetFile(callingPkg, url, mode, signal);
+ fd = openAssetFile(callingPkg, featureId, url, mode, signal);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
@@ -269,12 +279,14 @@
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
String authority = data.readString();
String method = data.readString();
String stringArg = data.readString();
Bundle args = data.readBundle();
- Bundle responseBundle = call(callingPkg, authority, method, stringArg, args);
+ Bundle responseBundle = call(callingPkg, featureId, authority, method,
+ stringArg, args);
reply.writeNoException();
reply.writeBundle(responseBundle);
@@ -297,6 +309,7 @@
{
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
String mimeType = data.readString();
Bundle opts = data.readBundle();
@@ -304,7 +317,7 @@
data.readStrongBinder());
AssetFileDescriptor fd;
- fd = openTypedAssetFile(callingPkg, url, mimeType, opts, signal);
+ fd = openTypedAssetFile(callingPkg, featureId, url, mimeType, opts, signal);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
@@ -330,9 +343,10 @@
{
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
- Uri out = canonicalize(callingPkg, url);
+ Uri out = canonicalize(callingPkg, featureId, url);
reply.writeNoException();
Uri.writeToParcel(reply, out);
return true;
@@ -342,9 +356,10 @@
{
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
- Uri out = uncanonicalize(callingPkg, url);
+ Uri out = uncanonicalize(callingPkg, featureId, url);
reply.writeNoException();
Uri.writeToParcel(reply, out);
return true;
@@ -353,12 +368,13 @@
case REFRESH_TRANSACTION: {
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
Bundle args = data.readBundle();
ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
data.readStrongBinder());
- boolean out = refresh(callingPkg, url, args, signal);
+ boolean out = refresh(callingPkg, featureId, url, args, signal);
reply.writeNoException();
reply.writeInt(out ? 0 : -1);
return true;
@@ -367,11 +383,12 @@
case CHECK_URI_PERMISSION_TRANSACTION: {
data.enforceInterface(IContentProvider.descriptor);
String callingPkg = data.readString();
+ String featureId = data.readString();
Uri uri = Uri.CREATOR.createFromParcel(data);
int uid = data.readInt();
int modeFlags = data.readInt();
- int out = checkUriPermission(callingPkg, uri, uid, modeFlags);
+ int out = checkUriPermission(callingPkg, featureId, uri, uid, modeFlags);
reply.writeNoException();
reply.writeInt(out);
return true;
@@ -407,8 +424,9 @@
}
@Override
- public Cursor query(String callingPkg, Uri url, @Nullable String[] projection,
- @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal)
+ public Cursor query(String callingPkg, @Nullable String featureId, Uri url,
+ @Nullable String[] projection, @Nullable Bundle queryArgs,
+ @Nullable ICancellationSignal cancellationSignal)
throws RemoteException {
BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
Parcel data = Parcel.obtain();
@@ -417,6 +435,7 @@
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
int length = 0;
if (projection != null) {
@@ -478,7 +497,8 @@
}
@Override
- public Uri insert(String callingPkg, Uri url, ContentValues values) throws RemoteException
+ public Uri insert(String callingPkg, @Nullable String featureId, Uri url,
+ ContentValues values) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -486,6 +506,7 @@
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
values.writeToParcel(data, 0);
@@ -501,13 +522,15 @@
}
@Override
- public int bulkInsert(String callingPkg, Uri url, ContentValues[] values) throws RemoteException {
+ public int bulkInsert(String callingPkg, @Nullable String featureId, Uri url,
+ ContentValues[] values) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
data.writeTypedArray(values, 0);
@@ -523,14 +546,15 @@
}
@Override
- public ContentProviderResult[] applyBatch(String callingPkg, String authority,
- ArrayList<ContentProviderOperation> operations)
- throws RemoteException, OperationApplicationException {
+ public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String featureId,
+ String authority, ArrayList<ContentProviderOperation> operations)
+ throws RemoteException, OperationApplicationException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
data.writeString(authority);
data.writeInt(operations.size());
for (ContentProviderOperation operation : operations) {
@@ -549,14 +573,15 @@
}
@Override
- public int delete(String callingPkg, Uri url, String selection, String[] selectionArgs)
- throws RemoteException {
+ public int delete(String callingPkg, @Nullable String featureId, Uri url, String selection,
+ String[] selectionArgs) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
data.writeString(selection);
data.writeStringArray(selectionArgs);
@@ -573,14 +598,15 @@
}
@Override
- public int update(String callingPkg, Uri url, ContentValues values, String selection,
- String[] selectionArgs) throws RemoteException {
+ public int update(String callingPkg, @Nullable String featureId, Uri url,
+ ContentValues values, String selection, String[] selectionArgs) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
values.writeToParcel(data, 0);
data.writeString(selection);
@@ -598,8 +624,8 @@
}
@Override
- public ParcelFileDescriptor openFile(
- String callingPkg, Uri url, String mode, ICancellationSignal signal, IBinder token)
+ public ParcelFileDescriptor openFile(String callingPkg, @Nullable String featureId, Uri url,
+ String mode, ICancellationSignal signal, IBinder token)
throws RemoteException, FileNotFoundException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -607,6 +633,7 @@
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
data.writeString(mode);
data.writeStrongBinder(signal != null ? signal.asBinder() : null);
@@ -626,8 +653,8 @@
}
@Override
- public AssetFileDescriptor openAssetFile(
- String callingPkg, Uri url, String mode, ICancellationSignal signal)
+ public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String featureId,
+ Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -635,6 +662,7 @@
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
data.writeString(mode);
data.writeStrongBinder(signal != null ? signal.asBinder() : null);
@@ -653,14 +681,15 @@
}
@Override
- public Bundle call(String callingPkg, String authority, String method, String request,
- Bundle args) throws RemoteException {
+ public Bundle call(String callingPkg, @Nullable String featureId, String authority,
+ String method, String request, Bundle args) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
data.writeString(authority);
data.writeString(method);
data.writeString(request);
@@ -700,14 +729,16 @@
}
@Override
- public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri url, String mimeType,
- Bundle opts, ICancellationSignal signal) throws RemoteException, FileNotFoundException {
+ public AssetFileDescriptor openTypedAssetFile(String callingPkg, @Nullable String featureId,
+ Uri url, String mimeType, Bundle opts, ICancellationSignal signal)
+ throws RemoteException, FileNotFoundException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
data.writeString(mimeType);
data.writeBundle(opts);
@@ -747,14 +778,15 @@
}
@Override
- public Uri canonicalize(String callingPkg, Uri url) throws RemoteException
- {
+ public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri url)
+ throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
mRemote.transact(IContentProvider.CANONICALIZE_TRANSACTION, data, reply, 0);
@@ -769,13 +801,15 @@
}
@Override
- public Uri uncanonicalize(String callingPkg, Uri url) throws RemoteException {
+ public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri url)
+ throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
mRemote.transact(IContentProvider.UNCANONICALIZE_TRANSACTION, data, reply, 0);
@@ -790,14 +824,15 @@
}
@Override
- public boolean refresh(String callingPkg, Uri url, Bundle args, ICancellationSignal signal)
- throws RemoteException {
+ public boolean refresh(String callingPkg, @Nullable String featureId, Uri url, Bundle args,
+ ICancellationSignal signal) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
data.writeBundle(args);
data.writeStrongBinder(signal != null ? signal.asBinder() : null);
@@ -814,14 +849,15 @@
}
@Override
- public int checkUriPermission(String callingPkg, Uri url, int uid, int modeFlags)
- throws RemoteException {
+ public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri url, int uid,
+ int modeFlags) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
data.writeString(callingPkg);
+ data.writeString(featureId);
url.writeToParcel(data, 0);
data.writeInt(uid);
data.writeInt(modeFlags);
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 7f9ea76..2657cc5 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -649,6 +649,7 @@
public ContentResolver(@Nullable Context context, @Nullable ContentInterface wrapped) {
mContext = context != null ? context : ActivityThread.currentApplication();
mPackageName = mContext.getOpPackageName();
+ mFeatureId = mContext.getFeatureId();
mTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
mWrapped = wrapped;
}
@@ -968,7 +969,7 @@
cancellationSignal.setRemote(remoteCancellationSignal);
}
try {
- qCursor = unstableProvider.query(mPackageName, uri, projection,
+ qCursor = unstableProvider.query(mPackageName, mFeatureId, uri, projection,
queryArgs, remoteCancellationSignal);
} catch (DeadObjectException e) {
// The remote process has died... but we only hold an unstable
@@ -979,8 +980,8 @@
if (stableProvider == null) {
return null;
}
- qCursor = stableProvider.query(
- mPackageName, uri, projection, queryArgs, remoteCancellationSignal);
+ qCursor = stableProvider.query(mPackageName, mFeatureId, uri, projection,
+ queryArgs, remoteCancellationSignal);
}
if (qCursor == null) {
return null;
@@ -1070,7 +1071,7 @@
}
try {
- return provider.canonicalize(mPackageName, url);
+ return provider.canonicalize(mPackageName, mFeatureId, url);
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
@@ -1114,7 +1115,7 @@
}
try {
- return provider.uncanonicalize(mPackageName, url);
+ return provider.uncanonicalize(mPackageName, mFeatureId, url);
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
@@ -1163,7 +1164,8 @@
remoteCancellationSignal = provider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
- return provider.refresh(mPackageName, url, args, remoteCancellationSignal);
+ return provider.refresh(mPackageName, mFeatureId, url, args,
+ remoteCancellationSignal);
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
@@ -1564,7 +1566,7 @@
try {
fd = unstableProvider.openAssetFile(
- mPackageName, uri, mode, remoteCancellationSignal);
+ mPackageName, mFeatureId, uri, mode, remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -1579,7 +1581,7 @@
throw new FileNotFoundException("No content provider: " + uri);
}
fd = stableProvider.openAssetFile(
- mPackageName, uri, mode, remoteCancellationSignal);
+ mPackageName, mFeatureId, uri, mode, remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -1730,7 +1732,7 @@
try {
fd = unstableProvider.openTypedAssetFile(
- mPackageName, uri, mimeType, opts, remoteCancellationSignal);
+ mPackageName, mFeatureId, uri, mimeType, opts, remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -1745,7 +1747,7 @@
throw new FileNotFoundException("No content provider: " + uri);
}
fd = stableProvider.openTypedAssetFile(
- mPackageName, uri, mimeType, opts, remoteCancellationSignal);
+ mPackageName, mFeatureId, uri, mimeType, opts, remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -1870,7 +1872,7 @@
}
try {
long startTime = SystemClock.uptimeMillis();
- Uri createdRow = provider.insert(mPackageName, url, values);
+ Uri createdRow = provider.insert(mPackageName, mFeatureId, url, values);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
return createdRow;
@@ -1951,7 +1953,7 @@
}
try {
long startTime = SystemClock.uptimeMillis();
- int rowsCreated = provider.bulkInsert(mPackageName, url, values);
+ int rowsCreated = provider.bulkInsert(mPackageName, mFeatureId, url, values);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "bulkinsert", null /* where */);
return rowsCreated;
@@ -1991,7 +1993,8 @@
}
try {
long startTime = SystemClock.uptimeMillis();
- int rowsDeleted = provider.delete(mPackageName, url, where, selectionArgs);
+ int rowsDeleted = provider.delete(mPackageName, mFeatureId, url, where,
+ selectionArgs);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "delete", where);
return rowsDeleted;
@@ -2035,7 +2038,8 @@
}
try {
long startTime = SystemClock.uptimeMillis();
- int rowsUpdated = provider.update(mPackageName, uri, values, where, selectionArgs);
+ int rowsUpdated = provider.update(mPackageName, mFeatureId, uri, values, where,
+ selectionArgs);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, uri, "update", where);
return rowsUpdated;
@@ -2084,7 +2088,8 @@
throw new IllegalArgumentException("Unknown authority " + authority);
}
try {
- final Bundle res = provider.call(mPackageName, authority, method, arg, extras);
+ final Bundle res = provider.call(mPackageName, mFeatureId, authority, method, arg,
+ extras);
Bundle.setDefusable(res, true);
return res;
} catch (RemoteException e) {
@@ -3436,6 +3441,11 @@
return mPackageName;
}
+ /** @hide */
+ public @Nullable String getFeatureId() {
+ return mFeatureId;
+ }
+
@UnsupportedAppUsage
private static volatile IContentService sContentService;
@UnsupportedAppUsage
@@ -3443,6 +3453,7 @@
@UnsupportedAppUsage
final String mPackageName;
+ final @Nullable String mFeatureId;
final int mTargetSdkVersion;
final ContentInterface mWrapped;
@@ -3638,19 +3649,19 @@
orientation.value = (extras != null) ? extras.getInt(EXTRA_ORIENTATION, 0) : 0;
return afd;
}), (ImageDecoder decoder, ImageInfo info, Source source) -> {
- decoder.setAllocator(allocator);
+ decoder.setAllocator(allocator);
- // One last-ditch check to see if we've been canceled.
- if (signal != null) signal.throwIfCanceled();
+ // One last-ditch check to see if we've been canceled.
+ if (signal != null) signal.throwIfCanceled();
- // We requested a rough thumbnail size, but the remote size may have
- // returned something giant, so defensively scale down as needed.
- final int widthSample = info.getSize().getWidth() / size.getWidth();
- final int heightSample = info.getSize().getHeight() / size.getHeight();
- final int sample = Math.min(widthSample, heightSample);
- if (sample > 1) {
- decoder.setTargetSampleSize(sample);
- }
+ // We requested a rough thumbnail size, but the remote size may have
+ // returned something giant, so defensively scale down as needed.
+ final int widthSample = info.getSize().getWidth() / size.getWidth();
+ final int heightSample = info.getSize().getHeight() / size.getHeight();
+ final int sample = Math.max(widthSample, heightSample);
+ if (sample > 1) {
+ decoder.setTargetSampleSize(sample);
+ }
});
// Transform the bitmap if requested. We use a side-channel to
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index fade0ab..d2c97c4 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -16,7 +16,6 @@
package android.content;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.content.res.AssetFileDescriptor;
@@ -38,66 +37,96 @@
* @hide
*/
public interface IContentProvider extends IInterface {
- public Cursor query(String callingPkg, Uri url, @Nullable String[] projection,
+ public Cursor query(String callingPkg, @Nullable String featureId, Uri url,
+ @Nullable String[] projection,
@Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal)
throws RemoteException;
public String getType(Uri url) throws RemoteException;
- @UnsupportedAppUsage
- public Uri insert(String callingPkg, Uri url, ContentValues initialValues)
- throws RemoteException;
- @UnsupportedAppUsage
- public int bulkInsert(String callingPkg, Uri url, ContentValues[] initialValues)
- throws RemoteException;
- @UnsupportedAppUsage
- public int delete(String callingPkg, Uri url, String selection, String[] selectionArgs)
- throws RemoteException;
- @UnsupportedAppUsage
- public int update(String callingPkg, Uri url, ContentValues values, String selection,
- String[] selectionArgs) throws RemoteException;
- public ParcelFileDescriptor openFile(
- String callingPkg, Uri url, String mode, ICancellationSignal signal,
- IBinder callerToken)
- throws RemoteException, FileNotFoundException;
- public AssetFileDescriptor openAssetFile(
- String callingPkg, Uri url, String mode, ICancellationSignal signal)
- throws RemoteException, FileNotFoundException;
-
@Deprecated
- public default ContentProviderResult[] applyBatch(String callingPkg,
- ArrayList<ContentProviderOperation> operations)
- throws RemoteException, OperationApplicationException {
- return applyBatch(callingPkg, "unknown", operations);
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+ + "ContentProviderClient#insert(android.net.Uri, android.content.ContentValues)} "
+ + "instead")
+ public default Uri insert(String callingPkg, Uri url, ContentValues initialValues)
+ throws RemoteException {
+ return insert(callingPkg, null, url, initialValues);
}
+ public Uri insert(String callingPkg, String featureId, Uri url, ContentValues initialValues)
+ throws RemoteException;
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+ + "ContentProviderClient#bulkInsert(android.net.Uri, android.content.ContentValues[])"
+ + "} instead")
+ public default int bulkInsert(String callingPkg, Uri url, ContentValues[] initialValues)
+ throws RemoteException {
+ return bulkInsert(callingPkg, null, url, initialValues);
+ }
+ public int bulkInsert(String callingPkg, String featureId, Uri url,
+ ContentValues[] initialValues) throws RemoteException;
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+ + "ContentProviderClient#delete(android.net.Uri, java.lang.String, java.lang"
+ + ".String[])} instead")
+ public default int delete(String callingPkg, Uri url, String selection, String[] selectionArgs)
+ throws RemoteException {
+ return delete(callingPkg, null, url, selection, selectionArgs);
+ }
+ public int delete(String callingPkg, String featureId, Uri url, String selection,
+ String[] selectionArgs) throws RemoteException;
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+ + "ContentProviderClient#update(android.net.Uri, android.content.ContentValues, java"
+ + ".lang.String, java.lang.String[])} instead")
+ public default int update(String callingPkg, Uri url, ContentValues values, String selection,
+ String[] selectionArgs) throws RemoteException {
+ return update(callingPkg, null, url, values, selection, selectionArgs);
+ }
+ public int update(String callingPkg, String featureId, Uri url, ContentValues values,
+ String selection, String[] selectionArgs) throws RemoteException;
- public ContentProviderResult[] applyBatch(String callingPkg, String authority,
- ArrayList<ContentProviderOperation> operations)
+ public ParcelFileDescriptor openFile(String callingPkg, @Nullable String featureId, Uri url,
+ String mode, ICancellationSignal signal, IBinder callerToken)
+ throws RemoteException, FileNotFoundException;
+
+ public AssetFileDescriptor openAssetFile(String callingPkg, @Nullable String featureId,
+ Uri url, String mode, ICancellationSignal signal)
+ throws RemoteException, FileNotFoundException;
+
+ public ContentProviderResult[] applyBatch(String callingPkg, @Nullable String featureId,
+ String authority, ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException;
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+ + "ContentProviderClient#call(java.lang.String, java.lang.String, android.os.Bundle)} "
+ + "instead")
public default Bundle call(String callingPkg, String method,
@Nullable String arg, @Nullable Bundle extras) throws RemoteException {
- return call(callingPkg, "unknown", method, arg, extras);
+ return call(callingPkg, null, "unknown", method, arg, extras);
}
- public Bundle call(String callingPkg, String authority, String method,
- @Nullable String arg, @Nullable Bundle extras) throws RemoteException;
+ public Bundle call(String callingPkg, @Nullable String featureId, String authority,
+ String method, @Nullable String arg, @Nullable Bundle extras) throws RemoteException;
- public int checkUriPermission(String callingPkg, Uri uri, int uid, int modeFlags)
- throws RemoteException;
+ public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri uri, int uid,
+ int modeFlags) throws RemoteException;
public ICancellationSignal createCancellationSignal() throws RemoteException;
- public Uri canonicalize(String callingPkg, Uri uri) throws RemoteException;
- public Uri uncanonicalize(String callingPkg, Uri uri) throws RemoteException;
+ public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri)
+ throws RemoteException;
- public boolean refresh(String callingPkg, Uri url, @Nullable Bundle args,
- ICancellationSignal cancellationSignal) throws RemoteException;
+ public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri)
+ throws RemoteException;
+
+ public boolean refresh(String callingPkg, @Nullable String featureId, Uri url,
+ @Nullable Bundle args, ICancellationSignal cancellationSignal) throws RemoteException;
// Data interchange.
public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException;
- public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri url, String mimeType,
- Bundle opts, ICancellationSignal signal) throws RemoteException, FileNotFoundException;
+
+ public AssetFileDescriptor openTypedAssetFile(String callingPkg, @Nullable String featureId,
+ Uri url, String mimeType, Bundle opts, ICancellationSignal signal)
+ throws RemoteException, FileNotFoundException;
/* IPC constants */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 71b94ed..b7a3c8f 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1314,7 +1314,8 @@
}
/**
- * Returns whether switching users is currently allowed.
+ * Returns whether switching users is currently allowed for the user this process is running
+ * under.
* <p>
* Switching users is not allowed in the following cases:
* <li>the user is in a phone call</li>
@@ -1329,10 +1330,24 @@
android.Manifest.permission.MANAGE_USERS,
android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
public @UserSwitchabilityResult int getUserSwitchability() {
- final boolean allowUserSwitchingWhenSystemUserLocked = Settings.Global.getInt(
- mContext.getContentResolver(),
- Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED, 0) != 0;
- final boolean systemUserUnlocked = isUserUnlocked(UserHandle.SYSTEM);
+ return getUserSwitchability(Process.myUserHandle());
+ }
+
+ /**
+ * Returns whether switching users is currently allowed for the provided user.
+ * <p>
+ * Switching users is not allowed in the following cases:
+ * <li>the user is in a phone call</li>
+ * <li>{@link #DISALLOW_USER_SWITCH} is set</li>
+ * <li>system user hasn't been unlocked yet</li>
+ *
+ * @return A {@link UserSwitchabilityResult} flag indicating if the user is switchable.
+ * @hide
+ */
+ @RequiresPermission(allOf = {Manifest.permission.READ_PHONE_STATE,
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+ public @UserSwitchabilityResult int getUserSwitchability(UserHandle userHandle) {
final TelephonyManager tm =
(TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
@@ -1340,12 +1355,22 @@
if (tm.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
flags |= SWITCHABILITY_STATUS_USER_IN_CALL;
}
- if (hasUserRestriction(DISALLOW_USER_SWITCH)) {
+ if (hasUserRestriction(DISALLOW_USER_SWITCH, userHandle)) {
flags |= SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED;
}
- if (!allowUserSwitchingWhenSystemUserLocked && !systemUserUnlocked) {
- flags |= SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED;
+
+ // System User is always unlocked in Headless System User Mode, so ignore this flag
+ if (!isHeadlessSystemUserMode()) {
+ final boolean allowUserSwitchingWhenSystemUserLocked = Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED, 0) != 0;
+ final boolean systemUserUnlocked = isUserUnlocked(UserHandle.SYSTEM);
+
+ if (!allowUserSwitchingWhenSystemUserLocked && !systemUserUnlocked) {
+ flags |= SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED;
+ }
}
+
return flags;
}
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 8b8afd5..bb8b041 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -586,9 +586,8 @@
@RequiresPermission(WRITE_DEVICE_CONFIG)
public static boolean setProperty(@NonNull String namespace, @NonNull String name,
@Nullable String value, boolean makeDefault) {
- String compositeName = createCompositeName(namespace, name);
ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
- return Settings.Config.putString(contentResolver, compositeName, value, makeDefault);
+ return Settings.Config.putString(contentResolver, namespace, name, value, makeDefault);
}
/**
@@ -672,12 +671,6 @@
}
}
- private static String createCompositeName(@NonNull String namespace, @NonNull String name) {
- Preconditions.checkNotNull(namespace);
- Preconditions.checkNotNull(name);
- return namespace + "/" + name;
- }
-
private static Uri createNamespaceUri(@NonNull String namespace) {
Preconditions.checkNotNull(namespace);
return CONTENT_URI.buildUpon().appendPath(namespace).build();
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 2143a0d..a80153d 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -1081,7 +1081,8 @@
// signed with platform signature can hold MANAGE_DOCUMENTS, we are going to check for
// MANAGE_DOCUMENTS or associated URI permission here instead
final Uri rootUri = extras.getParcelable(DocumentsContract.EXTRA_URI);
- enforceWritePermissionInner(rootUri, getCallingPackage(), null);
+ enforceWritePermissionInner(rootUri, getCallingPackage(), getCallingFeatureId(),
+ null);
final String rootId = DocumentsContract.getRootId(rootUri);
ejectRoot(rootId);
@@ -1102,7 +1103,8 @@
enforceTree(documentUri);
if (METHOD_IS_CHILD_DOCUMENT.equals(method)) {
- enforceReadPermissionInner(documentUri, getCallingPackage(), null);
+ enforceReadPermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
+ null);
final Uri childUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
final String childAuthority = childUri.getAuthority();
@@ -1114,7 +1116,8 @@
&& isChildDocument(documentId, childId));
} else if (METHOD_CREATE_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri, getCallingPackage(), null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
+ null);
final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE);
final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
@@ -1128,7 +1131,8 @@
out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
} else if (METHOD_CREATE_WEB_LINK_INTENT.equals(method)) {
- enforceWritePermissionInner(documentUri, getCallingPackage(), null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
+ null);
final Bundle options = extras.getBundle(DocumentsContract.EXTRA_OPTIONS);
final IntentSender intentSender = createWebLinkIntent(documentId, options);
@@ -1136,7 +1140,8 @@
out.putParcelable(DocumentsContract.EXTRA_RESULT, intentSender);
} else if (METHOD_RENAME_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri, getCallingPackage(), null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
+ null);
final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
final String newDocumentId = renameDocument(documentId, displayName);
@@ -1160,7 +1165,8 @@
}
} else if (METHOD_DELETE_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri, getCallingPackage(), null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
+ null);
deleteDocument(documentId);
// Document no longer exists, clean up any grants.
@@ -1170,8 +1176,10 @@
final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
final String targetId = DocumentsContract.getDocumentId(targetUri);
- enforceReadPermissionInner(documentUri, getCallingPackage(), null);
- enforceWritePermissionInner(targetUri, getCallingPackage(), null);
+ enforceReadPermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
+ null);
+ enforceWritePermissionInner(targetUri, getCallingPackage(), getCallingFeatureId(),
+ null);
final String newDocumentId = copyDocument(documentId, targetId);
@@ -1194,9 +1202,12 @@
final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
final String targetId = DocumentsContract.getDocumentId(targetUri);
- enforceWritePermissionInner(documentUri, getCallingPackage(), null);
- enforceReadPermissionInner(parentSourceUri, getCallingPackage(), null);
- enforceWritePermissionInner(targetUri, getCallingPackage(), null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
+ null);
+ enforceReadPermissionInner(parentSourceUri, getCallingPackage(), getCallingFeatureId(),
+ null);
+ enforceWritePermissionInner(targetUri, getCallingPackage(), getCallingFeatureId(),
+ null);
final String newDocumentId = moveDocument(documentId, parentSourceId, targetId);
@@ -1217,8 +1228,10 @@
final Uri parentSourceUri = extras.getParcelable(DocumentsContract.EXTRA_PARENT_URI);
final String parentSourceId = DocumentsContract.getDocumentId(parentSourceUri);
- enforceReadPermissionInner(parentSourceUri, getCallingPackage(), null);
- enforceWritePermissionInner(documentUri, getCallingPackage(), null);
+ enforceReadPermissionInner(parentSourceUri, getCallingPackage(), getCallingFeatureId(),
+ null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
+ null);
removeDocument(documentId, parentSourceId);
// It's responsibility of the provider to revoke any grants, as the document may be
@@ -1227,7 +1240,8 @@
final boolean isTreeUri = isTreeUri(documentUri);
if (isTreeUri) {
- enforceReadPermissionInner(documentUri, getCallingPackage(), null);
+ enforceReadPermissionInner(documentUri, getCallingPackage(), getCallingFeatureId(),
+ null);
} else {
getContext().enforceCallingPermission(Manifest.permission.MANAGE_DOCUMENTS, null);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b5580d4..44ab09e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -78,6 +78,7 @@
import android.view.Display;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
import com.android.internal.widget.ILockSettings;
import java.io.IOException;
@@ -2306,8 +2307,8 @@
arg.putBoolean(CALL_METHOD_MAKE_DEFAULT_KEY, true);
}
IContentProvider cp = mProviderHolder.getProvider(cr);
- cp.call(cr.getPackageName(), mProviderHolder.mUri.getAuthority(),
- mCallSetCommand, name, arg);
+ cp.call(cr.getPackageName(), cr.getFeatureId(),
+ mProviderHolder.mUri.getAuthority(), mCallSetCommand, name, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
return false;
@@ -2380,14 +2381,15 @@
if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
final long token = Binder.clearCallingIdentity();
try {
- b = cp.call(cr.getPackageName(), mProviderHolder.mUri.getAuthority(),
- mCallGetCommand, name, args);
+ b = cp.call(cr.getPackageName(), cr.getFeatureId(),
+ mProviderHolder.mUri.getAuthority(), mCallGetCommand, name,
+ args);
} finally {
Binder.restoreCallingIdentity(token);
}
} else {
- b = cp.call(cr.getPackageName(), mProviderHolder.mUri.getAuthority(),
- mCallGetCommand, name, args);
+ b = cp.call(cr.getPackageName(), cr.getFeatureId(),
+ mProviderHolder.mUri.getAuthority(), mCallGetCommand, name, args);
}
if (b != null) {
String value = b.getString(Settings.NameValueTable.VALUE);
@@ -2455,14 +2457,14 @@
if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
final long token = Binder.clearCallingIdentity();
try {
- c = cp.query(cr.getPackageName(), mUri, SELECT_VALUE_PROJECTION, queryArgs,
- null);
+ c = cp.query(cr.getPackageName(), cr.getFeatureId(), mUri,
+ SELECT_VALUE_PROJECTION, queryArgs, null);
} finally {
Binder.restoreCallingIdentity(token);
}
} else {
- c = cp.query(cr.getPackageName(), mUri, SELECT_VALUE_PROJECTION, queryArgs,
- null);
+ c = cp.query(cr.getPackageName(), cr.getFeatureId(), mUri,
+ SELECT_VALUE_PROJECTION, queryArgs, null);
}
if (c == null) {
Log.w(TAG, "Can't get key " + name + " from " + mUri);
@@ -2506,7 +2508,7 @@
boolean prefixCached = false;
int size = mValues.size();
for (int i = 0; i < size; ++i) {
- if (mValues.keyAt(i).startsWith(prefix + "/")) {
+ if (mValues.keyAt(i).startsWith(prefix)) {
prefixCached = true;
break;
}
@@ -2521,7 +2523,7 @@
} else {
for (int i = 0; i < size; ++i) {
String key = mValues.keyAt(i);
- if (key.startsWith(prefix + "/")) {
+ if (key.startsWith(prefix)) {
keyValues.put(key, mValues.get(key));
}
}
@@ -2557,8 +2559,8 @@
}
// Fetch all flags for the namespace at once for caching purposes
- Bundle b = cp.call(cr.getPackageName(), mProviderHolder.mUri.getAuthority(),
- mCallListCommand, null, args);
+ Bundle b = cp.call(cr.getPackageName(), cr.getFeatureId(),
+ mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
if (b == null) {
// Invalid response, return an empty map
return keyValues;
@@ -5132,8 +5134,8 @@
}
arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode);
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getPackageName(), sProviderHolder.mUri.getAuthority(),
- CALL_METHOD_RESET_SECURE, null, arg);
+ cp.call(resolver.getPackageName(), resolver.getFeatureId(),
+ sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_SECURE, null, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e);
}
@@ -8268,6 +8270,12 @@
public static final String AWARE_LOCK_ENABLED = "aware_lock_enabled";
/**
+ * Controls whether tap gesture is enabled.
+ * @hide
+ */
+ public static final String TAP_GESTURE = "tap_gesture";
+
+ /**
* Keys we no longer back up under the current schema, but want to continue to
* process when restoring historical backup datasets.
*
@@ -12821,8 +12829,8 @@
}
arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode);
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getPackageName(), sProviderHolder.mUri.getAuthority(),
- CALL_METHOD_RESET_GLOBAL, null, arg);
+ cp.call(resolver.getPackageName(), resolver.getFeatureId(),
+ sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_GLOBAL, null, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e);
}
@@ -13678,10 +13686,10 @@
}
/**
- * Look up a list of names in the database, based on a common prefix.
+ * Look up a list of names in the database, within the specified namespace.
*
* @param resolver to access the database with
- * @param prefix to apply to all of the names which will be fetched
+ * @param namespace to which the names belong
* @param names to look up in the table
* @return a non null, but possibly empty, map from name to value for any of the names that
* were found during lookup.
@@ -13690,16 +13698,17 @@
*/
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
static Map<String, String> getStrings(@NonNull ContentResolver resolver,
- @NonNull String prefix, @NonNull List<String> names) {
- List<String> concatenatedNames = new ArrayList<>(names.size());
+ @NonNull String namespace, @NonNull List<String> names) {
+ List<String> compositeNames = new ArrayList<>(names.size());
for (String name : names) {
- concatenatedNames.add(prefix + "/" + name);
+ compositeNames.add(createCompositeName(namespace, name));
}
+ String prefix = createPrefix(namespace);
ArrayMap<String, String> rawKeyValues = sNameValueCache.getStringsForPrefix(
- resolver, prefix, concatenatedNames);
+ resolver, prefix, compositeNames);
int size = rawKeyValues.size();
- int substringLength = prefix.length() + 1;
+ int substringLength = prefix.length();
ArrayMap<String, String> keyValues = new ArrayMap<>(size);
for (int i = 0; i < size; ++i) {
keyValues.put(rawKeyValues.keyAt(i).substring(substringLength),
@@ -13709,7 +13718,7 @@
}
/**
- * Store a name/value pair into the database.
+ * Store a name/value pair into the database within the specified namespace.
* <p>
* Also the method takes an argument whether to make the value the default for this setting.
* If the system already specified a default value, then the one passed in here will
@@ -13717,6 +13726,7 @@
* </p>
*
* @param resolver to access the database with.
+ * @param namespace to store the name/value pair in.
* @param name to store.
* @param value to associate with the name.
* @param makeDefault whether to make the value the default one.
@@ -13727,10 +13737,10 @@
* @hide
*/
@RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
- static boolean putString(@NonNull ContentResolver resolver, @NonNull String name,
- @Nullable String value, boolean makeDefault) {
- return sNameValueCache.putStringForUser(resolver, name, value, null, makeDefault,
- resolver.getUserId());
+ static boolean putString(@NonNull ContentResolver resolver, @NonNull String namespace,
+ @NonNull String name, @Nullable String value, boolean makeDefault) {
+ return sNameValueCache.putStringForUser(resolver, createCompositeName(namespace, name),
+ value, null, makeDefault, resolver.getUserId());
}
/**
@@ -13741,29 +13751,40 @@
*
* @param resolver Handle to the content resolver.
* @param resetMode The reset mode to use.
- * @param prefix Optionally, to limit which which pairs are reset.
+ * @param namespace Optionally, to limit which which namespace is reset.
*
- * @see #putString(ContentResolver, String, String, boolean)
+ * @see #putString(ContentResolver, String, String, String, boolean)
*
* @hide
*/
@RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
static void resetToDefaults(@NonNull ContentResolver resolver, @ResetMode int resetMode,
- @Nullable String prefix) {
+ @Nullable String namespace) {
try {
Bundle arg = new Bundle();
arg.putInt(CALL_METHOD_USER_KEY, resolver.getUserId());
arg.putInt(CALL_METHOD_RESET_MODE_KEY, resetMode);
- if (prefix != null) {
- arg.putString(Settings.CALL_METHOD_PREFIX_KEY, prefix);
+ if (namespace != null) {
+ arg.putString(Settings.CALL_METHOD_PREFIX_KEY, createPrefix(namespace));
}
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getPackageName(), sProviderHolder.mUri.getAuthority(),
- CALL_METHOD_RESET_CONFIG, null, arg);
+ cp.call(resolver.getPackageName(), resolver.getFeatureId(),
+ sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_CONFIG, null, arg);
} catch (RemoteException e) {
Log.w(TAG, "Can't reset to defaults for " + DeviceConfig.CONTENT_URI, e);
}
}
+
+ private static String createCompositeName(@NonNull String namespace, @NonNull String name) {
+ Preconditions.checkNotNull(namespace);
+ Preconditions.checkNotNull(name);
+ return createPrefix(namespace) + name;
+ }
+
+ private static String createPrefix(@NonNull String namespace) {
+ Preconditions.checkNotNull(namespace);
+ return namespace + "/";
+ }
}
/**
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 2f0a4eb..59e9ed1 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -712,6 +712,8 @@
mSurfaceAlpha = 1f;
synchronized (mSurfaceControlLock) {
+ mSurface.release();
+
if (mRtHandlingPositionUpdates) {
mRtReleaseSurfaces = true;
return;
@@ -725,7 +727,6 @@
mTmpTransaction.remove(mBackgroundControl);
mBackgroundControl = null;
}
- mSurface.release();
mTmpTransaction.apply();
}
}
@@ -1198,7 +1199,6 @@
mRtTransaction.remove(mBackgroundControl);
mSurfaceControl = null;
mBackgroundControl = null;
- mSurface.release();
}
mRtHandlingPositionUpdates = false;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 20dc234..85bf19f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -439,7 +439,6 @@
boolean mReportNextDraw;
boolean mFullRedrawNeeded;
boolean mNewSurfaceNeeded;
- boolean mHasHadWindowFocus;
boolean mLastWasImTarget;
boolean mForceNextWindowRelayout;
CountDownLatch mWindowDrawCountDown;
@@ -2123,11 +2122,6 @@
endDragResizing();
destroyHardwareResources();
}
- if (viewVisibility == View.GONE) {
- // After making a window gone, we will count it as being
- // shown for the first time the next time it gets focus.
- mHasHadWindowFocus = false;
- }
}
// Non-visible windows can't hold accessibility focus.
@@ -2823,8 +2817,7 @@
if (imm != null && imTarget) {
imm.onPreWindowFocus(mView, hasWindowFocus);
imm.onPostWindowFocus(mView, mView.findFocus(),
- mWindowAttributes.softInputMode,
- !mHasHadWindowFocus, mWindowAttributes.flags);
+ mWindowAttributes.softInputMode, mWindowAttributes.flags);
}
}
}
@@ -3017,8 +3010,7 @@
if (hasWindowFocus) {
if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
imm.onPostWindowFocus(mView, mView.findFocus(),
- mWindowAttributes.softInputMode,
- !mHasHadWindowFocus, mWindowAttributes.flags);
+ mWindowAttributes.softInputMode, mWindowAttributes.flags);
}
// Clear the forward bit. We can just do this directly, since
// the window manager doesn't care about it.
@@ -3028,7 +3020,6 @@
.softInputMode &=
~WindowManager.LayoutParams
.SOFT_INPUT_IS_FORWARD_NAVIGATION;
- mHasHadWindowFocus = true;
// Refocusing a window that has a focused view should fire a
// focus event for the view since the global focused view changed.
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 6420d71..7ee53f2 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -358,7 +358,7 @@
boolean mActive = false;
/**
- * {@code true} if next {@link #onPostWindowFocus(View, View, int, boolean, int)} needs to
+ * {@code true} if next {@link #onPostWindowFocus(View, View, int, int)} needs to
* restart input.
*/
boolean mRestartOnNextWindowFocus = true;
@@ -1925,13 +1925,12 @@
* @hide
*/
public void onPostWindowFocus(View rootView, View focusedView,
- @SoftInputModeFlags int softInputMode, boolean first, int windowFlags) {
+ @SoftInputModeFlags int softInputMode, int windowFlags) {
boolean forceNewFocus = false;
synchronized (mH) {
if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
+ " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode)
- + " first=" + first + " flags=#"
- + Integer.toHexString(windowFlags));
+ + " flags=#" + Integer.toHexString(windowFlags));
if (mRestartOnNextWindowFocus) {
if (DEBUG) Log.v(TAG, "Restarting due to mRestartOnNextWindowFocus");
mRestartOnNextWindowFocus = false;
@@ -1947,9 +1946,6 @@
startInputFlags |= StartInputFlags.IS_TEXT_EDITOR;
}
}
- if (first) {
- startInputFlags |= StartInputFlags.FIRST_WINDOW_FOCUS_GAIN;
- }
if (checkFocusNoStartInput(forceNewFocus)) {
// We need to restart input on the current focus view. This
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 23d1237..3824c22 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -22,7 +22,10 @@
/**
* Manages the cookies used by an application's {@link WebView} instances.
- * Cookies are manipulated according to RFC2109.
+ * <p>
+ * CookieManager represents cookies as strings in the same format as the
+ * HTTP {@code Cookie} and {@code Set-Cookie} header fields (defined in
+ * <a href="https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03">RFC6265bis</a>).
*/
public abstract class CookieManager {
/**
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index 025e27b..382a254 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -168,9 +168,6 @@
if ((startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0) {
joiner.add("IS_TEXT_EDITOR");
}
- if ((startInputFlags & StartInputFlags.FIRST_WINDOW_FOCUS_GAIN) != 0) {
- joiner.add("FIRST_WINDOW_FOCUS_GAIN");
- }
if ((startInputFlags & StartInputFlags.INITIAL_CONNECTION) != 0) {
joiner.add("INITIAL_CONNECTION");
}
diff --git a/core/java/com/android/internal/inputmethod/StartInputFlags.java b/core/java/com/android/internal/inputmethod/StartInputFlags.java
index ba26d8d..5a8d2c2 100644
--- a/core/java/com/android/internal/inputmethod/StartInputFlags.java
+++ b/core/java/com/android/internal/inputmethod/StartInputFlags.java
@@ -30,7 +30,6 @@
@IntDef(flag = true, value = {
StartInputFlags.VIEW_HAS_FOCUS,
StartInputFlags.IS_TEXT_EDITOR,
- StartInputFlags.FIRST_WINDOW_FOCUS_GAIN,
StartInputFlags.INITIAL_CONNECTION})
public @interface StartInputFlags {
/**
@@ -44,13 +43,8 @@
int IS_TEXT_EDITOR = 2;
/**
- * This is the first time the window has gotten focus.
- */
- int FIRST_WINDOW_FOCUS_GAIN = 4;
-
- /**
* An internal concept to distinguish "start" and "restart". This concept doesn't look well
* documented hence we probably need to revisit this though.
*/
- int INITIAL_CONNECTION = 8;
+ int INITIAL_CONNECTION = 4;
}
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index c0e4e1f..3704ccd 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -59,8 +59,8 @@
return instance_;
}
-static bool IsMemfd(const std::string& path) {
- return android::base::StartsWith(path, "/memfd:");
+static bool IsArtMemfd(const std::string& path) {
+ return android::base::StartsWith(path, "/memfd:/boot-image-methods.art");
}
bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const {
@@ -91,8 +91,8 @@
return true;
}
- // In-memory files created through memfd_create are allowed.
- if (IsMemfd(path)) {
+ // the in-memory file created by ART through memfd_create is allowed.
+ if (IsArtMemfd(path)) {
return true;
}
@@ -321,8 +321,8 @@
return DetachSocket(fail_fn);
}
- // Children can directly use in-memory files created through memfd_create.
- if (IsMemfd(file_path)) {
+ // Children can directly use the in-memory file created by ART through memfd_create.
+ if (IsArtMemfd(file_path)) {
return;
}
@@ -545,6 +545,10 @@
}
void FileDescriptorTable::RestatInternal(std::set<int>& open_fds, fail_fn_t fail_fn) {
+ // ART creates a file through memfd for optimization purposes. We make sure
+ // there is at most one being created.
+ bool art_memfd_seen = false;
+
// Iterate through the list of file descriptors we've already recorded
// and check whether :
//
@@ -577,6 +581,14 @@
// FD.
}
+ if (IsArtMemfd(it->second->file_path)) {
+ if (art_memfd_seen) {
+ fail_fn("ART fd already seen: " + it->second->file_path);
+ } else {
+ art_memfd_seen = true;
+ }
+ }
+
++it;
// Finally, remove the FD from the set of open_fds. We do this last because
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index f14f289..e140ad2 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -82,22 +82,23 @@
final AssetFileDescriptor afd = new AssetFileDescriptor(
new ParcelFileDescriptor(mImage.getFileDescriptor()), 0, mSize, null);
- when(mProvider.openTypedAssetFile(any(), any(), any(), any(), any())).thenReturn(afd);
+ when(mProvider.openTypedAssetFile(any(), any(), any(), any(), any(), any())).thenReturn(
+ afd);
}
- private static void assertImageAspectAndContents(Bitmap bitmap) {
+ private static void assertImageAspectAndContents(int width, int height, Bitmap bitmap) {
// And correct aspect ratio
- final int before = (100 * 1280) / 960;
+ final int before = (100 * width) / height;
final int after = (100 * bitmap.getWidth()) / bitmap.getHeight();
assertEquals(before, after);
// And scaled correctly
final int halfX = bitmap.getWidth() / 2;
final int halfY = bitmap.getHeight() / 2;
- assertEquals(Color.BLUE, bitmap.getPixel(halfX - 10, halfY - 10));
- assertEquals(Color.RED, bitmap.getPixel(halfX + 10, halfY - 10));
- assertEquals(Color.RED, bitmap.getPixel(halfX - 10, halfY + 10));
- assertEquals(Color.RED, bitmap.getPixel(halfX + 10, halfY + 10));
+ assertEquals(Color.BLUE, bitmap.getPixel(halfX - 5, halfY - 5));
+ assertEquals(Color.RED, bitmap.getPixel(halfX + 5, halfY - 5));
+ assertEquals(Color.RED, bitmap.getPixel(halfX - 5, halfY + 5));
+ assertEquals(Color.RED, bitmap.getPixel(halfX + 5, halfY + 5));
}
@Test
@@ -112,7 +113,7 @@
assertEquals(1280, res.getWidth());
assertEquals(960, res.getHeight());
- assertImageAspectAndContents(res);
+ assertImageAspectAndContents(1280, 960, res);
}
@Test
@@ -127,7 +128,7 @@
assertTrue(res.getWidth() <= 640);
assertTrue(res.getHeight() <= 480);
- assertImageAspectAndContents(res);
+ assertImageAspectAndContents(1280, 960, res);
}
@Test
@@ -142,7 +143,7 @@
assertTrue(res.getWidth() <= 640);
assertTrue(res.getHeight() <= 480);
- assertImageAspectAndContents(res);
+ assertImageAspectAndContents(1280, 960, res);
}
@Test
@@ -157,7 +158,23 @@
assertEquals(32, res.getWidth());
assertEquals(24, res.getHeight());
- assertImageAspectAndContents(res);
+ assertImageAspectAndContents(32, 24, res);
+ }
+
+ @Test
+ public void testLoadThumbnail_Large() throws Exception {
+ // Test very large and extreme ratio image
+ initImage(1080, 30000);
+
+ Bitmap res = ContentResolver.loadThumbnail(mClient,
+ Uri.parse("content://com.example/"), new Size(1080, 540), null,
+ ImageDecoder.ALLOCATOR_SOFTWARE);
+
+ // Size should be much smaller
+ assertTrue(res.getWidth() <= 2160);
+ assertTrue(res.getHeight() <= 1080);
+
+ assertImageAspectAndContents(1080, 30000, res);
}
@Test
diff --git a/core/tests/coretests/src/android/graphics/TypefaceEqualsTest.java b/core/tests/coretests/src/android/graphics/TypefaceEqualsTest.java
new file mode 100644
index 0000000..6ae7eb7
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/TypefaceEqualsTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.graphics;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.content.res.AssetManager;
+import android.graphics.fonts.Font;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TypefaceEqualsTest {
+ @Test
+ public void testFontEqualWithLocale() throws IOException {
+ final AssetManager am =
+ InstrumentationRegistry.getInstrumentation().getContext().getAssets();
+
+ Font masterFont = new Font.Builder(am, "fonts/a3em.ttf").build();
+
+ Font jaFont = new Font.Builder(masterFont.getBuffer(), new File("fonts/a3em.ttf"), "ja")
+ .build();
+ Font jaFont2 = new Font.Builder(masterFont.getBuffer(), new File("fonts/a3em.ttf"), "ja")
+ .build();
+ Font koFont = new Font.Builder(masterFont.getBuffer(), new File("fonts/a3em.ttf"), "ko")
+ .build();
+
+ assertEquals(jaFont, jaFont2);
+ assertNotEquals(jaFont, koFont);
+ assertNotEquals(jaFont, masterFont);
+ }
+}
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index 1f2dfe0..0c83390 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -388,6 +388,13 @@
assertThat(properties.getKeyset()).containsExactly(KEY, KEY2);
assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE3);
assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2);
+
+ DeviceConfig.setProperty(NAMESPACE, KEY3, VALUE, false);
+ properties = DeviceConfig.getProperties(NAMESPACE);
+ assertThat(properties.getKeyset()).containsExactly(KEY, KEY2, KEY3);
+ assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE3);
+ assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2);
+ assertThat(properties.getString(KEY3, DEFAULT_VALUE)).isEqualTo(VALUE);
}
@Test
diff --git a/core/tests/coretests/src/android/provider/TestDocumentsProvider.java b/core/tests/coretests/src/android/provider/TestDocumentsProvider.java
index 1bd8ff6..5f640be 100644
--- a/core/tests/coretests/src/android/provider/TestDocumentsProvider.java
+++ b/core/tests/coretests/src/android/provider/TestDocumentsProvider.java
@@ -93,12 +93,14 @@
}
@Override
- protected int enforceReadPermissionInner(Uri uri, String callingPkg, IBinder callerToken) {
+ protected int enforceReadPermissionInner(Uri uri, String callingPkg,
+ @Nullable String callingFeatureId, IBinder callerToken) {
return AppOpsManager.MODE_ALLOWED;
}
@Override
- protected int enforceWritePermissionInner(Uri uri, String callingPkg, IBinder callerToken) {
+ protected int enforceWritePermissionInner(Uri uri, String callingPkg,
+ @Nullable String callingFeatureId, IBinder callerToken) {
return AppOpsManager.MODE_ALLOWED;
}
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index b3f6fdb..364e4ea 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -46,10 +46,12 @@
import android.os.Binder;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
import android.view.WindowManagerGlobal;
import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -70,6 +72,8 @@
*/
@RunWith(AndroidJUnit4.class)
@MediumTest
+@Presubmit
+@FlakyTest(bugId = 143153552)
public class ActivityThreadClientTest {
@Test
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index 552088f..ba96a06 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -519,12 +519,13 @@
}
Font f = (Font) o;
return mFontStyle.equals(f.mFontStyle) && f.mTtcIndex == mTtcIndex
- && Arrays.equals(f.mAxes, mAxes) && f.mBuffer.equals(mBuffer);
+ && Arrays.equals(f.mAxes, mAxes) && f.mBuffer.equals(mBuffer)
+ && Objects.equals(f.mLocaleList, mLocaleList);
}
@Override
public int hashCode() {
- return Objects.hash(mFontStyle, mTtcIndex, Arrays.hashCode(mAxes), mBuffer);
+ return Objects.hash(mFontStyle, mTtcIndex, Arrays.hashCode(mAxes), mBuffer, mLocaleList);
}
@Override
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index ec83a19..1eb089d 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -136,7 +136,6 @@
const Paint* paint) override;
virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) override;
- virtual bool drawTextAbsolutePos() const override { return true; }
virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index b98ffca..c138a32 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -95,18 +95,10 @@
void operator()(size_t start, size_t end) {
auto glyphFunc = [&](uint16_t* text, float* positions) {
- if (canvas->drawTextAbsolutePos()) {
- for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) {
- text[textIndex++] = layout.getGlyphId(i);
- positions[posIndex++] = x + layout.getX(i);
- positions[posIndex++] = y + layout.getY(i);
- }
- } else {
- for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) {
- text[textIndex++] = layout.getGlyphId(i);
- positions[posIndex++] = layout.getX(i);
- positions[posIndex++] = layout.getY(i);
- }
+ for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) {
+ text[textIndex++] = layout.getGlyphId(i);
+ positions[posIndex++] = x + layout.getX(i);
+ positions[posIndex++] = y + layout.getY(i);
}
};
@@ -166,9 +158,6 @@
minikin::MinikinRect bounds;
layout.getBounds(&bounds);
- if (!drawTextAbsolutePos()) {
- bounds.offset(x, y);
- }
// Set align to left for drawing, as we don't want individual
// glyphs centered or right-aligned; the offset above takes
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index b90a4a3..27dfed3 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -253,15 +253,6 @@
virtual void drawPicture(const SkPicture& picture) = 0;
/**
- * Specifies if the positions passed to ::drawText are absolute or relative
- * to the (x,y) value provided.
- *
- * If true the (x,y) values are ignored. Otherwise, those (x,y) values need
- * to be added to each glyph's position to get its absolute position.
- */
- virtual bool drawTextAbsolutePos() const = 0;
-
- /**
* Draws a VectorDrawable onto the canvas.
*/
virtual void drawVectorDrawable(VectorDrawableRoot* tree) = 0;
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index d3db9d8..87b3be9 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -230,7 +230,7 @@
public static final String METADATA_SETTINGS_FOOTER_STRING =
"com.android.settings.location.FOOTER_STRING";
- private static final long GET_CURRENT_LOCATION_TIMEOUT_MS = 30 * 1000;
+ private static final long GET_CURRENT_LOCATION_MAX_TIMEOUT_MS = 30 * 1000;
private final Context mContext;
@@ -630,7 +630,10 @@
@Nullable CancellationSignal cancellationSignal,
@NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Location> consumer) {
LocationRequest currentLocationRequest = new LocationRequest(locationRequest)
- .setNumUpdates(1).setExpireIn(GET_CURRENT_LOCATION_TIMEOUT_MS);
+ .setNumUpdates(1);
+ if (currentLocationRequest.getExpireIn() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) {
+ currentLocationRequest.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
+ }
GetCurrentLocationTransport listenerTransport = new GetCurrentLocationTransport(executor,
consumer);
@@ -684,6 +687,7 @@
LocationRequest request = LocationRequest.createFromDeprecatedProvider(
provider, 0, 0, true);
+ request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
requestLocationUpdates(request, listener, looper);
}
@@ -714,6 +718,7 @@
LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
criteria, 0, 0, true);
+ request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
requestLocationUpdates(request, listener, looper);
}
@@ -740,6 +745,7 @@
LocationRequest request = LocationRequest.createFromDeprecatedProvider(
provider, 0, 0, true);
+ request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
requestLocationUpdates(request, pendingIntent);
}
@@ -767,6 +773,7 @@
LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
criteria, 0, 0, true);
+ request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
requestLocationUpdates(request, pendingIntent);
}
@@ -1835,7 +1842,7 @@
}
try {
- return mGnssStatusListenerManager.addListener(listener, new Handler());
+ return mGnssStatusListenerManager.addListener(listener, Runnable::run);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2422,7 +2429,7 @@
mAlarmManager = alarmManager;
mAlarmManager.set(
ELAPSED_REALTIME,
- SystemClock.elapsedRealtime() + GET_CURRENT_LOCATION_TIMEOUT_MS,
+ SystemClock.elapsedRealtime() + GET_CURRENT_LOCATION_MAX_TIMEOUT_MS,
"GetCurrentLocation",
this,
null);
@@ -2694,9 +2701,9 @@
return mTtff;
}
- public boolean addListener(@NonNull GpsStatus.Listener listener, @NonNull Handler handler)
+ public boolean addListener(@NonNull GpsStatus.Listener listener, @NonNull Executor executor)
throws RemoteException {
- return addInternal(listener, handler);
+ return addInternal(listener, executor);
}
public boolean addListener(@NonNull OnNmeaMessageListener listener,
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 0f38f7f..3abd2c2 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -161,6 +161,7 @@
private boolean mExplicitFastestInterval = false;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private long mExpireAt = Long.MAX_VALUE; // no expiry
+ private long mExpireIn = Long.MAX_VALUE; // no expiry
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private int mNumUpdates = Integer.MAX_VALUE; // no expiry
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -268,6 +269,7 @@
mFastestInterval = src.mFastestInterval;
mExplicitFastestInterval = src.mExplicitFastestInterval;
mExpireAt = src.mExpireAt;
+ mExpireIn = src.mExpireIn;
mNumUpdates = src.mNumUpdates;
mSmallestDisplacement = src.mSmallestDisplacement;
mProvider = src.mProvider;
@@ -342,7 +344,7 @@
* @throws IllegalArgumentException if the interval is less than zero
*/
public @NonNull LocationRequest setInterval(long millis) {
- checkInterval(millis);
+ Preconditions.checkArgument(millis >= 0, "invalid interval: + millis");
mInterval = millis;
if (!mExplicitFastestInterval) {
mFastestInterval = (long) (mInterval / FASTEST_INTERVAL_FACTOR);
@@ -370,9 +372,7 @@
*
* @param enabled Enable or disable low power mode
* @return the same object, so that setters can be chained
- * @hide
*/
- @SystemApi
public @NonNull LocationRequest setLowPowerMode(boolean enabled) {
mLowPowerMode = enabled;
return this;
@@ -380,10 +380,7 @@
/**
* Returns true if low power mode is enabled.
- *
- * @hide
*/
- @SystemApi
public boolean isLowPowerMode() {
return mLowPowerMode;
}
@@ -431,93 +428,80 @@
* <p>An interval of 0 is allowed, but not recommended, since
* location updates may be extremely fast on future implementations.
*
- * <p>If {@link #setFastestInterval} is set slower than {@link #setInterval},
+ * <p>If the fastest interval set is slower than {@link #setInterval},
* then your effective fastest interval is {@link #setInterval}.
*
- * @param millis fastest interval for updates in milliseconds, exact
+ * @param millis fastest interval for updates in milliseconds
* @return the same object, so that setters can be chained
* @throws IllegalArgumentException if the interval is less than zero
*/
public @NonNull LocationRequest setFastestInterval(long millis) {
- checkInterval(millis);
+ Preconditions.checkArgument(millis >= 0, "invalid interval: + millis");
mExplicitFastestInterval = true;
mFastestInterval = millis;
return this;
}
/**
- * Get the fastest interval of this request, in milliseconds.
+ * Get the fastest interval of this request in milliseconds. The system will never provide
+ * location updates faster than the minimum of the fastest interval and {@link #getInterval}.
*
- * <p>The system will never provide location updates faster
- * than the minimum of {@link #getFastestInterval} and
- * {@link #getInterval}.
- *
- * @return fastest interval in milliseconds, exact
+ * @return fastest interval in milliseconds
*/
public long getFastestInterval() {
return mFastestInterval;
}
/**
- * Set the duration of this request, in milliseconds.
+ * Set the expiration time of this request in milliseconds of realtime since boot. Values in the
+ * past are allowed, but indicate that the request has already expired. The location manager
+ * will automatically stop updates after the request expires.
*
- * <p>The duration begins immediately (and not when the request
- * is passed to the location manager), so call this method again
- * if the request is re-used at a later time.
+ * @param millis expiration time of request in milliseconds since boot
+ * @return the same object, so that setters can be chained
+ * @see SystemClock#elapsedRealtime()
+ * @deprecated Prefer {@link #setExpireIn(long)}.
+ */
+ @Deprecated
+ public @NonNull LocationRequest setExpireAt(long millis) {
+ mExpireAt = Math.max(millis, 0);
+ return this;
+ }
+
+ /**
+ * Get the request expiration time in milliseconds of realtime since boot.
*
- * <p>The location manager will automatically stop updates after
- * the request expires.
- *
- * <p>The duration includes suspend time. Values less than 0
- * are allowed, but indicate that the request has already expired.
+ * @return request expiration time in milliseconds since boot
+ * @see SystemClock#elapsedRealtime()
+ * @deprecated Prefer {@link #getExpireIn()}.
+ */
+ @Deprecated
+ public long getExpireAt() {
+ return mExpireAt;
+ }
+
+ /**
+ * Set the duration of this request in milliseconds of realtime. Values less than 0 are allowed,
+ * but indicate that the request has already expired. The location manager will automatically
+ * stop updates after the request expires.
*
* @param millis duration of request in milliseconds
* @return the same object, so that setters can be chained
+ * @see SystemClock#elapsedRealtime()
*/
public @NonNull LocationRequest setExpireIn(long millis) {
- long elapsedRealtime = SystemClock.elapsedRealtime();
-
- // Check for > Long.MAX_VALUE overflow (elapsedRealtime > 0):
- if (millis > Long.MAX_VALUE - elapsedRealtime) {
- mExpireAt = Long.MAX_VALUE;
- } else {
- mExpireAt = millis + elapsedRealtime;
- }
-
- if (mExpireAt < 0) mExpireAt = 0;
+ mExpireIn = millis;
return this;
}
/**
- * Set the request expiration time, in millisecond since boot.
+ * Get the request expiration duration in milliseconds of realtime.
*
- * <p>This expiration time uses the same time base as {@link SystemClock#elapsedRealtime}.
- *
- * <p>The location manager will automatically stop updates after
- * the request expires.
- *
- * <p>The duration includes suspend time. Values before {@link SystemClock#elapsedRealtime}
- * are allowed, but indicate that the request has already expired.
- *
- * @param millis expiration time of request, in milliseconds since boot including suspend
- * @return the same object, so that setters can be chained
+ * @return request expiration duration in milliseconds
+ * @see SystemClock#elapsedRealtime()
*/
- public @NonNull LocationRequest setExpireAt(long millis) {
- mExpireAt = millis;
- if (mExpireAt < 0) mExpireAt = 0;
- return this;
- }
-
- /**
- * Get the request expiration time, in milliseconds since boot.
- *
- * <p>This value can be compared to {@link SystemClock#elapsedRealtime} to determine
- * the time until expiration.
- *
- * @return expiration time of request, in milliseconds since boot including suspend
- */
- public long getExpireAt() {
- return mExpireAt;
+ public long getExpireIn() {
+ return mExpireIn;
}
/**
@@ -638,13 +622,6 @@
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- private static void checkInterval(long millis) {
- if (millis < 0) {
- throw new IllegalArgumentException("invalid interval: " + millis);
- }
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private static void checkQuality(int quality) {
switch (quality) {
case ACCURACY_FINE:
@@ -682,6 +659,7 @@
request.setFastestInterval(in.readLong());
request.setInterval(in.readLong());
request.setExpireAt(in.readLong());
+ request.setExpireIn(in.readLong());
request.setNumUpdates(in.readInt());
request.setSmallestDisplacement(in.readFloat());
request.setHideFromAppOps(in.readInt() != 0);
@@ -711,6 +689,7 @@
parcel.writeLong(mFastestInterval);
parcel.writeLong(mInterval);
parcel.writeLong(mExpireAt);
+ parcel.writeLong(mExpireIn);
parcel.writeInt(mNumUpdates);
parcel.writeFloat(mSmallestDisplacement);
parcel.writeInt(mHideFromAppOps ? 1 : 0);
@@ -753,9 +732,11 @@
s.append(" fastest=");
TimeUtils.formatDuration(mFastestInterval, s);
if (mExpireAt != Long.MAX_VALUE) {
- long expireIn = mExpireAt - SystemClock.elapsedRealtime();
+ s.append(" expireAt=").append(TimeUtils.formatUptime(mExpireAt));
+ }
+ if (mExpireIn != Long.MAX_VALUE) {
s.append(" expireIn=");
- TimeUtils.formatDuration(expireIn, s);
+ TimeUtils.formatDuration(mExpireIn, s);
}
if (mNumUpdates != Integer.MAX_VALUE) {
s.append(" num=").append(mNumUpdates);
diff --git a/media/java/android/media/Utils.java b/media/java/android/media/Utils.java
index 5b62f16..d942bb6 100644
--- a/media/java/android/media/Utils.java
+++ b/media/java/android/media/Utils.java
@@ -16,8 +16,8 @@
package android.media;
-import android.content.Context;
import android.content.ContentResolver;
+import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
@@ -206,12 +206,13 @@
}
static Size parseSize(Object o, Size fallback) {
+ if (o == null) {
+ return fallback;
+ }
try {
return Size.parseSize((String) o);
} catch (ClassCastException e) {
} catch (NumberFormatException e) {
- } catch (NullPointerException e) {
- return fallback;
}
Log.w(TAG, "could not parse size '" + o + "'");
return fallback;
@@ -226,14 +227,15 @@
return Integer.parseInt(s);
} catch (ClassCastException e) {
} catch (NumberFormatException e) {
- } catch (NullPointerException e) {
- return fallback;
}
Log.w(TAG, "could not parse integer '" + o + "'");
return fallback;
}
static Range<Integer> parseIntRange(Object o, Range<Integer> fallback) {
+ if (o == null) {
+ return fallback;
+ }
try {
String s = (String)o;
int ix = s.indexOf('-');
@@ -246,8 +248,6 @@
return Range.create(value, value);
} catch (ClassCastException e) {
} catch (NumberFormatException e) {
- } catch (NullPointerException e) {
- return fallback;
} catch (IllegalArgumentException e) {
}
Log.w(TAG, "could not parse integer range '" + o + "'");
@@ -255,6 +255,9 @@
}
static Range<Long> parseLongRange(Object o, Range<Long> fallback) {
+ if (o == null) {
+ return fallback;
+ }
try {
String s = (String)o;
int ix = s.indexOf('-');
@@ -267,8 +270,6 @@
return Range.create(value, value);
} catch (ClassCastException e) {
} catch (NumberFormatException e) {
- } catch (NullPointerException e) {
- return fallback;
} catch (IllegalArgumentException e) {
}
Log.w(TAG, "could not parse long range '" + o + "'");
@@ -276,6 +277,9 @@
}
static Range<Rational> parseRationalRange(Object o, Range<Rational> fallback) {
+ if (o == null) {
+ return fallback;
+ }
try {
String s = (String)o;
int ix = s.indexOf('-');
@@ -288,8 +292,6 @@
return Range.create(value, value);
} catch (ClassCastException e) {
} catch (NumberFormatException e) {
- } catch (NullPointerException e) {
- return fallback;
} catch (IllegalArgumentException e) {
}
Log.w(TAG, "could not parse rational range '" + o + "'");
@@ -297,6 +299,9 @@
}
static Pair<Size, Size> parseSizeRange(Object o) {
+ if (o == null) {
+ return null;
+ }
try {
String s = (String)o;
int ix = s.indexOf('-');
@@ -309,8 +314,6 @@
return Pair.create(value, value);
} catch (ClassCastException e) {
} catch (NumberFormatException e) {
- } catch (NullPointerException e) {
- return null;
} catch (IllegalArgumentException e) {
}
Log.w(TAG, "could not parse size range '" + o + "'");
diff --git a/media/java/android/media/tv/OWNER b/media/java/android/media/tv/OWNER
new file mode 100644
index 0000000..64c0bb5
--- /dev/null
+++ b/media/java/android/media/tv/OWNER
@@ -0,0 +1,5 @@
+amyjojo@google.com
+nchalko@google.com
+shubang@google.com
+quxiangfang@google.com
+
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 0228dc9..2257747 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -16,6 +16,13 @@
package android.media.tv.tuner;
+import android.annotation.IntDef;
+import android.hardware.tv.tuner.V1_0.Constants;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
/**
* Tuner is used to interact with tuner devices.
*
@@ -25,11 +32,41 @@
private static final String TAG = "MediaTvTuner";
private static final boolean DEBUG = false;
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({FRONTEND_TYPE_UNDEFINED, FRONTEND_TYPE_ANALOG, FRONTEND_TYPE_ATSC, FRONTEND_TYPE_ATSC3,
+ FRONTEND_TYPE_DVBC, FRONTEND_TYPE_DVBS, FRONTEND_TYPE_DVBT, FRONTEND_TYPE_ISDBS,
+ FRONTEND_TYPE_ISDBS3, FRONTEND_TYPE_ISDBT})
+ public @interface FrontendType {}
+
+ public static final int FRONTEND_TYPE_UNDEFINED = Constants.FrontendType.UNDEFINED;
+ public static final int FRONTEND_TYPE_ANALOG = Constants.FrontendType.ANALOG;
+ public static final int FRONTEND_TYPE_ATSC = Constants.FrontendType.ATSC;
+ public static final int FRONTEND_TYPE_ATSC3 = Constants.FrontendType.ATSC3;
+ public static final int FRONTEND_TYPE_DVBC = Constants.FrontendType.DVBC;
+ public static final int FRONTEND_TYPE_DVBS = Constants.FrontendType.DVBS;
+ public static final int FRONTEND_TYPE_DVBT = Constants.FrontendType.DVBT;
+ public static final int FRONTEND_TYPE_ISDBS = Constants.FrontendType.ISDBS;
+ public static final int FRONTEND_TYPE_ISDBS3 = Constants.FrontendType.ISDBS3;
+ public static final int FRONTEND_TYPE_ISDBT = Constants.FrontendType.ISDBT;
+
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({FRONTEND_EVENT_TYPE_LOCKED, FRONTEND_EVENT_TYPE_NO_SIGNAL,
+ FRONTEND_EVENT_TYPE_LOST_LOCK})
+ public @interface FrontendEventType {}
+
+ public static final int FRONTEND_EVENT_TYPE_LOCKED = Constants.FrontendEventType.LOCKED;
+ public static final int FRONTEND_EVENT_TYPE_NO_SIGNAL = Constants.FrontendEventType.NO_SIGNAL;
+ public static final int FRONTEND_EVENT_TYPE_LOST_LOCK = Constants.FrontendEventType.LOST_LOCK;
+
static {
System.loadLibrary("media_tv_tuner");
nativeInit();
}
+ private FrontendCallback mFrontendCallback;
+ private List<Integer> mFrontendIds;
+
public Tuner() {
nativeSetup();
}
@@ -48,4 +85,48 @@
* Native setup.
*/
private native void nativeSetup();
+
+ /**
+ * Native method to get all frontend IDs.
+ */
+ private native List<Integer> nativeGetFrontendIds();
+
+ /**
+ * Native method to open frontend of the given ID.
+ */
+ private native Frontend nativeOpenFrontendById(int id);
+
+
+ /**
+ * Frontend Callback.
+ */
+ public interface FrontendCallback {
+
+ /**
+ * Invoked when there is a frontend event.
+ */
+ void onEvent(int frontendEventType);
+ }
+
+ protected static class Frontend {
+ int mId;
+ private Frontend(int id) {
+ mId = id;
+ }
+ }
+
+ private List<Integer> getFrontendIds() {
+ mFrontendIds = nativeGetFrontendIds();
+ return mFrontendIds;
+ }
+
+ private Frontend openFrontendById(int id) {
+ if (mFrontendIds == null) {
+ getFrontendIds();
+ }
+ if (!mFrontendIds.contains(id)) {
+ return null;
+ }
+ return nativeOpenFrontendById(id);
+ }
}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index a596d89..3742f97 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -132,6 +132,7 @@
shared_libs: [
"android.hardware.tv.tuner@1.0",
"libandroid_runtime",
+ "libhidlbase",
"liblog",
"libutils",
],
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index d499eee..ba133fc 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -25,10 +25,13 @@
#pragma GCC diagnostic ignored "-Wunused-function"
+using ::android::hardware::hidl_vec;
using ::android::hardware::tv::tuner::V1_0::ITuner;
+using ::android::hardware::tv::tuner::V1_0::Result;
struct fields_t {
jfieldID context;
+ jmethodID frontendInitID;
};
static fields_t gFields;
@@ -69,6 +72,50 @@
return mTuner;
}
+jobject JTuner::getFrontendIds() {
+ ALOGD("JTuner::getFrontendIds()");
+ hidl_vec<FrontendId> feIds;
+ mTuner->getFrontendIds([&](Result, const hidl_vec<FrontendId>& frontendIds) {
+ feIds = frontendIds;
+ });
+ if (feIds.size() == 0) {
+ ALOGW("Frontend isn't available");
+ return NULL;
+ }
+
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jclass arrayListClazz = env->FindClass("java/util/ArrayList");
+ jmethodID arrayListAdd = env->GetMethodID(arrayListClazz, "add", "(Ljava/lang/Object;)Z");
+ jobject obj = env->NewObject(arrayListClazz, env->GetMethodID(arrayListClazz, "<init>", "()V"));
+
+ jclass integerClazz = env->FindClass("java/lang/Integer");
+ jmethodID intInit = env->GetMethodID(integerClazz, "<init>", "(I)V");
+
+ for (int i=0; i < feIds.size(); i++) {
+ jobject idObj = env->NewObject(integerClazz, intInit, feIds[i]);
+ env->CallBooleanMethod(obj, arrayListAdd, idObj);
+ }
+ return obj;
+}
+
+jobject JTuner::openFrontendById(int id) {
+ mTuner->openFrontendById(id, [&](Result, const sp<IFrontend>& frontend) {
+ mFe = frontend;
+ });
+ if (mFe == nullptr) {
+ ALOGE("Failed to open frontend");
+ return NULL;
+ }
+
+ jint jId = (jint) id;
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ // TODO: add more fields to frontend
+ return env->NewObject(
+ env->FindClass("android/media/tv/tuner/Tuner$Frontend"),
+ gFields.frontendInitID,
+ (jint) jId);
+}
+
} // namespace android
////////////////////////////////////////////////////////////////////////////////
@@ -99,6 +146,9 @@
gFields.context = env->GetFieldID(clazz, "mNativeContext", "J");
CHECK(gFields.context != NULL);
+
+ jclass frontendClazz = env->FindClass("android/media/tv/tuner/Tuner$Frontend");
+ gFields.frontendInitID = env->GetMethodID(frontendClazz, "<init>", "(I)V");
}
static void android_media_tv_Tuner_native_setup(JNIEnv *env, jobject thiz) {
@@ -106,9 +156,23 @@
setTuner(env,thiz, tuner);
}
+static jobject android_media_tv_Tuner_get_frontend_ids(JNIEnv *env, jobject thiz) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->getFrontendIds();
+}
+
+static jobject android_media_tv_Tuner_open_frontend_by_id(JNIEnv *env, jobject thiz, jint id) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->openFrontendById(id);
+}
+
static const JNINativeMethod gMethods[] = {
{ "nativeInit", "()V", (void *)android_media_tv_Tuner_native_init },
{ "nativeSetup", "()V", (void *)android_media_tv_Tuner_native_setup },
+ { "nativeGetFrontendIds", "()Ljava/util/List;",
+ (void *)android_media_tv_Tuner_get_frontend_ids },
+ { "nativeOpenFrontendById", "(I)Landroid/media/tv/tuner/Tuner$Frontend;",
+ (void *)android_media_tv_Tuner_open_frontend_by_id },
};
static int register_android_media_tv_Tuner(JNIEnv *env) {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index e7d5924..1425542 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -22,6 +22,8 @@
#include "jni.h"
+using ::android::hardware::tv::tuner::V1_0::FrontendId;
+using ::android::hardware::tv::tuner::V1_0::IFrontend;
using ::android::hardware::tv::tuner::V1_0::ITuner;
namespace android {
@@ -29,6 +31,8 @@
struct JTuner : public RefBase {
JTuner(JNIEnv *env, jobject thiz);
sp<ITuner> getTunerService();
+ jobject getFrontendIds();
+ jobject openFrontendById(int id);
protected:
virtual ~JTuner();
@@ -36,6 +40,7 @@
jclass mClass;
jweak mObject;
static sp<ITuner> mTuner;
+ sp<IFrontend> mFe;
};
} // namespace android
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
index 74bf1a2..de353bf 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
@@ -42,6 +42,7 @@
public class MediaInserterTest extends InstrumentationTestCase {
+ private static final String TEST_FEATURE_ID = "testFeature";
private MediaInserter mMediaInserter;
private static final int TEST_BUFFER_SIZE = 10;
private @Mock IContentProvider mMockProvider;
@@ -86,7 +87,8 @@
MockitoAnnotations.initMocks(this);
final ContentProviderClient client = new ContentProviderClient(
- getInstrumentation().getContext().getContentResolver(), mMockProvider, true);
+ getInstrumentation().getContext().createFeatureContext(TEST_FEATURE_ID)
+ .getContentResolver(), mMockProvider, true);
mMediaInserter = new MediaInserter(client, TEST_BUFFER_SIZE);
mPackageName = getInstrumentation().getContext().getPackageName();
mFilesCounter = 0;
@@ -142,31 +144,36 @@
fillBuffer(sVideoUri, TEST_BUFFER_SIZE - 2);
fillBuffer(sImagesUri, TEST_BUFFER_SIZE - 1);
- verify(mMockProvider, never()).bulkInsert(eq(mPackageName), any(), any());
+ verify(mMockProvider, never()).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ any());
}
@SmallTest
public void testInsertContentsEqualToBufferSize() throws Exception {
- when(mMockProvider.bulkInsert(eq(mPackageName), any(), any())).thenReturn(1);
+ when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ any())).thenReturn(1);
fillBuffer(sFilesUri, TEST_BUFFER_SIZE);
fillBuffer(sAudioUri, TEST_BUFFER_SIZE);
fillBuffer(sVideoUri, TEST_BUFFER_SIZE);
fillBuffer(sImagesUri, TEST_BUFFER_SIZE);
- verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), any(), any());
+ verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ any());
}
@SmallTest
public void testInsertContentsMoreThanBufferSize() throws Exception {
- when(mMockProvider.bulkInsert(eq(mPackageName), any(), any())).thenReturn(1);
+ when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ any())).thenReturn(1);
fillBuffer(sFilesUri, TEST_BUFFER_SIZE + 1);
fillBuffer(sAudioUri, TEST_BUFFER_SIZE + 2);
fillBuffer(sVideoUri, TEST_BUFFER_SIZE + 3);
fillBuffer(sImagesUri, TEST_BUFFER_SIZE + 4);
- verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), any(), any());
+ verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ any());
}
@SmallTest
@@ -176,7 +183,8 @@
@SmallTest
public void testFlushAllWithSomeContents() throws Exception {
- when(mMockProvider.bulkInsert(eq(mPackageName), any(), any())).thenReturn(1);
+ when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ any())).thenReturn(1);
fillBuffer(sFilesUri, TEST_BUFFER_SIZE - 4);
fillBuffer(sAudioUri, TEST_BUFFER_SIZE - 3);
@@ -184,12 +192,14 @@
fillBuffer(sImagesUri, TEST_BUFFER_SIZE - 1);
mMediaInserter.flushAll();
- verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), any(), any());
+ verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ any());
}
@SmallTest
public void testInsertContentsAfterFlushAll() throws Exception {
- when(mMockProvider.bulkInsert(eq(mPackageName), any(), any())).thenReturn(1);
+ when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ any())).thenReturn(1);
fillBuffer(sFilesUri, TEST_BUFFER_SIZE - 4);
fillBuffer(sAudioUri, TEST_BUFFER_SIZE - 3);
@@ -202,15 +212,20 @@
fillBuffer(sVideoUri, TEST_BUFFER_SIZE + 3);
fillBuffer(sImagesUri, TEST_BUFFER_SIZE + 4);
- verify(mMockProvider, times(8)).bulkInsert(eq(mPackageName), any(), any());
+ verify(mMockProvider, times(8)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), any(),
+ any());
}
@SmallTest
public void testInsertContentsWithDifferentSizePerContentType() throws Exception {
- when(mMockProvider.bulkInsert(eq(mPackageName), eqUri(sFilesUri), any())).thenReturn(1);
- when(mMockProvider.bulkInsert(eq(mPackageName), eqUri(sAudioUri), any())).thenReturn(1);
- when(mMockProvider.bulkInsert(eq(mPackageName), eqUri(sVideoUri), any())).thenReturn(1);
- when(mMockProvider.bulkInsert(eq(mPackageName), eqUri(sImagesUri), any())).thenReturn(1);
+ when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), eqUri(sFilesUri),
+ any())).thenReturn(1);
+ when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), eqUri(sAudioUri),
+ any())).thenReturn(1);
+ when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), eqUri(sVideoUri),
+ any())).thenReturn(1);
+ when(mMockProvider.bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID), eqUri(sImagesUri),
+ any())).thenReturn(1);
for (int i = 0; i < TEST_BUFFER_SIZE; ++i) {
fillBuffer(sFilesUri, 1);
@@ -219,9 +234,13 @@
fillBuffer(sImagesUri, 4);
}
- verify(mMockProvider, times(1)).bulkInsert(eq(mPackageName), eqUri(sFilesUri), any());
- verify(mMockProvider, times(2)).bulkInsert(eq(mPackageName), eqUri(sAudioUri), any());
- verify(mMockProvider, times(3)).bulkInsert(eq(mPackageName), eqUri(sVideoUri), any());
- verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eqUri(sImagesUri), any());
+ verify(mMockProvider, times(1)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID),
+ eqUri(sFilesUri), any());
+ verify(mMockProvider, times(2)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID),
+ eqUri(sAudioUri), any());
+ verify(mMockProvider, times(3)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID),
+ eqUri(sVideoUri), any());
+ verify(mMockProvider, times(4)).bulkInsert(eq(mPackageName), eq(TEST_FEATURE_ID),
+ eqUri(sImagesUri), any());
}
}
diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp
index 9791da6..45f42f1b5 100644
--- a/native/android/system_fonts.cpp
+++ b/native/android/system_fonts.cpp
@@ -16,6 +16,8 @@
#include <jni.h>
+#define LOG_TAG "SystemFont"
+
#include <android/font.h>
#include <android/font_matcher.h>
#include <android/system_fonts.h>
@@ -47,9 +49,14 @@
using XmlCharUniquePtr = std::unique_ptr<xmlChar, XmlCharDeleter>;
using XmlDocUniquePtr = std::unique_ptr<xmlDoc, XmlDocDeleter>;
+struct ParserState {
+ xmlNode* mFontNode = nullptr;
+ XmlCharUniquePtr mLocale;
+};
+
struct ASystemFontIterator {
XmlDocUniquePtr mXmlDoc;
- xmlNode* mFontNode;
+ ParserState state;
// The OEM customization XML.
XmlDocUniquePtr mCustomizationXmlDoc;
@@ -97,6 +104,7 @@
const xmlChar* FAMILY_TAG = BAD_CAST("family");
const xmlChar* FONT_TAG = BAD_CAST("font");
+const xmlChar* LOCALE_ATTR_NAME = BAD_CAST("lang");
xmlNode* firstElement(xmlNode* node, const xmlChar* tag) {
for (xmlNode* child = node->children; child; child = child->next) {
@@ -116,9 +124,9 @@
return nullptr;
}
-void copyFont(const XmlDocUniquePtr& xmlDoc, xmlNode* fontNode, AFont* out,
+void copyFont(const XmlDocUniquePtr& xmlDoc, const ParserState& state, AFont* out,
const std::string& pathPrefix) {
- const xmlChar* LOCALE_ATTR_NAME = BAD_CAST("lang");
+ xmlNode* fontNode = state.mFontNode;
XmlCharUniquePtr filePathStr(
xmlNodeListGetString(xmlDoc.get(), fontNode->xmlChildrenNode, 1));
out->mFilePath = pathPrefix + xmlTrim(
@@ -139,9 +147,10 @@
out->mCollectionIndex = indexStr ?
strtol(reinterpret_cast<const char*>(indexStr.get()), nullptr, 10) : 0;
- XmlCharUniquePtr localeStr(xmlGetProp(xmlDoc->parent, LOCALE_ATTR_NAME));
out->mLocale.reset(
- localeStr ? new std::string(reinterpret_cast<const char*>(localeStr.get())) : nullptr);
+ state.mLocale ?
+ new std::string(reinterpret_cast<const char*>(state.mLocale.get()))
+ : nullptr);
const xmlChar* TAG_ATTR_NAME = BAD_CAST("tag");
const xmlChar* STYLEVALUE_ATTR_NAME = BAD_CAST("stylevalue");
@@ -178,25 +187,27 @@
return S_ISREG(st.st_mode);
}
-xmlNode* findFirstFontNode(const XmlDocUniquePtr& doc) {
+bool findFirstFontNode(const XmlDocUniquePtr& doc, ParserState* state) {
xmlNode* familySet = xmlDocGetRootElement(doc.get());
if (familySet == nullptr) {
- return nullptr;
+ return false;
}
xmlNode* family = firstElement(familySet, FAMILY_TAG);
if (family == nullptr) {
- return nullptr;
+ return false;
}
+ state->mLocale.reset(xmlGetProp(family, LOCALE_ATTR_NAME));
xmlNode* font = firstElement(family, FONT_TAG);
while (font == nullptr) {
family = nextSibling(family, FAMILY_TAG);
if (family == nullptr) {
- return nullptr;
+ return false;
}
font = firstElement(family, FONT_TAG);
}
- return font;
+ state->mFontNode = font;
+ return font != nullptr;
}
} // namespace
@@ -272,38 +283,38 @@
return result.release();
}
-xmlNode* findNextFontNode(const XmlDocUniquePtr& xmlDoc, xmlNode* fontNode) {
- if (fontNode == nullptr) {
+bool findNextFontNode(const XmlDocUniquePtr& xmlDoc, ParserState* state) {
+ if (state->mFontNode == nullptr) {
if (!xmlDoc) {
- return nullptr; // Already at the end.
+ return false; // Already at the end.
} else {
// First time to query font.
- return findFirstFontNode(xmlDoc);
+ return findFirstFontNode(xmlDoc, state);
}
} else {
- xmlNode* nextNode = nextSibling(fontNode, FONT_TAG);
+ xmlNode* nextNode = nextSibling(state->mFontNode, FONT_TAG);
while (nextNode == nullptr) {
- xmlNode* family = nextSibling(fontNode->parent, FAMILY_TAG);
+ xmlNode* family = nextSibling(state->mFontNode->parent, FAMILY_TAG);
if (family == nullptr) {
break;
}
+ state->mLocale.reset(xmlGetProp(family, LOCALE_ATTR_NAME));
nextNode = firstElement(family, FONT_TAG);
}
- return nextNode;
+ state->mFontNode = nextNode;
+ return nextNode != nullptr;
}
}
AFont* ASystemFontIterator_next(ASystemFontIterator* ite) {
LOG_ALWAYS_FATAL_IF(ite == nullptr, "nullptr has passed as iterator argument");
if (ite->mXmlDoc) {
- ite->mFontNode = findNextFontNode(ite->mXmlDoc, ite->mFontNode);
- if (ite->mFontNode == nullptr) {
+ if (!findNextFontNode(ite->mXmlDoc, &ite->state)) {
// Reached end of the XML file. Continue OEM customization.
ite->mXmlDoc.reset();
- ite->mFontNode = nullptr;
} else {
std::unique_ptr<AFont> font = std::make_unique<AFont>();
- copyFont(ite->mXmlDoc, ite->mFontNode, font.get(), "/system/fonts/");
+ copyFont(ite->mXmlDoc, ite->state, font.get(), "/system/fonts/");
if (!isFontFileAvailable(font->mFilePath)) {
return ASystemFontIterator_next(ite);
}
@@ -312,15 +323,13 @@
}
if (ite->mCustomizationXmlDoc) {
// TODO: Filter only customizationType="new-named-family"
- ite->mFontNode = findNextFontNode(ite->mCustomizationXmlDoc, ite->mFontNode);
- if (ite->mFontNode == nullptr) {
+ if (!findNextFontNode(ite->mCustomizationXmlDoc, &ite->state)) {
// Reached end of the XML file. Finishing
ite->mCustomizationXmlDoc.reset();
- ite->mFontNode = nullptr;
return nullptr;
} else {
std::unique_ptr<AFont> font = std::make_unique<AFont>();
- copyFont(ite->mCustomizationXmlDoc, ite->mFontNode, font.get(), "/product/fonts/");
+ copyFont(ite->mCustomizationXmlDoc, ite->state, font.get(), "/product/fonts/");
if (!isFontFileAvailable(font->mFilePath)) {
return ASystemFontIterator_next(ite);
}
@@ -351,7 +360,7 @@
const char* AFont_getLocale(const AFont* font) {
LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument");
- return font->mLocale ? nullptr : font->mLocale->c_str();
+ return font->mLocale ? font->mLocale->c_str() : nullptr;
}
size_t AFont_getCollectionIndex(const AFont* font) {
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index 672879a..b2451c9 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -63,6 +63,64 @@
}
+android_library {
+ name: "CarSystemUI-tests",
+ manifest: "tests/AndroidManifest.xml",
+ resource_dirs: [
+ "tests/res",
+ "res-keyguard",
+ "res",
+ ],
+ srcs: [
+ "tests/src/**/*.java",
+ "src/**/*.java",
+ "src/**/I*.aidl",
+ ],
+ static_libs: [
+ "SystemUI-tests",
+ "CarNotificationLib",
+ "SystemUIPluginLib",
+ "SystemUISharedLib",
+ "SettingsLib",
+ "android.car.userlib",
+ "androidx.legacy_legacy-support-v4",
+ "androidx.recyclerview_recyclerview",
+ "androidx.preference_preference",
+ "androidx.appcompat_appcompat",
+ "androidx.mediarouter_mediarouter",
+ "androidx.palette_palette",
+ "androidx.legacy_legacy-preference-v14",
+ "androidx.leanback_leanback",
+ "androidx.slice_slice-core",
+ "androidx.slice_slice-view",
+ "androidx.slice_slice-builders",
+ "androidx.arch.core_core-runtime",
+ "androidx.lifecycle_lifecycle-extensions",
+ "SystemUI-tags",
+ "SystemUI-proto",
+ "metrics-helper-lib",
+ "androidx.test.rules", "hamcrest-library",
+ "mockito-target-inline-minus-junit4",
+ "testables",
+ "truth-prebuilt",
+ "dagger2-2.19",
+ "//external/kotlinc:kotlin-annotations",
+ ],
+ libs: [
+ "android.test.runner",
+ "telephony-common",
+ "android.test.base",
+ "android.car",
+ ],
+
+ aaptflags: [
+ "--extra-packages",
+ "com.android.systemui",
+ ],
+
+ plugins: ["dagger2-compiler-2.19"],
+}
+
android_app {
name: "CarSystemUI",
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ComponentBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarComponentBinder.java
similarity index 62%
copy from packages/SystemUI/src/com/android/systemui/dagger/ComponentBinder.java
copy to packages/CarSystemUI/src/com/android/systemui/CarComponentBinder.java
index 4e4c06e..c40eda9 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ComponentBinder.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarComponentBinder.java
@@ -14,18 +14,19 @@
* limitations under the License.
*/
-package com.android.systemui.dagger;
+package com.android.systemui;
-import dagger.Binds;
+import com.android.systemui.dagger.DefaultActivityBinder;
+import com.android.systemui.dagger.DefaultServiceBinder;
+
import dagger.Module;
/**
- * Dagger Module that collects related sub-modules together.
+ * Supply Activities, Services, and SystemUI Objects for CarSystemUI.
*/
-@Module(includes = {ActivityBinder.class, ServiceBinder.class, SystemUIBinder.class})
-public abstract class ComponentBinder {
- /** */
- @Binds
- public abstract ContextComponentHelper bindComponentHelper(
- ContextComponentResolver componentHelper);
+@Module(includes = {
+ DefaultActivityBinder.class,
+ DefaultServiceBinder.class,
+ CarSystemUIBinder.class})
+public class CarComponentBinder {
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
index 8e0a3eb..4f7b5d5 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
@@ -16,7 +16,16 @@
package com.android.systemui;
+import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.navigationbar.car.CarNavigationBar;
+import com.android.systemui.pip.PipUI;
+import com.android.systemui.power.PowerUI;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsModule;
+import com.android.systemui.statusbar.car.CarStatusBar;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.util.leak.GarbageMonitor;
+import com.android.systemui.volume.VolumeUI;
import dagger.Binds;
import dagger.Module;
@@ -24,11 +33,71 @@
import dagger.multibindings.IntoMap;
/** Binder for car specific {@link SystemUI} modules. */
-@Module
+@Module(includes = {RecentsModule.class})
public abstract class CarSystemUIBinder {
/** */
@Binds
@IntoMap
@ClassKey(CarNavigationBar.class)
public abstract SystemUI bindCarNavigationBar(CarNavigationBar sysui);
+
+ /** Inject into GarbageMonitor.Service. */
+ @Binds
+ @IntoMap
+ @ClassKey(GarbageMonitor.Service.class)
+ public abstract SystemUI bindGarbageMonitorService(GarbageMonitor.Service service);
+
+ /** Inject into KeyguardViewMediator. */
+ @Binds
+ @IntoMap
+ @ClassKey(KeyguardViewMediator.class)
+ public abstract SystemUI bindKeyguardViewMediator(KeyguardViewMediator sysui);
+
+ /** Inject into LatencyTests. */
+ @Binds
+ @IntoMap
+ @ClassKey(LatencyTester.class)
+ public abstract SystemUI bindLatencyTester(LatencyTester sysui);
+
+ /** Inject into PipUI. */
+ @Binds
+ @IntoMap
+ @ClassKey(PipUI.class)
+ public abstract SystemUI bindPipUI(PipUI sysui);
+
+ /** Inject into PowerUI. */
+ @Binds
+ @IntoMap
+ @ClassKey(PowerUI.class)
+ public abstract SystemUI bindPowerUI(PowerUI sysui);
+
+ /** Inject into Recents. */
+ @Binds
+ @IntoMap
+ @ClassKey(Recents.class)
+ public abstract SystemUI bindRecents(Recents sysui);
+
+ /** Inject into ScreenDecorations. */
+ @Binds
+ @IntoMap
+ @ClassKey(ScreenDecorations.class)
+ public abstract SystemUI bindScreenDecorations(ScreenDecorations sysui);
+
+ /** Inject into StatusBar. */
+ @Binds
+ @IntoMap
+ @ClassKey(StatusBar.class)
+ public abstract SystemUI bindsStatusBar(CarStatusBar sysui);
+
+ /** Inject into StatusBarGoogle. */
+ @Binds
+ @IntoMap
+ @ClassKey(CarStatusBar.class)
+ public abstract SystemUI bindsCarStatusBar(CarStatusBar sysui);
+
+ /** Inject into VolumeUI. */
+ @Binds
+ @IntoMap
+ @ClassKey(VolumeUI.class)
+ public abstract SystemUI bindVolumeUI(VolumeUI sysui);
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 93e553f..d3a6cde 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -46,8 +46,6 @@
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
-import dagger.multibindings.ClassKey;
-import dagger.multibindings.IntoMap;
@Module
abstract class CarSystemUIModule {
@@ -105,11 +103,6 @@
public abstract StatusBar bindStatusBar(CarStatusBar statusBar);
@Binds
- @IntoMap
- @ClassKey(StatusBar.class)
- public abstract SystemUI providesStatusBar(CarStatusBar statusBar);
-
- @Binds
abstract VolumeDialogComponent bindVolumeDialogComponent(
CarVolumeDialogComponent carVolumeDialogComponent);
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
index c2847c8..51b263e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
@@ -29,6 +29,7 @@
@Singleton
@Component(
modules = {
+ CarComponentBinder.class,
DependencyProvider.class,
DependencyBinder.class,
SystemUIFactory.ContextHolder.class,
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
index 63bc66a..98b91ebd 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
@@ -25,6 +25,7 @@
import android.os.ServiceManager;
import android.view.Display;
import android.view.Gravity;
+import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -291,7 +292,8 @@
}
boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
- mCarNavigationBarController.setBottomWindowVisibility(!isKeyboardVisible);
+ mCarNavigationBarController.setBottomWindowVisibility(
+ isKeyboardVisible ? View.GONE : View.VISIBLE);
}
private void updateNavBarForKeyguardContent() {
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java
index f59f886..6bed69b 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java
@@ -98,31 +98,30 @@
}
/** Toggles the bottom nav bar visibility. */
- public boolean setBottomWindowVisibility(boolean isVisible) {
- return setWindowVisibility(getBottomWindow(), isVisible);
+ public boolean setBottomWindowVisibility(@View.Visibility int visibility) {
+ return setWindowVisibility(getBottomWindow(), visibility);
}
/** Toggles the left nav bar visibility. */
- public boolean setLeftWindowVisibility(boolean isVisible) {
- return setWindowVisibility(getLeftWindow(), isVisible);
+ public boolean setLeftWindowVisibility(@View.Visibility int visibility) {
+ return setWindowVisibility(getLeftWindow(), visibility);
}
/** Toggles the right nav bar visibility. */
- public boolean setRightWindowVisibility(boolean isVisible) {
- return setWindowVisibility(getRightWindow(), isVisible);
+ public boolean setRightWindowVisibility(@View.Visibility int visibility) {
+ return setWindowVisibility(getRightWindow(), visibility);
}
- private boolean setWindowVisibility(ViewGroup window, boolean isVisible) {
+ private boolean setWindowVisibility(ViewGroup window, @View.Visibility int visibility) {
if (window == null) {
return false;
}
- int newVisibility = isVisible ? View.VISIBLE : View.GONE;
- if (window.getVisibility() == newVisibility) {
+ if (window.getVisibility() == visibility) {
return false;
}
- window.setVisibility(newVisibility);
+ window.setVisibility(visibility);
return true;
}
@@ -228,6 +227,63 @@
}
}
+ /**
+ * Shows all of the keyguard specific buttons on the valid instances of
+ * {@link CarNavigationBarView}.
+ */
+ public void showAllKeyguardButtons(boolean isSetUp) {
+ checkAllBars(isSetUp);
+ if (mTopView != null) {
+ mTopView.showKeyguardButtons();
+ }
+ if (mBottomView != null) {
+ mBottomView.showKeyguardButtons();
+ }
+ if (mLeftView != null) {
+ mLeftView.showKeyguardButtons();
+ }
+ if (mRightView != null) {
+ mRightView.showKeyguardButtons();
+ }
+ }
+
+ /**
+ * Hides all of the keyguard specific buttons on the valid instances of
+ * {@link CarNavigationBarView}.
+ */
+ public void hideAllKeyguardButtons(boolean isSetUp) {
+ checkAllBars(isSetUp);
+ if (mTopView != null) {
+ mTopView.hideKeyguardButtons();
+ }
+ if (mBottomView != null) {
+ mBottomView.hideKeyguardButtons();
+ }
+ if (mLeftView != null) {
+ mLeftView.hideKeyguardButtons();
+ }
+ if (mRightView != null) {
+ mRightView.hideKeyguardButtons();
+ }
+ }
+
+ /** Toggles whether the notifications icon has an unseen indicator or not. */
+ public void toggleAllNotificationsUnseenIndicator(boolean isSetUp, boolean hasUnseen) {
+ checkAllBars(isSetUp);
+ if (mTopView != null) {
+ mTopView.toggleNotificationUnseenIndicator(hasUnseen);
+ }
+ if (mBottomView != null) {
+ mBottomView.toggleNotificationUnseenIndicator(hasUnseen);
+ }
+ if (mLeftView != null) {
+ mLeftView.toggleNotificationUnseenIndicator(hasUnseen);
+ }
+ if (mRightView != null) {
+ mRightView.toggleNotificationUnseenIndicator(hasUnseen);
+ }
+ }
+
/** Interface for controlling the notifications shade. */
public interface NotificationsShadeController {
/** Toggles the visibility of the notifications shade. */
@@ -244,4 +300,11 @@
}
}
}
+
+ private void checkAllBars(boolean isSetUp) {
+ mTopView = getTopBar(isSetUp);
+ mBottomView = getBottomBar(isSetUp);
+ mLeftView = getLeftBar(isSetUp);
+ mRightView = getRightBar(isSetUp);
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java
index 24f8b74..c245508 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java
@@ -24,6 +24,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.navigationbar.car.CarNavigationBarController.NotificationsShadeController;
import com.android.systemui.statusbar.phone.StatusBarIconController;
/**
@@ -35,7 +36,7 @@
public class CarNavigationBarView extends LinearLayout {
private View mNavButtons;
private CarNavigationButton mNotificationsButton;
- private CarNavigationBarController.NotificationsShadeController mNotificationsShadeController;
+ private NotificationsShadeController mNotificationsShadeController;
private Context mContext;
private View mLockScreenButtons;
// used to wire in open/close gestures for notifications
@@ -81,13 +82,18 @@
return super.onInterceptTouchEvent(ev);
}
- public void setNotificationsPanelController(
- CarNavigationBarController.NotificationsShadeController controller) {
+ /** Sets the notifications panel controller. */
+ public void setNotificationsPanelController(NotificationsShadeController controller) {
mNotificationsShadeController = controller;
}
+ /** Gets the notifications panel controller. */
+ public NotificationsShadeController getNotificationsPanelController() {
+ return mNotificationsShadeController;
+ }
+
/**
- * Set a touch listener that will be called from onInterceptTouchEvent and onTouchEvent
+ * Sets a touch listener that will be called from onInterceptTouchEvent and onTouchEvent
*
* @param statusBarWindowTouchListener The listener to call from touch and intercept touch
*/
@@ -95,6 +101,11 @@
mStatusBarWindowTouchListener = statusBarWindowTouchListener;
}
+ /** Gets the touch listener that will be called from onInterceptTouchEvent and onTouchEvent. */
+ public OnTouchListener getStatusBarWindowTouchListener() {
+ return mStatusBarWindowTouchListener;
+ }
+
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mStatusBarWindowTouchListener != null) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java
index 40823ab..922bfff 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java
@@ -150,6 +150,11 @@
updateImage();
}
+ /** Gets whether the icon is in an unseen state. */
+ public boolean getUnseen() {
+ return mHasUnseen;
+ }
+
private void updateImage() {
if (mHasUnseen) {
setImageResource(mSelected ? UNSEEN_SELECTED_ICON_RESOURCE_ID
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 601bc40..d548fa1 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -27,7 +27,6 @@
import android.car.drivingstate.CarDrivingStateEvent;
import android.car.drivingstate.CarUxRestrictionsManager;
import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
-import android.car.trust.CarTrustAgentEnrollmentManager;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -168,13 +167,7 @@
private Drawable mNotificationPanelBackground;
private ViewGroup mTopNavigationBarContainer;
- private ViewGroup mNavigationBarWindow;
- private ViewGroup mLeftNavigationBarWindow;
- private ViewGroup mRightNavigationBarWindow;
private CarNavigationBarView mTopNavigationBarView;
- private CarNavigationBarView mNavigationBarView;
- private CarNavigationBarView mLeftNavigationBarView;
- private CarNavigationBarView mRightNavigationBarView;
private final Object mQueueLock = new Object();
private final CarNavigationBarController mCarNavigationBarController;
@@ -227,6 +220,8 @@
// Whether heads-up notifications should be shown when shade is open.
private boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen;
+ private CarUxRestrictionManagerWrapper mCarUxRestrictionManagerWrapper;
+
private final CarPowerStateListener mCarPowerStateListener =
(int state) -> {
// When the car powers on, clear all notifications and mute/unread states.
@@ -425,30 +420,32 @@
new DeviceProvisionedController.DeviceProvisionedListener() {
@Override
public void onUserSetupChanged() {
- mHandler.post(() -> restartNavBarsIfNecessary());
+ mHandler.post(() -> resetSystemBarsIfNecessary());
}
@Override
public void onUserSwitched() {
- mHandler.post(() -> restartNavBarsIfNecessary());
+ mHandler.post(() -> resetSystemBarsIfNecessary());
}
});
+ // Used by onDrivingStateChanged and it can be called inside
+ // DrivingStateHelper.connectToCarService()
+ mSwitchToGuestTimer = new SwitchToGuestTimer(mContext);
+
// Register a listener for driving state changes.
mDrivingStateHelper = new DrivingStateHelper(mContext, this::onDrivingStateChanged);
mDrivingStateHelper.connectToCarService();
mPowerManagerHelper = new PowerManagerHelper(mContext, mCarPowerStateListener);
mPowerManagerHelper.connectToCarService();
-
- mSwitchToGuestTimer = new SwitchToGuestTimer(mContext);
}
- private void restartNavBarsIfNecessary() {
+ private void resetSystemBarsIfNecessary() {
boolean currentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
if (mDeviceIsSetUpForUser != currentUserSetup) {
mDeviceIsSetUpForUser = currentUserSetup;
- restartNavBars();
+ resetSystemBars();
}
}
@@ -456,19 +453,9 @@
* Remove all content from navbars and rebuild them. Used to allow for different nav bars
* before and after the device is provisioned. . Also for change of density and font size.
*/
- private void restartNavBars() {
+ private void resetSystemBars() {
mCarFacetButtonController.removeAll();
- if (mNavigationBarWindow != null) {
- mNavigationBarView = null;
- }
- if (mLeftNavigationBarWindow != null) {
- mLeftNavigationBarView = null;
- }
- if (mRightNavigationBarWindow != null) {
- mRightNavigationBarView = null;
- }
-
buildNavBarContent();
// CarFacetButtonController was reset therefore we need to re-add the status bar elements
// to the controller.
@@ -480,51 +467,22 @@
* the full screen user selector is shown.
*/
void setNavBarVisibility(@View.Visibility int visibility) {
- if (mNavigationBarWindow != null) {
- mNavigationBarWindow.setVisibility(visibility);
- }
- if (mLeftNavigationBarWindow != null) {
- mLeftNavigationBarWindow.setVisibility(visibility);
- }
- if (mRightNavigationBarWindow != null) {
- mRightNavigationBarWindow.setVisibility(visibility);
- }
+ mCarNavigationBarController.setBottomWindowVisibility(visibility);
+ mCarNavigationBarController.setLeftWindowVisibility(visibility);
+ mCarNavigationBarController.setRightWindowVisibility(visibility);
}
@Override
public boolean hideKeyguard() {
boolean result = super.hideKeyguard();
- if (mNavigationBarView != null) {
- mNavigationBarView.hideKeyguardButtons();
- }
- if (mLeftNavigationBarView != null) {
- mLeftNavigationBarView.hideKeyguardButtons();
- }
- if (mRightNavigationBarView != null) {
- mRightNavigationBarView.hideKeyguardButtons();
- }
+ mCarNavigationBarController.hideAllKeyguardButtons(mDeviceIsSetUpForUser);
return result;
}
@Override
public void showKeyguard() {
super.showKeyguard();
- updateNavBarForKeyguardContent();
- }
-
- /**
- * Switch to the keyguard applicable content contained in the nav bars
- */
- private void updateNavBarForKeyguardContent() {
- if (mNavigationBarView != null) {
- mNavigationBarView.showKeyguardButtons();
- }
- if (mLeftNavigationBarView != null) {
- mLeftNavigationBarView.showKeyguardButtons();
- }
- if (mRightNavigationBarView != null) {
- mRightNavigationBarView.showKeyguardButtons();
- }
+ mCarNavigationBarController.showAllKeyguardButtons(mDeviceIsSetUpForUser);
}
@Override
@@ -617,31 +575,31 @@
animateCollapsePanels();
}
});
- Car car = Car.createCar(mContext);
- CarUxRestrictionsManager carUxRestrictionsManager = (CarUxRestrictionsManager)
- car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
CarNotificationListener carNotificationListener = new CarNotificationListener();
- CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper =
- new CarUxRestrictionManagerWrapper();
- carUxRestrictionManagerWrapper.setCarUxRestrictionsManager(carUxRestrictionsManager);
+ mCarUxRestrictionManagerWrapper = new CarUxRestrictionManagerWrapper();
+ // This can take time if car service is not ready up to this time.
+ // TODO(b/142808072) Refactor CarUxRestrictionManagerWrapper to allow setting
+ // CarUxRestrictionsManager later and switch to Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT.
+ Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER,
+ (car, ready) -> {
+ if (!ready) {
+ return;
+ }
+ CarUxRestrictionsManager carUxRestrictionsManager =
+ (CarUxRestrictionsManager)
+ car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
+ mCarUxRestrictionManagerWrapper.setCarUxRestrictionsManager(
+ carUxRestrictionsManager);
+ });
mNotificationDataManager = new NotificationDataManager();
mNotificationDataManager.setOnUnseenCountUpdateListener(
() -> {
- if (mNavigationBarView != null && mNotificationDataManager != null) {
- Boolean hasUnseen =
+ if (mNotificationDataManager != null) {
+ boolean hasUnseen =
mNotificationDataManager.getUnseenNotificationCount() > 0;
- if (mNavigationBarView != null) {
- mNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
- }
-
- if (mLeftNavigationBarView != null) {
- mLeftNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
- }
-
- if (mRightNavigationBarView != null) {
- mRightNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
- }
+ mCarNavigationBarController.toggleAllNotificationsUnseenIndicator(
+ mDeviceIsSetUpForUser, hasUnseen);
}
});
@@ -652,7 +610,7 @@
mNotificationClickHandlerFactory, mNotificationDataManager);
mNotificationClickHandlerFactory.setNotificationDataManager(mNotificationDataManager);
- carNotificationListener.registerAsSystemService(mContext, carUxRestrictionManagerWrapper,
+ carNotificationListener.registerAsSystemService(mContext, mCarUxRestrictionManagerWrapper,
carHeadsUpNotificationManager, mNotificationDataManager);
mNotificationView = mStatusBarWindow.findViewById(R.id.notification_view);
@@ -752,7 +710,7 @@
mNotificationView,
PreprocessingManager.getInstance(mContext),
carNotificationListener,
- carUxRestrictionManagerWrapper,
+ mCarUxRestrictionManagerWrapper,
mNotificationDataManager);
mNotificationViewController.enable();
}
@@ -895,37 +853,27 @@
@Override
protected void createNavigationBar(@Nullable RegisterStatusBarResult result) {
- buildNavBarWindows();
+ mTopNavigationBarContainer = mStatusBarWindow
+ .findViewById(R.id.car_top_navigation_bar_container);
+
buildNavBarContent();
}
private void buildNavBarContent() {
buildTopBar();
- mNavigationBarView = mCarNavigationBarController.getBottomBar(mDeviceIsSetUpForUser);
mCarNavigationBarController.registerBottomBarTouchListener(
mNavBarNotificationTouchListener);
- mLeftNavigationBarView = mCarNavigationBarController.getLeftBar(mDeviceIsSetUpForUser);
mCarNavigationBarController.registerLeftBarTouchListener(
mNavBarNotificationTouchListener);
- mRightNavigationBarView = mCarNavigationBarController.getLeftBar(mDeviceIsSetUpForUser);
mCarNavigationBarController.registerRightBarTouchListener(
mNavBarNotificationTouchListener);
mCarNavigationBarController.registerNotificationController(() -> togglePanel());
}
- private void buildNavBarWindows() {
- mTopNavigationBarContainer = mStatusBarWindow
- .findViewById(R.id.car_top_navigation_bar_container);
-
- mNavigationBarWindow = mCarNavigationBarController.getBottomWindow();
- mLeftNavigationBarWindow = mCarNavigationBarController.getLeftWindow();
- mRightNavigationBarWindow = mCarNavigationBarController.getRightWindow();
- }
-
private void buildTopBar() {
mTopNavigationBarContainer.removeAllViews();
mTopNavigationBarView = mCarNavigationBarController.getTopBar(mDeviceIsSetUpForUser);
@@ -1011,12 +959,8 @@
UserSwitcherController userSwitcherController =
Dependency.get(UserSwitcherController.class);
if (userSwitcherController.useFullscreenUserSwitcher()) {
- Car car = Car.createCar(mContext);
- CarTrustAgentEnrollmentManager enrollmentManager = (CarTrustAgentEnrollmentManager) car
- .getCarManager(Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE);
mFullscreenUserSwitcher = new FullscreenUserSwitcher(this,
- mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub),
- enrollmentManager, mContext);
+ mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub), mContext);
} else {
super.createUserSwitcher();
}
@@ -1099,7 +1043,7 @@
@Override
public void onDensityOrFontScaleChanged() {
super.onDensityOrFontScaleChanged();
- restartNavBars();
+ resetSystemBars();
// Need to update the background on density changed in case the change was due to night
// mode.
mNotificationPanelBackground = getDefaultWallpaper();
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
index a442426..cd87e78 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
@@ -17,14 +17,11 @@
package com.android.systemui.statusbar.car;
import android.car.Car;
-import android.car.CarNotConnectedException;
+import android.car.Car.CarServiceLifecycleListener;
import android.car.drivingstate.CarDrivingStateEvent;
import android.car.drivingstate.CarDrivingStateManager;
import android.car.drivingstate.CarDrivingStateManager.CarDrivingStateEventListener;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.ServiceConnection;
-import android.os.IBinder;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -55,16 +52,11 @@
if (mDrivingStateManager == null) {
return false;
}
- try {
- CarDrivingStateEvent currentState = mDrivingStateManager.getCurrentCarDrivingState();
- if (currentState != null) {
- return currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_IDLING
- || currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING;
- }
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Cannot determine current driving state. Car not connected", e);
+ CarDrivingStateEvent currentState = mDrivingStateManager.getCurrentCarDrivingState();
+ if (currentState != null) {
+ return currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_IDLING
+ || currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING;
}
-
return false; // Default to false.
}
@@ -72,55 +64,25 @@
* Establishes connection with the Car service.
*/
public void connectToCarService() {
- mCar = Car.createCar(mContext, mCarConnectionListener);
- if (mCar != null) {
- mCar.connect();
- }
+ mCar = Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT,
+ mCarServiceLifecycleListener);
}
- /**
- * Disconnects from Car service and cleans up listeners.
- */
- public void disconnectFromCarService() {
- if (mCar != null) {
- mCar.disconnect();
+ private final CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> {
+ if (!ready) {
+ return;
}
- }
-
- private final ServiceConnection mCarConnectionListener =
- new ServiceConnection() {
- public void onServiceConnected(ComponentName name, IBinder service) {
- logD("Car Service connected");
- try {
- mDrivingStateManager = (CarDrivingStateManager) mCar.getCarManager(
- Car.CAR_DRIVING_STATE_SERVICE);
- if (mDrivingStateManager != null) {
- mDrivingStateManager.registerListener(mDrivingStateHandler);
- mDrivingStateHandler.onDrivingStateChanged(
- mDrivingStateManager.getCurrentCarDrivingState());
- } else {
- Log.e(TAG, "CarDrivingStateService service not available");
- }
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car not connected", e);
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- destroyDrivingStateManager();
- }
- };
-
- private void destroyDrivingStateManager() {
- try {
- if (mDrivingStateManager != null) {
- mDrivingStateManager.unregisterListener();
- }
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Error unregistering listeners", e);
+ logD("Car Service connected");
+ mDrivingStateManager = (CarDrivingStateManager) car.getCarManager(
+ Car.CAR_DRIVING_STATE_SERVICE);
+ if (mDrivingStateManager != null) {
+ mDrivingStateManager.registerListener(mDrivingStateHandler);
+ mDrivingStateHandler.onDrivingStateChanged(
+ mDrivingStateManager.getCurrentCarDrivingState());
+ } else {
+ Log.e(TAG, "CarDrivingStateService service not available");
}
- }
+ };
private void logD(String message) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index 0f7c1ee..31aced0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -18,6 +18,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.car.Car;
import android.car.trust.CarTrustAgentEnrollmentManager;
import android.car.userlib.CarUserManagerHelper;
import android.content.BroadcastReceiver;
@@ -50,7 +51,7 @@
private final CarStatusBar mStatusBar;
private final Context mContext;
private final UserManager mUserManager;
- private final CarTrustAgentEnrollmentManager mEnrollmentManager;
+ private CarTrustAgentEnrollmentManager mEnrollmentManager;
private CarTrustAgentUnlockDialogHelper mUnlockDialogHelper;
private UserGridRecyclerView.UserRecord mSelectedUser;
private CarUserManagerHelper mCarUserManagerHelper;
@@ -64,13 +65,11 @@
mContext.unregisterReceiver(mUserUnlockReceiver);
}
};
+ private final Car mCar;
-
- public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub,
- CarTrustAgentEnrollmentManager enrollmentManager, Context context) {
+ public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub, Context context) {
mStatusBar = statusBar;
mParent = containerStub.inflate();
- mEnrollmentManager = enrollmentManager;
mContext = context;
View container = mParent.findViewById(R.id.container);
@@ -86,6 +85,15 @@
mUnlockDialogHelper = new CarTrustAgentUnlockDialogHelper(mContext);
mUserManager = mContext.getSystemService(UserManager.class);
+ mCar = Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT,
+ (car, ready) -> {
+ if (!ready) {
+ return;
+ }
+ mEnrollmentManager = (CarTrustAgentEnrollmentManager) car
+ .getCarManager(Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE);
+ });
+
mShortAnimDuration = container.getResources()
.getInteger(android.R.integer.config_shortAnimTime);
IntentFilter filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
@@ -201,6 +209,9 @@
}
private boolean hasTrustedDevice(int uid) {
+ if (mEnrollmentManager == null) { // car service not ready, so it cannot be available.
+ return false;
+ }
return !mEnrollmentManager.getEnrolledDeviceInfoForUser(uid).isEmpty();
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java
index 8de1439..a27dd34 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java
@@ -18,13 +18,10 @@
import android.annotation.NonNull;
import android.car.Car;
-import android.car.CarNotConnectedException;
+import android.car.Car.CarServiceLifecycleListener;
import android.car.hardware.power.CarPowerManager;
import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.ServiceConnection;
-import android.os.IBinder;
import android.util.Log;
/**
@@ -39,55 +36,30 @@
private Car mCar;
private CarPowerManager mCarPowerManager;
- private final ServiceConnection mCarConnectionListener =
- new ServiceConnection() {
- public void onServiceConnected(ComponentName name, IBinder service) {
- Log.d(TAG, "Car Service connected");
- try {
- mCarPowerManager = (CarPowerManager) mCar.getCarManager(Car.POWER_SERVICE);
- if (mCarPowerManager != null) {
- mCarPowerManager.setListener(mCarPowerStateListener);
- } else {
- Log.e(TAG, "CarPowerManager service not available");
- }
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car not connected", e);
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- destroyCarPowerManager();
- }
- };
+ private final CarServiceLifecycleListener mCarServiceLifecycleListener;
PowerManagerHelper(Context context, @NonNull CarPowerStateListener listener) {
mContext = context;
mCarPowerStateListener = listener;
+ mCarServiceLifecycleListener = (car, ready) -> {
+ if (!ready) {
+ return;
+ }
+ Log.d(TAG, "Car Service connected");
+ mCarPowerManager = (CarPowerManager) car.getCarManager(Car.POWER_SERVICE);
+ if (mCarPowerManager != null) {
+ mCarPowerManager.setListener(mCarPowerStateListener);
+ } else {
+ Log.e(TAG, "CarPowerManager service not available");
+ }
+ };
}
/**
* Connect to Car service.
*/
void connectToCarService() {
- mCar = Car.createCar(mContext, mCarConnectionListener);
- if (mCar != null) {
- mCar.connect();
- }
- }
-
- /**
- * Disconnects from Car service.
- */
- void disconnectFromCarService() {
- if (mCar != null) {
- mCar.disconnect();
- }
- }
-
- private void destroyCarPowerManager() {
- if (mCarPowerManager != null) {
- mCarPowerManager.clearListener();
- }
+ mCar = Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT,
+ mCarServiceLifecycleListener);
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index 3b48259..05657ff 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -32,7 +32,6 @@
import android.content.IntentFilter;
import android.content.pm.UserInfo;
import android.content.res.Resources;
-import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.AsyncTask;
import android.os.UserHandle;
@@ -67,6 +66,7 @@
private CarUserManagerHelper mCarUserManagerHelper;
private UserManager mUserManager;
private Context mContext;
+ private UserIconProvider mUserIconProvider;
private final BroadcastReceiver mUserUpdateReceiver = new BroadcastReceiver() {
@Override
@@ -80,6 +80,7 @@
mContext = context;
mCarUserManagerHelper = new CarUserManagerHelper(mContext);
mUserManager = UserManager.get(mContext);
+ mUserIconProvider = new UserIconProvider();
addItemDecoration(new ItemSpacingDecoration(mContext.getResources().getDimensionPixelSize(
R.dimen.car_user_switcher_vertical_spacing_between_users)));
@@ -252,9 +253,7 @@
@Override
public void onBindViewHolder(UserAdapterViewHolder holder, int position) {
UserRecord userRecord = mUsers.get(position);
- RoundedBitmapDrawable circleIcon = RoundedBitmapDrawableFactory.create(mRes,
- getUserRecordIcon(userRecord));
- circleIcon.setCircular(true);
+ RoundedBitmapDrawable circleIcon = getCircularUserRecordIcon(userRecord);
holder.mUserAvatarImageView.setImageDrawable(circleIcon);
holder.mUserNameTextView.setText(userRecord.mInfo.name);
@@ -336,17 +335,20 @@
}
}
- private Bitmap getUserRecordIcon(UserRecord userRecord) {
+ private RoundedBitmapDrawable getCircularUserRecordIcon(UserRecord userRecord) {
+ Resources resources = mContext.getResources();
+ RoundedBitmapDrawable circleIcon;
if (userRecord.mIsStartGuestSession) {
- return mCarUserManagerHelper.getGuestDefaultIcon();
+ circleIcon = mUserIconProvider.getRoundedGuestDefaultIcon(resources);
+ } else if (userRecord.mIsAddUser) {
+ circleIcon = RoundedBitmapDrawableFactory.create(mRes, UserIcons.convertToBitmap(
+ mContext.getDrawable(R.drawable.car_add_circle_round)));
+ circleIcon.setCircular(true);
+ } else {
+ circleIcon = mUserIconProvider.getRoundedUserIcon(userRecord.mInfo, mContext);
}
- if (userRecord.mIsAddUser) {
- return UserIcons.convertToBitmap(mContext
- .getDrawable(R.drawable.car_add_circle_round));
- }
-
- return mCarUserManagerHelper.getUserIcon(userRecord.mInfo);
+ return circleIcon;
}
@Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserIconProvider.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserIconProvider.java
new file mode 100644
index 0000000..9464eab
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserIconProvider.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.car;
+
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import androidx.core.graphics.drawable.RoundedBitmapDrawable;
+import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
+
+import com.android.internal.util.UserIcons;
+import com.android.systemui.R;
+
+/**
+ * Simple class for providing icons for users.
+ */
+public class UserIconProvider {
+ /**
+ * Gets a scaled rounded icon for the given user. If a user does not have an icon saved, this
+ * method will default to a generic icon and update UserManager to use that icon.
+ *
+ * @param userInfo User for which the icon is requested.
+ * @param context Context to use for resources
+ * @return {@link RoundedBitmapDrawable} representing the icon for the user.
+ */
+ public RoundedBitmapDrawable getRoundedUserIcon(UserInfo userInfo, Context context) {
+ UserManager userManager = UserManager.get(context);
+ Resources res = context.getResources();
+ Bitmap icon = userManager.getUserIcon(userInfo.id);
+
+ if (icon == null) {
+ icon = assignDefaultIcon(userManager, res, userInfo);
+ }
+
+ return createScaledRoundIcon(res, icon);
+ }
+
+ /** Returns a scaled, rounded, default icon for the Guest user */
+ public RoundedBitmapDrawable getRoundedGuestDefaultIcon(Resources resources) {
+ return createScaledRoundIcon(resources, getGuestUserDefaultIcon(resources));
+ }
+
+ private RoundedBitmapDrawable createScaledRoundIcon(Resources resources, Bitmap icon) {
+ BitmapDrawable scaledIcon = scaleUserIcon(resources, icon);
+ RoundedBitmapDrawable circleIcon =
+ RoundedBitmapDrawableFactory.create(resources, scaledIcon.getBitmap());
+ circleIcon.setCircular(true);
+ return circleIcon;
+ }
+
+ /**
+ * Returns a {@link Drawable} for the given {@code icon} scaled to the appropriate size.
+ */
+ private static BitmapDrawable scaleUserIcon(Resources res, Bitmap icon) {
+ int desiredSize = res.getDimensionPixelSize(R.dimen.car_primary_icon_size);
+ Bitmap scaledIcon =
+ Bitmap.createScaledBitmap(icon, desiredSize, desiredSize, /*filter=*/ true);
+ return new BitmapDrawable(res, scaledIcon);
+ }
+
+ /**
+ * Assigns a default icon to a user according to the user's id. Handles Guest icon and non-guest
+ * user icons.
+ *
+ * @param userManager {@link UserManager} to set user icon
+ * @param resources {@link Resources} to grab icons from
+ * @param userInfo User whose avatar is set to default icon.
+ * @return Bitmap of the user icon.
+ */
+ private Bitmap assignDefaultIcon(
+ UserManager userManager, Resources resources, UserInfo userInfo) {
+ Bitmap bitmap = userInfo.isGuest()
+ ? getGuestUserDefaultIcon(resources)
+ : getUserDefaultIcon(resources, userInfo.id);
+ userManager.setUserIcon(userInfo.id, bitmap);
+ return bitmap;
+ }
+
+ /**
+ * Gets a bitmap representing the user's default avatar.
+ *
+ * @param resources The resources to pull from
+ * @param id The id of the user to get the icon for. Pass {@link UserHandle#USER_NULL} for
+ * Guest user.
+ * @return Default user icon
+ */
+ private Bitmap getUserDefaultIcon(Resources resources, @UserIdInt int id) {
+ return UserIcons.convertToBitmap(
+ UserIcons.getDefaultUserIcon(resources, id, /* light= */ false));
+ }
+
+ private Bitmap getGuestUserDefaultIcon(Resources resources) {
+ return getUserDefaultIcon(resources, UserHandle.USER_NULL);
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
index e81be1b..41914d2 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
@@ -20,15 +20,13 @@
import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS;
import android.car.Car;
+import android.car.Car.CarServiceLifecycleListener;
import android.car.VehicleUnit;
import android.car.hardware.CarPropertyValue;
import android.car.hardware.hvac.CarHvacManager;
import android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.ServiceConnection;
import android.os.Handler;
-import android.os.IBinder;
import android.util.Log;
import java.util.ArrayList;
@@ -54,6 +52,7 @@
private Car mCar;
private CarHvacManager mHvacManager;
private HashMap<HvacKey, List<TemperatureView>> mTempComponents = new HashMap<>();
+
/**
* Callback for getting changes from {@link CarHvacManager} and setting the UI elements to
* match.
@@ -85,39 +84,17 @@
+ " zone: " + zone);
}
};
- /**
- * If the connection to car service goes away then restart it.
- */
- private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- Log.d(TAG, "Death of HVAC triggering a restart");
- if (mCar != null) {
- mCar.disconnect();
- }
- destroyHvacManager();
- mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY);
- }
- };
- /**
- * Registers callbacks and initializes components upon connection.
- */
- private ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- try {
- service.linkToDeath(mRestart, 0);
- mHvacManager = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
- mHvacManager.registerCallback(mHardwareCallback);
- initComponents();
- } catch (Exception e) {
- Log.e(TAG, "Failed to correctly connect to HVAC", e);
- }
- }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- destroyHvacManager();
+ private final CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> {
+ if (!ready) {
+ return;
+ }
+ try {
+ mHvacManager = (CarHvacManager) car.getCarManager(Car.HVAC_SERVICE);
+ mHvacManager.registerCallback(mHardwareCallback);
+ initComponents();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to correctly connect to HVAC", e);
}
};
@@ -132,18 +109,8 @@
*/
public void connectToCarService() {
mHandler = new Handler();
- mCar = Car.createCar(mContext, mServiceConnection, mHandler);
- if (mCar != null) {
- // note: this connect call handles the retries
- mCar.connect();
- }
- }
-
- private void destroyHvacManager() {
- if (mHvacManager != null) {
- mHvacManager.unregisterCallback(mHardwareCallback);
- mHvacManager = null;
- }
+ mCar = Car.createCar(mContext, /* handler= */ mHandler, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT,
+ mCarServiceLifecycleListener);
}
/**
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index 22c7c7a..d979bad 100644
--- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -24,12 +24,10 @@
import android.app.Dialog;
import android.app.KeyguardManager;
import android.car.Car;
-import android.car.CarNotConnectedException;
+import android.car.Car.CarServiceLifecycleListener;
import android.car.media.CarAudioManager;
-import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
-import android.content.ServiceConnection;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.Color;
@@ -39,7 +37,6 @@
import android.media.AudioManager;
import android.os.Debug;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.util.AttributeSet;
@@ -146,42 +143,30 @@
private boolean mDismissing;
private boolean mExpanded;
private View mExpandIcon;
- private final ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- try {
- mExpanded = false;
- mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
- int volumeGroupCount = mCarAudioManager.getVolumeGroupCount();
- // Populates volume slider items from volume groups to UI.
- for (int groupId = 0; groupId < volumeGroupCount; groupId++) {
- VolumeItem volumeItem = getVolumeItemForUsages(
- mCarAudioManager.getUsagesForVolumeGroupId(groupId));
- mAvailableVolumeItems.add(volumeItem);
- // The first one is the default item.
- if (groupId == 0) {
- setuptListItem(0);
- }
- }
- // If list is already initiated, update its content.
- if (mVolumeItemsAdapter != null) {
- mVolumeItemsAdapter.notifyDataSetChanged();
- }
- mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
+ private final CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> {
+ if (!ready) {
+ return;
+ }
+ mExpanded = false;
+ mCarAudioManager = (CarAudioManager) car.getCarManager(Car.AUDIO_SERVICE);
+ int volumeGroupCount = mCarAudioManager.getVolumeGroupCount();
+ // Populates volume slider items from volume groups to UI.
+ for (int groupId = 0; groupId < volumeGroupCount; groupId++) {
+ VolumeItem volumeItem = getVolumeItemForUsages(
+ mCarAudioManager.getUsagesForVolumeGroupId(groupId));
+ mAvailableVolumeItems.add(volumeItem);
+ // The first one is the default item.
+ if (groupId == 0) {
+ setuptListItem(0);
}
}
- /**
- * This does not get called when service is properly disconnected.
- * So we need to also handle cleanups in destroy().
- */
- @Override
- public void onServiceDisconnected(ComponentName name) {
- cleanupAudioManager();
+ // If list is already initiated, update its content.
+ if (mVolumeItemsAdapter != null) {
+ mVolumeItemsAdapter.notifyDataSetChanged();
}
+ mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback);
};
private void setuptListItem(int groupId) {
@@ -196,25 +181,14 @@
public CarVolumeDialogImpl(Context context) {
mContext = context;
mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- mCar = Car.createCar(mContext, mServiceConnection);
}
private static int getSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
- try {
- return carAudioManager.getGroupVolume(volumeGroupId);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
- }
- return 0;
+ return carAudioManager.getGroupVolume(volumeGroupId);
}
private static int getMaxSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
- try {
- return carAudioManager.getGroupMaxVolume(volumeGroupId);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
- }
- return 0;
+ return carAudioManager.getGroupMaxVolume(volumeGroupId);
}
/**
@@ -224,8 +198,8 @@
@Override
public void init(int windowType, Callback callback) {
initDialog();
-
- mCar.connect();
+ mCar = Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT,
+ mCarServiceLifecycleListener);
}
@Override
@@ -235,7 +209,10 @@
cleanupAudioManager();
// unregisterVolumeCallback is not being called when disconnect car, so we manually cleanup
// audio manager beforehand.
- mCar.disconnect();
+ if (mCar != null) {
+ mCar.disconnect();
+ mCar = null;
+ }
}
private void initDialog() {
@@ -605,18 +582,14 @@
// sent back down again.
return;
}
- try {
- if (mCarAudioManager == null) {
- Log.w(TAG, "Ignoring volume change event because the car isn't connected");
- return;
- }
- mAvailableVolumeItems.get(mVolumeGroupId).progress = progress;
- mAvailableVolumeItems.get(
- mVolumeGroupId).carVolumeItem.setProgress(progress);
- mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
+ if (mCarAudioManager == null) {
+ Log.w(TAG, "Ignoring volume change event because the car isn't connected");
+ return;
}
+ mAvailableVolumeItems.get(mVolumeGroupId).progress = progress;
+ mAvailableVolumeItems.get(
+ mVolumeGroupId).carVolumeItem.setProgress(progress);
+ mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0);
}
@Override
diff --git a/packages/CarSystemUI/tests/Android.mk b/packages/CarSystemUI/tests/Android.mk
new file mode 100644
index 0000000..1366568
--- /dev/null
+++ b/packages/CarSystemUI/tests/Android.mk
@@ -0,0 +1,88 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_USE_AAPT2 := true
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JACK_FLAGS := --multi-dex native
+LOCAL_DX_FLAGS := --multi-dex
+
+LOCAL_PACKAGE_NAME := CarSystemUITests
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+ CarSystemUI-tests
+
+LOCAL_MULTILIB := both
+
+LOCAL_JNI_SHARED_LIBRARIES := \
+ libdexmakerjvmtiagent \
+ libmultiplejvmtiagentsinterferenceagent
+
+LOCAL_JAVA_LIBRARIES := \
+ android.test.runner \
+ telephony-common \
+ android.test.base \
+
+LOCAL_AAPT_FLAGS := --extra-packages com.android.systemui
+
+# sign this with platform cert, so this test is allowed to inject key events into
+# UI it doesn't own. This is necessary to allow screenshots to be taken
+LOCAL_CERTIFICATE := platform
+
+# Provide jack a list of classes to exclude from code coverage.
+# This is needed because the CarSystemUITests compile CarSystemUI source directly, rather than using
+# LOCAL_INSTRUMENTATION_FOR := CarSystemUI.
+#
+# We want to exclude the test classes from code coverage measurements, but they share the same
+# package as the rest of SystemUI so they can't be easily filtered by package name.
+#
+# Generate a comma separated list of patterns based on the test source files under src/
+# SystemUI classes are in ../src/ so they won't be excluded.
+# Example:
+# Input files: src/com/android/systemui/Test.java src/com/android/systemui/AnotherTest.java
+# Generated exclude list: com.android.systemui.Test*,com.android.systemui.AnotherTest*
+
+# Filter all src files under src/ to just java files
+local_java_files := $(filter %.java,$(call all-java-files-under, src))
+
+# Transform java file names into full class names.
+# This only works if the class name matches the file name and the directory structure
+# matches the package.
+local_classes := $(subst /,.,$(patsubst src/%.java,%,$(local_java_files)))
+local_comma := ,
+local_empty :=
+local_space := $(local_empty) $(local_empty)
+
+# Convert class name list to jacoco exclude list
+# This appends a * to all classes and replace the space separators with commas.
+jacoco_exclude := $(subst $(space),$(comma),$(patsubst %,%*,$(local_classes)))
+
+LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.systemui.*,com.android.keyguard.*
+LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := $(jacoco_exclude)
+
+ifeq ($(EXCLUDE_SYSTEMUI_TESTS),)
+ include $(BUILD_PACKAGE)
+endif
+
+# Reset variables
+local_java_files :=
+local_classes :=
+local_comma :=
+local_space :=
+jacoco_exclude :=
diff --git a/packages/CarSystemUI/tests/AndroidManifest.xml b/packages/CarSystemUI/tests/AndroidManifest.xml
new file mode 100644
index 0000000..a74bb56
--- /dev/null
+++ b/packages/CarSystemUI/tests/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:sharedUserId="android.uid.system"
+ package="com.android.systemui.tests">
+
+ <application android:debuggable="true" android:largeHeap="true">
+ <uses-library android:name="android.test.runner" />
+
+ <provider
+ android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer"
+ tools:replace="android:authorities"
+ android:authorities="${applicationId}.lifecycle-tests"
+ android:exported="false"
+ android:enabled="false"
+ android:multiprocess="true" />
+ </application>
+
+ <instrumentation android:name="android.testing.TestableInstrumentation"
+ android:targetPackage="com.android.systemui.tests"
+ android:label="Tests for CarSystemUI">
+ </instrumentation>
+</manifest>
diff --git a/packages/CarSystemUI/tests/AndroidTest.xml b/packages/CarSystemUI/tests/AndroidTest.xml
new file mode 100644
index 0000000..8685632
--- /dev/null
+++ b/packages/CarSystemUI/tests/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<configuration description="Runs Tests for CarSystemUI.">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="CarSystemUITests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="framework-base-presubmit" />
+ <option name="test-tag" value="CarSystemUITests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.systemui.tests" />
+ <option name="runner" value="android.testing.TestableInstrumentation" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/packages/CarSystemUI/tests/res/values/config.xml b/packages/CarSystemUI/tests/res/values/config.xml
new file mode 100644
index 0000000..0d08ac2
--- /dev/null
+++ b/packages/CarSystemUI/tests/res/values/config.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<resources>
+ <!-- Configure which system ui bars should be displayed.
+ These can be overwritten within the tests. -->
+ <bool name="config_enableLeftNavigationBar">false</bool>
+ <bool name="config_enableRightNavigationBar">false</bool>
+ <bool name="config_enableBottomNavigationBar">false</bool>
+</resources>
diff --git a/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java b/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
new file mode 100644
index 0000000..fe59cbf
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android;
+
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import android.testing.AndroidTestingRunner;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.internal.runner.ClassPathScanner;
+import androidx.test.internal.runner.ClassPathScanner.ChainedClassNameFilter;
+import androidx.test.internal.runner.ClassPathScanner.ExternalClassNameFilter;
+
+import com.android.systemui.SysuiBaseFragmentTest;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * This is named AAAPlusPlusVerifySysuiRequiredTestPropertiesTest for two reasons.
+ * a) Its so awesome it deserves an AAA++
+ * b) It should run first to draw attention to itself.
+ *
+ * For trues though: this test verifies that all the sysui tests extend the right classes.
+ * This matters because including tests with different context implementations in the same
+ * test suite causes errors, such as the incorrect settings provider being cached.
+ * For an example, see {@link com.android.systemui.DependencyTest}.
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class AAAPlusPlusVerifySysuiRequiredTestPropertiesTest extends SysuiTestCase {
+
+ private static final String TAG = "AAA++VerifyTest";
+
+ private static final Class[] BASE_CLS_WHITELIST = {
+ SysuiTestCase.class,
+ SysuiBaseFragmentTest.class,
+ };
+
+ private static final Class[] SUPPORTED_SIZES = {
+ SmallTest.class,
+ MediumTest.class,
+ LargeTest.class,
+ android.test.suitebuilder.annotation.SmallTest.class,
+ android.test.suitebuilder.annotation.MediumTest.class,
+ android.test.suitebuilder.annotation.LargeTest.class,
+ };
+
+ @Test
+ public void testAllClassInheritance() throws Throwable {
+ ArrayList<String> fails = new ArrayList<>();
+ for (String className : getClassNamesFromClassPath()) {
+ Class<?> cls = Class.forName(className, false, this.getClass().getClassLoader());
+ if (!isTestClass(cls)) continue;
+
+ boolean hasParent = false;
+ for (Class<?> parent : BASE_CLS_WHITELIST) {
+ if (parent.isAssignableFrom(cls)) {
+ hasParent = true;
+ break;
+ }
+ }
+ boolean hasSize = hasSize(cls);
+ if (!hasSize) {
+ fails.add(cls.getName() + " does not have size annotation, such as @SmallTest");
+ }
+ if (!hasParent) {
+ fails.add(cls.getName() + " does not extend any of " + getClsStr());
+ }
+ }
+
+ assertThat("All sysui test classes must have size and extend one of " + getClsStr(),
+ fails, is(empty()));
+ }
+
+ private boolean hasSize(Class<?> cls) {
+ for (int i = 0; i < SUPPORTED_SIZES.length; i++) {
+ if (cls.getDeclaredAnnotation(SUPPORTED_SIZES[i]) != null) return true;
+ }
+ return false;
+ }
+
+ private Collection<String> getClassNamesFromClassPath() {
+ ClassPathScanner scanner = new ClassPathScanner(mContext.getPackageCodePath());
+
+ ChainedClassNameFilter filter = new ChainedClassNameFilter();
+
+ filter.add(new ExternalClassNameFilter());
+ filter.add(s -> s.startsWith("com.android.systemui")
+ || s.startsWith("com.android.keyguard"));
+
+ try {
+ return scanner.getClassPathEntries(filter);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to scan classes", e);
+ }
+ return Collections.emptyList();
+ }
+
+ private String getClsStr() {
+ return TextUtils.join(",", Arrays.asList(BASE_CLS_WHITELIST)
+ .stream().map(cls -> cls.getSimpleName()).toArray());
+ }
+
+ /**
+ * Determines if given class is a valid test class.
+ *
+ * @return <code>true</code> if loadedClass is a test
+ */
+ private boolean isTestClass(Class<?> loadedClass) {
+ try {
+ if (Modifier.isAbstract(loadedClass.getModifiers())) {
+ logDebug(String.format("Skipping abstract class %s: not a test",
+ loadedClass.getName()));
+ return false;
+ }
+ // TODO: try to find upstream junit calls to replace these checks
+ if (junit.framework.Test.class.isAssignableFrom(loadedClass)) {
+ // ensure that if a TestCase, it has at least one test method otherwise
+ // TestSuite will throw error
+ if (junit.framework.TestCase.class.isAssignableFrom(loadedClass)) {
+ return hasJUnit3TestMethod(loadedClass);
+ }
+ return true;
+ }
+ // TODO: look for a 'suite' method?
+ if (loadedClass.isAnnotationPresent(RunWith.class)) {
+ return true;
+ }
+ for (Method testMethod : loadedClass.getMethods()) {
+ if (testMethod.isAnnotationPresent(Test.class)) {
+ return true;
+ }
+ }
+ logDebug(String.format("Skipping class %s: not a test", loadedClass.getName()));
+ return false;
+ } catch (Exception e) {
+ // Defensively catch exceptions - Will throw runtime exception if it cannot load
+ // methods.
+ // For earlier versions of Android (Pre-ICS), Dalvik might try to initialize a class
+ // during getMethods(), fail to do so, hide the error and throw a NoSuchMethodException.
+ // Since the java.lang.Class.getMethods does not declare such an exception, resort to a
+ // generic catch all.
+ // For ICS+, Dalvik will throw a NoClassDefFoundException.
+ Log.w(TAG, String.format("%s in isTestClass for %s", e.toString(),
+ loadedClass.getName()));
+ return false;
+ } catch (Error e) {
+ // defensively catch Errors too
+ Log.w(TAG, String.format("%s in isTestClass for %s", e.toString(),
+ loadedClass.getName()));
+ return false;
+ }
+ }
+
+ private boolean hasJUnit3TestMethod(Class<?> loadedClass) {
+ for (Method testMethod : loadedClass.getMethods()) {
+ if (isPublicTestMethod(testMethod)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // copied from junit.framework.TestSuite
+ private boolean isPublicTestMethod(Method m) {
+ return isTestMethod(m) && Modifier.isPublic(m.getModifiers());
+ }
+
+ // copied from junit.framework.TestSuite
+ private boolean isTestMethod(Method m) {
+ return m.getParameterTypes().length == 0 && m.getName().startsWith("test")
+ && m.getReturnType().equals(Void.TYPE);
+ }
+
+ /**
+ * Utility method for logging debug messages. Only actually logs a message if TAG is marked
+ * as loggable to limit log spam during normal use.
+ */
+ private void logDebug(String msg) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, msg);
+ }
+ }
+}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarControllerTest.java
new file mode 100644
index 0000000..901d200
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarControllerTest.java
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.navigationbar.car;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableResources;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.statusbar.car.hvac.HvacController;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import dagger.Lazy;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class CarNavigationBarControllerTest extends SysuiTestCase {
+
+ private CarNavigationBarController mCarNavigationBar;
+ private NavigationBarViewFactory mNavigationBarViewFactory;
+ private Lazy<HvacController> mHvacControllerLazy;
+ private TestableResources mTestableResources;
+
+ @Mock
+ private HvacController mHvacController;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mNavigationBarViewFactory = new NavigationBarViewFactory(mContext);
+ mHvacControllerLazy = () -> mHvacController;
+ mTestableResources = mContext.getOrCreateTestableResources();
+
+ // Needed to inflate top navigation bar.
+ mDependency.injectMockDependency(DarkIconDispatcher.class);
+ mDependency.injectMockDependency(StatusBarIconController.class);
+ }
+
+ @Test
+ public void testConnectToHvac_callsConnect() {
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+
+ mCarNavigationBar.connectToHvac();
+
+ verify(mHvacController).connectToCarService();
+ }
+
+ @Test
+ public void testRemoveAllFromHvac_callsRemoveAll() {
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+
+ mCarNavigationBar.removeAllFromHvac();
+
+ verify(mHvacController).removeAllComponents();
+ }
+
+ @Test
+ public void testGetBottomWindow_bottomDisabled_returnsNull() {
+ mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, false);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+
+ ViewGroup window = mCarNavigationBar.getBottomWindow();
+
+ assertThat(window).isNull();
+ }
+
+ @Test
+ public void testGetBottomWindow_bottomEnabled_returnsWindow() {
+ mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+
+ ViewGroup window = mCarNavigationBar.getBottomWindow();
+
+ assertThat(window).isNotNull();
+ }
+
+ @Test
+ public void testGetBottomWindow_bottomEnabled_calledTwice_returnsSameWindow() {
+ mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+
+ ViewGroup window1 = mCarNavigationBar.getBottomWindow();
+ ViewGroup window2 = mCarNavigationBar.getBottomWindow();
+
+ assertThat(window1).isEqualTo(window2);
+ }
+
+ @Test
+ public void testGetLeftWindow_leftDisabled_returnsNull() {
+ mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, false);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+ ViewGroup window = mCarNavigationBar.getLeftWindow();
+ assertThat(window).isNull();
+ }
+
+ @Test
+ public void testGetLeftWindow_leftEnabled_returnsWindow() {
+ mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+
+ ViewGroup window = mCarNavigationBar.getLeftWindow();
+
+ assertThat(window).isNotNull();
+ }
+
+ @Test
+ public void testGetLeftWindow_leftEnabled_calledTwice_returnsSameWindow() {
+ mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+
+ ViewGroup window1 = mCarNavigationBar.getLeftWindow();
+ ViewGroup window2 = mCarNavigationBar.getLeftWindow();
+
+ assertThat(window1).isEqualTo(window2);
+ }
+
+ @Test
+ public void testGetRightWindow_rightDisabled_returnsNull() {
+ mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, false);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+
+ ViewGroup window = mCarNavigationBar.getRightWindow();
+
+ assertThat(window).isNull();
+ }
+
+ @Test
+ public void testGetRightWindow_rightEnabled_returnsWindow() {
+ mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+
+ ViewGroup window = mCarNavigationBar.getRightWindow();
+
+ assertThat(window).isNotNull();
+ }
+
+ @Test
+ public void testGetRightWindow_rightEnabled_calledTwice_returnsSameWindow() {
+ mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+
+ ViewGroup window1 = mCarNavigationBar.getRightWindow();
+ ViewGroup window2 = mCarNavigationBar.getRightWindow();
+
+ assertThat(window1).isEqualTo(window2);
+ }
+
+ @Test
+ public void testSetBottomWindowVisibility_setTrue_isVisible() {
+ mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+
+ ViewGroup window = mCarNavigationBar.getBottomWindow();
+ mCarNavigationBar.setBottomWindowVisibility(View.VISIBLE);
+
+ assertThat(window.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void testSetBottomWindowVisibility_setFalse_isGone() {
+ mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+
+ ViewGroup window = mCarNavigationBar.getBottomWindow();
+ mCarNavigationBar.setBottomWindowVisibility(View.GONE);
+
+ assertThat(window.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void testSetLeftWindowVisibility_setTrue_isVisible() {
+ mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+
+ ViewGroup window = mCarNavigationBar.getLeftWindow();
+ mCarNavigationBar.setLeftWindowVisibility(View.VISIBLE);
+
+ assertThat(window.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void testSetLeftWindowVisibility_setFalse_isGone() {
+ mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+
+ ViewGroup window = mCarNavigationBar.getLeftWindow();
+ mCarNavigationBar.setLeftWindowVisibility(View.GONE);
+
+ assertThat(window.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void testSetRightWindowVisibility_setTrue_isVisible() {
+ mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+
+ ViewGroup window = mCarNavigationBar.getRightWindow();
+ mCarNavigationBar.setRightWindowVisibility(View.VISIBLE);
+
+ assertThat(window.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void testSetRightWindowVisibility_setFalse_isGone() {
+ mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+
+ ViewGroup window = mCarNavigationBar.getRightWindow();
+ mCarNavigationBar.setRightWindowVisibility(View.GONE);
+
+ assertThat(window.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void testRegisterBottomBarTouchListener_createViewFirst_registrationSuccessful() {
+ mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+
+ CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
+ View.OnTouchListener controller = bottomBar.getStatusBarWindowTouchListener();
+ assertThat(controller).isNull();
+ mCarNavigationBar.registerBottomBarTouchListener(mock(View.OnTouchListener.class));
+ controller = bottomBar.getStatusBarWindowTouchListener();
+
+ assertThat(controller).isNotNull();
+ }
+
+ @Test
+ public void testRegisterBottomBarTouchListener_registerFirst_registrationSuccessful() {
+ mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+
+ mCarNavigationBar.registerBottomBarTouchListener(mock(View.OnTouchListener.class));
+ CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
+ View.OnTouchListener controller = bottomBar.getStatusBarWindowTouchListener();
+
+ assertThat(controller).isNotNull();
+ }
+
+ @Test
+ public void testRegisterNotificationController_createViewFirst_registrationSuccessful() {
+ mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+
+ CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
+ CarNavigationBarController.NotificationsShadeController controller =
+ bottomBar.getNotificationsPanelController();
+ assertThat(controller).isNull();
+ mCarNavigationBar.registerNotificationController(
+ mock(CarNavigationBarController.NotificationsShadeController.class));
+ controller = bottomBar.getNotificationsPanelController();
+
+ assertThat(controller).isNotNull();
+ }
+
+ @Test
+ public void testRegisterNotificationController_registerFirst_registrationSuccessful() {
+ mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+
+ mCarNavigationBar.registerNotificationController(
+ mock(CarNavigationBarController.NotificationsShadeController.class));
+ CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
+ CarNavigationBarController.NotificationsShadeController controller =
+ bottomBar.getNotificationsPanelController();
+
+ assertThat(controller).isNotNull();
+ }
+
+ @Test
+ public void testShowAllKeyguardButtons_bottomEnabled_bottomKeyguardButtonsVisible() {
+ mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+ CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
+ View bottomKeyguardButtons = bottomBar.findViewById(R.id.lock_screen_nav_buttons);
+
+ mCarNavigationBar.showAllKeyguardButtons(/* isSetUp= */ true);
+
+ assertThat(bottomKeyguardButtons.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void testShowAllKeyguardButtons_bottomEnabled_bottomNavButtonsGone() {
+ mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+ CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
+ View bottomButtons = bottomBar.findViewById(R.id.nav_buttons);
+
+ mCarNavigationBar.showAllKeyguardButtons(/* isSetUp= */ true);
+
+ assertThat(bottomButtons.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void testHideAllKeyguardButtons_bottomEnabled_bottomKeyguardButtonsGone() {
+ mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+ CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
+ View bottomKeyguardButtons = bottomBar.findViewById(R.id.lock_screen_nav_buttons);
+
+ mCarNavigationBar.showAllKeyguardButtons(/* isSetUp= */ true);
+ assertThat(bottomKeyguardButtons.getVisibility()).isEqualTo(View.VISIBLE);
+ mCarNavigationBar.hideAllKeyguardButtons(/* isSetUp= */ true);
+
+ assertThat(bottomKeyguardButtons.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void testHideAllKeyguardButtons_bottomEnabled_bottomNavButtonsVisible() {
+ mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+ CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
+ View bottomButtons = bottomBar.findViewById(R.id.nav_buttons);
+
+ mCarNavigationBar.showAllKeyguardButtons(/* isSetUp= */ true);
+ assertThat(bottomButtons.getVisibility()).isEqualTo(View.GONE);
+ mCarNavigationBar.hideAllKeyguardButtons(/* isSetUp= */ true);
+
+ assertThat(bottomButtons.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void testToggleAllNotificationsUnseenIndicator_bottomEnabled_hasUnseen_setCorrectly() {
+ mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+ CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
+ CarNavigationButton notifications = bottomBar.findViewById(R.id.notifications);
+
+ boolean hasUnseen = true;
+ mCarNavigationBar.toggleAllNotificationsUnseenIndicator(/* isSetUp= */ true,
+ hasUnseen);
+
+ assertThat(notifications.getUnseen()).isTrue();
+ }
+
+ @Test
+ public void testToggleAllNotificationsUnseenIndicator_bottomEnabled_noUnseen_setCorrectly() {
+ mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
+ mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
+ mHvacControllerLazy);
+ CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
+ CarNavigationButton notifications = bottomBar.findViewById(R.id.notifications);
+
+ boolean hasUnseen = false;
+ mCarNavigationBar.toggleAllNotificationsUnseenIndicator(/* isSetUp= */ true,
+ hasUnseen);
+
+ assertThat(notifications.getUnseen()).isFalse();
+ }
+}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 48d34ae..af96982 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -129,17 +129,17 @@
}
@Override
- protected int enforceReadPermissionInner(Uri uri, String callingPkg, IBinder callerToken)
- throws SecurityException {
+ protected int enforceReadPermissionInner(Uri uri, String callingPkg,
+ @Nullable String featureId, IBinder callerToken) throws SecurityException {
enforceShellRestrictions();
- return super.enforceReadPermissionInner(uri, callingPkg, callerToken);
+ return super.enforceReadPermissionInner(uri, callingPkg, featureId, callerToken);
}
@Override
- protected int enforceWritePermissionInner(Uri uri, String callingPkg, IBinder callerToken)
- throws SecurityException {
+ protected int enforceWritePermissionInner(Uri uri, String callingPkg,
+ @Nullable String featureId, IBinder callerToken) throws SecurityException {
enforceShellRestrictions();
- return super.enforceWritePermissionInner(uri, callingPkg, callerToken);
+ return super.enforceWritePermissionInner(uri, callingPkg, featureId, callerToken);
}
public void updateVolumes() {
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
index c4df2e8..b9daf7f 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
@@ -406,8 +406,8 @@
return null;
}
try {
- return provider.call(context.getPackageName(), uri.getAuthority(),
- method, uri.toString(), bundle);
+ return provider.call(context.getPackageName(), context.getFeatureId(),
+ uri.getAuthority(), method, uri.toString(), bundle);
} catch (RemoteException e) {
return null;
}
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 245ca14..f25b5eb 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Beskikbaar via %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Tik om aan te meld"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Gekoppel, geen internet nie"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Daar kan nie by private DNS-bediener ingegaan word nie"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Beperkte verbinding"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Geen internet nie"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Aanmelding word vereis"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index bfd3196..6332c848 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"በ%1$s በኩል የሚገኝ"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"ለመመዝገብ መታ ያድርጉ"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"ተገናኝቷል፣ ምንም በይነመረብ የለም"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"የግል ዲኤንኤስ አገልጋይ ሊደረስበት አይችልም"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"የተገደበ ግንኙነት"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"ምንም በይነመረብ የለም"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"ወደ መለያ መግባት ያስፈልጋል"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 8b67b0b..8c72527 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"متوفرة عبر %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"انقر للاشتراك."</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"متصلة ولكن بلا إنترنت"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"لا يمكن الوصول إلى خادم أسماء نظام نطاقات خاص"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"اتصال محدود"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"لا يتوفر اتصال إنترنت."</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"يلزم تسجيل الدخول"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index f3ca337..a7ea1e0 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -43,6 +43,8 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$sৰ মাধ্যমেৰে উপলব্ধ"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"ছাইন আপ কৰিবলৈ টিপক"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"সংযোজিত, ইণ্টাৰনেট নাই"</string>
+ <!-- no translation found for private_dns_broken (7356676011023412490) -->
+ <skip />
<string name="wifi_limited_connection" msgid="7717855024753201527">"ইণ্টাৰনেট সংযোগ সীমিত"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"ইণ্টাৰনেট সংযোগ নাই"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"ছাইন ইন কৰা দৰকাৰী"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index d35dfe8..cb7db78 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s vasitəsilə əlçatandır"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Qeydiyyatdan keçmək üçün klikləyin"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Qoşuludur, internet yoxdur"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Özəl DNS serverinə giriş mümkün deyil"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Məhdud bağlantı"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"İnternet yoxdur"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Giriş tələb olunur"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 448de4b..75feb32 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Dostupna je preko pristupne tačke %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Dodirnite da biste se registrovali"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Veza je uspostavljena, nema interneta"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Pristup privatnom DNS serveru nije uspeo"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Ograničena veza"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Nema interneta"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Treba da se prijavite"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index d68c0f3..677aa24 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Даступна праз %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Націсніце, каб зарэгістравацца"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Падключана, без доступу да інтэрнэту"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Не ўдалося атрымаць доступ да прыватнага DNS-сервера"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Абмежаваныя магчымасці падключэння"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Не падключана да інтэрнэту"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Трэба выканаць уваход"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index cb99f64..1620422 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Мрежата е достъпна през „%1$s“"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Докоснете, за да се регистрирате"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Установена е връзка – няма достъп до интернет"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Не може да се осъществи достъп до частния DNS сървър"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Ограничена връзка"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Няма връзка с интернет"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Изисква се вход в профила"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index f2f4f52..b1e37a6 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s এর মাধ্যমে উপলব্ধ"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"সাইন-আপ করতে ট্যাপ করুন"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"কানেক্ট, ইন্টারনেট নেই"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"ব্যক্তিগত ডিএনএস সার্ভার অ্যাক্সেস করা যাবে না"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"সীমিত কানেকশন"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"ইন্টারনেট কানেকশন নেই"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"সাইন-ইন করা দরকার"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 45b8dd9..911a831 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Dostupan preko %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Dodirnite za prijavu"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Povezano, nema interneta"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Nije moguće pristupiti privatnom DNS serveru"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Ograničena veza"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Nema internetske veze"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Potrebna je prijava"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index b624df0..58c2b67 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Disponible mitjançant %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Toca per registrar-te"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Connectada, sense Internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"No es pot accedir al servidor DNS privat"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Connexió limitada"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Sense connexió a Internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Cal iniciar la sessió"</string>
@@ -237,7 +238,7 @@
<string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="7234956835280563341">"Activa el còdec d\'àudio per Bluetooth\nSelecció: mode de canal"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Còdec LDAC d\'àudio per Bluetooth: qualitat de reproducció"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="6893955536658137179">"Activa l\'LDAC d\'àudio per Bluetooth\nSelecció de còdec: qualitat de reproducció"</string>
- <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"S\'està reproduint en temps real: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
+ <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Reproducció en continu: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
<string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS privat"</string>
<string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Selecciona el mode de DNS privat"</string>
<string name="private_dns_mode_off" msgid="8236575187318721684">"Desactivat"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 042e12a..3c3d5b8 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Dostupné prostřednictvím %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Klepnutím se zaregistrujete"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Připojeno, není k dispozici internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Nelze získat přístup k soukromému serveru DNS"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Omezené připojení"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Nejste připojeni k internetu"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Je vyžadováno přihlášení"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 4723293..bf5d6cf 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Tilgængelig via %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Tryk for at registrere dig"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Tilsluttet – intet internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Der er ikke adgang til den private DNS-server"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Begrænset forbindelse"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Intet internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Login er påkrævet"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 4f5a965..a6dbd5a 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Verfügbar über %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Zum Anmelden tippen"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Verbunden, kein Internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Auf den privaten DNS-Server kann nicht zugegriffen werden"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Eingeschränkte Verbindung"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Kein Internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Anmeldung erforderlich"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 753dea8..fdba74a 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Διαθέσιμο μέσω %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Πατήστε για εγγραφή"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Συνδέθηκε, χωρίς σύνδεση στο διαδίκτυο"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Δεν είναι δυνατή η πρόσβαση στον ιδιωτικό διακομιστή DNS."</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Περιορισμένη σύνδεση"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Δεν υπάρχει σύνδεση στο διαδίκτυο"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Απαιτείται σύνδεση"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index dd3d278..581adf8 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Available via %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Tap to sign up"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Connected, no Internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Private DNS server cannot be accessed"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Limited connection"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"No Internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Sign-in required"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index dd3d278..581adf8 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Available via %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Tap to sign up"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Connected, no Internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Private DNS server cannot be accessed"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Limited connection"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"No Internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Sign-in required"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index dd3d278..581adf8 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Available via %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Tap to sign up"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Connected, no Internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Private DNS server cannot be accessed"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Limited connection"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"No Internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Sign-in required"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index dd3d278..581adf8 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Available via %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Tap to sign up"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Connected, no Internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Private DNS server cannot be accessed"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Limited connection"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"No Internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Sign-in required"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index d9f61d8..e75d7bc 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Available via %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Tap to sign up"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Connected, no internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Private DNS server cannot be accessed"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Limited connection"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"No internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Sign in required"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 30cb0a1..97cce55 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Disponible a través de %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Presiona para registrarte"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Conectado pero sin conexión a Internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"No se puede acceder al servidor DNS privado"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Conexión limitada"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Sin Internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Acceso obligatorio"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 32905df..7ba1a94 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Disponible a través de %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Toca para registrarte"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Conexión sin Internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"No se ha podido acceder al servidor DNS privado"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Conexión limitada"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Sin Internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Debes iniciar sesión"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 79b8a84..0e98752 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Saadaval üksuse %1$s kaudu"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Puudutage registreerumiseks"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Ühendatud, Interneti-ühendus puudub"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Privaatsele DNS-serverile ei pääse juurde"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Piiratud ühendus"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Interneti-ühendus puudub"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Nõutav on sisselogimine"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 38ae9c2..872e9a5 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s bidez erabilgarri"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Sakatu erregistratzeko"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Konektatuta; ezin da atzitu Internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Ezin da atzitu DNS zerbitzari pribatua"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Konexio mugatua"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Ez dago Interneteko konexiorik"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Saioa hasi behar da"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index a883d6cd..6e281fe 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"در دسترس از طریق %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"برای ثبتنام ضربه بزنید"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"متصل، بدون اینترنت"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"سرور DNS خصوصی قابل دسترسی نیست"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"اتصال محدود"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"عدم دسترسی به اینترنت"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"ورود به سیستم لازم است"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 929e101..8c3630a 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Käytettävissä seuraavan kautta: %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Rekisteröidy napauttamalla"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Yhdistetty, ei internetyhteyttä"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Ei pääsyä yksityiselle DNS-palvelimelle"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Rajallinen yhteys"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Ei internetyhteyttä"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Sisäänkirjautuminen vaaditaan"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index d3bfc96..6c5834a 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Accessible par %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Toucher pour vous connecter"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Connecté, aucun accès à Internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Impossible d\'accéder au serveur DNS privé"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Connexion limitée"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Aucune connexion Internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Connexion requise"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index b5706ce..508556e 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Disponible via %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Appuyez ici pour vous connecter"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Connecté, aucun accès à Internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Impossible d\'accéder au serveur DNS privé"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Connexion limitée"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Aucun accès à Internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Connexion requise"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 2aa1604..1a3ae3d 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Dispoñible a través de %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Toca para rexistrarte"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Conexión sen Internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Non se puido acceder ao servidor DNS privado"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Pouca conexión"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Non hai conexión a Internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"É obrigatorio iniciar sesión"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index c2cc4c5..b3f5185 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s દ્વારા ઉપલબ્ધ"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"સાઇન અપ કરવા માટે ટૅપ કરો"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"કનેક્ટ કર્યું, કોઈ ઇન્ટરનેટ નથી"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"ખાનગી DNS સર્વર ઍક્સેસ કરી શકાતા નથી"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"મર્યાદિત કનેક્શન"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"ઇન્ટરનેટ ઍક્સેસ નથી"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"સાઇન ઇન આવશ્યક"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 66c7a16..454773c 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s के द्वारा उपलब्ध"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"साइन अप करने के लिए टैप करें"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"कनेक्ट हो गया है, लेकिन इंटरनेट नहीं है"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"निजी डीएनएस सर्वर को ऐक्सेस नहीं किया जा सकता"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"सीमित कनेक्शन"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"इंटरनेट कनेक्शन नहीं है"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"साइन इन करना ज़रूरी है"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 0fc16f8..0ec154b 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Dostupno putem %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Dodirnite da biste se registrirali"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Povezano, bez interneta"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Nije moguće pristupiti privatnom DNS poslužitelju"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Ograničena veza"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Nema interneta"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Obavezna prijava"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 1388dbb..4a0af7d 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Elérhető a következőn keresztül: %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Koppintson a regisztrációhoz"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Csatlakozva, nincs internet-hozzáférés"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"A privát DNS-kiszolgálóhoz nem lehet hozzáférni"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Korlátozott kapcsolat"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Nincs internetkapcsolat"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Bejelentkezést igényel"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 2145adb..cc0ecb8 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Հասանելի է %1$s-ի միջոցով"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Հպեք՝ գրանցվելու համար"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Միացված է, սակայն ինտերնետ կապ չկա"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Մասնավոր DNS սերվերն անհասանելի է"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Սահմանափակ կապ"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Ինտերնետ կապ չկա"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Անհրաժեշտ է մուտք գործել"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 7ebe6b7..0733c1f 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Tersedia melalui %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Ketuk untuk mendaftar"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Tersambung, tidak ada internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Server DNS pribadi tidak dapat diakses"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Koneksi terbatas"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Tidak ada internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Perlu login"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index eede117..28ed8fc 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Í boði í gegnum %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Ýttu til að skrá þig"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Tengt, enginn netaðgangur"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Ekki næst í DNS-einkaþjón"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Takmörkuð tenging"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Engin nettenging"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Innskráningar krafist"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 2c64ec2..b0569b0 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Disponibile tramite %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Tocca per registrarti"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Connesso, senza Internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Non è possibile accedere al server DNS privato"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Connessione limitata"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Nessuna connessione a Internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Accesso richiesto"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 30d6a4a..f7d4fcd 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -43,6 +43,8 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"זמינה דרך %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"יש להקיש כדי להירשם"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"מחובר. אין אינטרנט"</string>
+ <!-- no translation found for private_dns_broken (7356676011023412490) -->
+ <skip />
<string name="wifi_limited_connection" msgid="7717855024753201527">"חיבור מוגבל"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"אין אינטרנט"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"נדרשת כניסה"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 6cb5514..4d5c86c 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s経由で使用可能"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"タップして登録してください"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"接続済み、インターネット接続なし"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"プライベート DNS サーバーにアクセスできません"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"接続が制限されています"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"インターネット未接続"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"ログインが必要"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 4c78a38..20342bc 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"ხელმისაწვდომია %1$s-ით"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"შეეხეთ რეგისტრაციისთვის"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"დაკავშირებულია, ინტერნეტის გარეშე"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"პირად DNS სერვერზე წვდომა შეუძლებელია"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"შეზღუდული კავშირი"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"ინტერნეტ-კავშირი არ არის"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"აუცილებელია სისტემაში შესვლა"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index ce08657..7b6d29e 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s арқылы қолжетімді"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Тіркелу үшін түртіңіз."</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Қосылған, интернет жоқ"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Жеке DNS серверіне кіру мүмкін емес."</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Шектеулі байланыс"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Интернетпен байланыс жоқ"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Есептік жазбаға кіру керек"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 43c9282..115be8e 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"មានតាមរយៈ %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"ចុចដើម្បីចុះឈ្មោះ"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"បានភ្ជាប់ ប៉ុន្តែគ្មានអ៊ីនធឺណិតទេ"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"មិនអាចចូលប្រើម៉ាស៊ីនមេ DNS ឯកជនបានទេ"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"ការតភ្ជាប់មានកម្រិត"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"គ្មានអ៊ីនធឺណិតទេ"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"តម្រូវឱ្យចូលគណនី"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 253104b..ac8bfb1 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -43,6 +43,8 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s ಮೂಲಕ ಲಭ್ಯವಿದೆ"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"ಸೈನ್ ಅಪ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆ, ಇಂಟರ್ನೆಟ್ ಇಲ್ಲ"</string>
+ <!-- no translation found for private_dns_broken (7356676011023412490) -->
+ <skip />
<string name="wifi_limited_connection" msgid="7717855024753201527">"ಸೀಮಿತ ಸಂಪರ್ಕ"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"ಇಂಟರ್ನೆಟ್ ಇಲ್ಲ"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"ಸೈನ್ ಇನ್ ಮಾಡುವ ಅಗತ್ಯವಿದೆ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index ac44c0d..4e54310 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s을(를) 통해 사용 가능"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"탭하여 가입"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"연결됨, 인터넷 사용 불가"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"비공개 DNS 서버에 액세스할 수 없습니다."</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"제한된 연결"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"인터넷 연결 없음"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"로그인 필요"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index dd1ff30..e891b5a 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s аркылуу жеткиликтүү"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Катталуу үчүн таптап коюңуз"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Туташып турат, Интернет жок"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Жеке DNS сервери жеткиликсиз"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Байланыш чектелген"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Интернет жок"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Аккаунтка кирүү талап кылынат"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 28e8111..406a42b 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"ມີໃຫ້ຜ່ານ %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"ແຕະເພື່ອສະໝັກ"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"ເຊື່ອມຕໍ່ແລ້ວ, ບໍ່ມີອິນເຕີເນັດ"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"ບໍ່ສາມາດເຂົ້າເຖິງເຊີບເວີ DNS ສ່ວນຕົວໄດ້"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"ການເຊື່ອມຕໍ່ຈຳກັດ"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"ບໍ່ມີອິນເຕີເນັດ"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"ຈຳເປັນຕ້ອງເຂົ້າສູ່ລະບົບ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 93fcaa7..b305fd90 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Pasiekiama naudojant „%1$s“"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Palieskite, kad prisiregistruotumėte"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Prisijungta, nėra interneto"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Privataus DNS serverio negalima pasiekti"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Ribotas ryšys"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Nėra interneto ryšio"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Reikia prisijungti"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index c12d097..c9e2c11 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Pieejams, izmantojot %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Pieskarieties, lai reģistrētos"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Savienojums izveidots, nav piekļuves internetam"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Nevar piekļūt privātam DNS serverim."</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Ierobežots savienojums"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Nav piekļuves internetam"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Nepieciešama pierakstīšanās"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 9a76a0a..e06d414 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Достапно преку %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Допрете за да се регистрирате"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Поврзана, нема интернет"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Не може да се пристапи до приватниот DNS-сервер"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Ограничена врска"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Нема интернет"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Потребно е најавување"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index bb76295..36e632a 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s വഴി ലഭ്യം"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"സൈൻ അപ്പ് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"കണക്റ്റ് ചെയ്തു, ഇന്റർനെറ്റ് ഇല്ല"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"സ്വകാര്യ DNS സെർവർ ആക്സസ് ചെയ്യാനാവില്ല"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"പരിമിത കണക്ഷൻ"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"ഇന്റർനെറ്റ് ഇല്ല"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"സൈൻ ഇൻ ചെയ്യേണ്ടത് ആവശ്യമാണ്"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 65a8ca6..1a5a0af 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s-р боломжтой"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Бүртгүүлэхийн тулд товшино уу"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Холбогдсон хэдий ч интернет алга"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Хувийн DNS серверт хандах боломжгүй байна"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Хязгаарлагдмал холболт"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Интернэт алга"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Нэвтрэх шаардлагатай"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 3d75ad6..7510107 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -43,6 +43,8 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s द्वारे उपलब्ध"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"साइन अप करण्यासाठी टॅप करा"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"कनेक्ट केले, इंटरनेट नाही"</string>
+ <!-- no translation found for private_dns_broken (7356676011023412490) -->
+ <skip />
<string name="wifi_limited_connection" msgid="7717855024753201527">"मर्यादित कनेक्शन"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"इंटरनेट नाही"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"साइन इन करणे आवश्यक आहे"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 0b2a4b0..82bd697 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Tersedia melalui %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Ketik untuk daftar"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Disambungkan, tiada Internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Pelayan DNS peribadi tidak boleh diakses"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Sambungan terhad"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Tiada Internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Log masuk diperlukan"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 6fde69a..9636f06 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s မှတစ်ဆင့်ရနိုင်သည်"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"အကောင့်ဖွင့်ရန် တို့ပါ"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"ချိတ်ဆက်ထားသည်၊ အင်တာနက်မရှိ"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"သီးသန့် ဒီအန်အက်စ် (DNS) ဆာဗာကို သုံး၍မရပါ။"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"ချိတ်ဆက်မှု ကန့်သတ်ထားသည်"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"အင်တာနက် မရှိပါ"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"လက်မှတ်ထိုးဝင်ရန် လိုအပ်သည်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 66ad20e..99af7c8 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Tilgjengelig via %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Trykk for å registrere deg"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Tilkoblet – ingen Internett-tilgang"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Den private DNS-tjeneren kan ikke nås"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Begrenset tilkobling"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Ingen internettilkobling"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Pålogging kreves"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 860e9bf..cdf2c7d 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s मार्फत उपलब्ध"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"साइन अप गर्न ट्याप गर्नुहोस्"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"जडान गरियो तर इन्टरनेट छैन"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"निजी DNS सर्भरमाथि पहुँच प्राप्त गर्न सकिँदैन"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"सीमित जडान"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"इन्टरनेटमाथिको पहुँच छैन"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"साइन इन गर्न आवश्यक छ"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 2fff3283..709e98d 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Beschikbaar via %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Tik om aan te melden"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Verbonden, geen internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Geen toegang tot privé-DNS-server"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Beperkte verbinding"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Geen internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Inloggen vereist"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index ff2a534..abe0198 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -43,6 +43,8 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s ମାଧ୍ୟମରେ ଉପଲବ୍ଧ"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"ସାଇନ୍ ଅପ୍ ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"ସଂଯୁକ୍ତ, ଇଣ୍ଟର୍ନେଟ୍ ନାହିଁ"</string>
+ <!-- no translation found for private_dns_broken (7356676011023412490) -->
+ <skip />
<string name="wifi_limited_connection" msgid="7717855024753201527">"ସୀମିତ ସଂଯୋଗ"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"କୌଣସି ଇଣ୍ଟରନେଟ୍ ନାହିଁ"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"ସାଇନ୍-ଇନ୍ ଆବଶ୍ୟକ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 0cf95753..b372185 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s ਰਾਹੀਂ ਉਪਲਬਧ"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"ਸਾਈਨ-ਅੱਪ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"ਕਨੈਕਟ ਕੀਤਾ, ਕੋਈ ਇੰਟਰਨੈੱਟ ਨਹੀਂ"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"ਨਿੱਜੀ ਡੋਮੇਨ ਨਾਮ ਪ੍ਰਣਾਲੀ (DNS) ਸਰਵਰ \'ਤੇ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"ਸੀਮਤ ਕਨੈਕਸ਼ਨ"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"ਇੰਟਰਨੈੱਟ ਨਹੀਂ"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"ਸਾਈਨ-ਇਨ ਲੋੜੀਂਦਾ ਹੈ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index caed8c3..e7a8f22 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -43,6 +43,8 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Dostępne przez %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Kliknij, by się zarejestrować"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Połączono, brak internetu"</string>
+ <!-- no translation found for private_dns_broken (7356676011023412490) -->
+ <skip />
<string name="wifi_limited_connection" msgid="7717855024753201527">"Ograniczone połączenie"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Brak internetu"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Musisz się zalogować"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 04a24f1..6e75fba 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Disponível via %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Toque para se inscrever"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Conectada, sem Internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Não é possível acessar o servidor DNS privado"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Conexão limitada"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Sem Internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"É necessário fazer login"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index a206d1a..4ee0a17 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Disponível através de %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Toque para se inscrever"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Ligado, sem Internet."</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Não é possível aceder ao servidor DNS."</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Ligação limitada"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Sem Internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"É necessário iniciar sessão"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 04a24f1..6e75fba 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Disponível via %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Toque para se inscrever"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Conectada, sem Internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Não é possível acessar o servidor DNS privado"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Conexão limitada"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Sem Internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"É necessário fazer login"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 76a56e5..c5725d3 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Disponibilă prin %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Atingeți pentru a vă înscrie"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Conectată, fără internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Serverul DNS privat nu poate fi accesat"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Conexiune limitată"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Fără conexiune la internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Trebuie să vă conectați"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 6e1d29a..6e98601 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Доступно через %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Нажмите, чтобы зарегистрироваться"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Подключено, без доступа к Интернету"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Доступа к частному DNS-серверу нет."</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Подключение к сети ограничено."</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Нет подключения к Интернету"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Требуется выполнить вход."</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 186c23a..0f67a65 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s හරහා ලබා ගැනීමට හැකිය"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"ලියාපදිංචි වීමට තට්ටු කරන්න"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"සම්බන්ධයි, අන්තර්ජාලය නැත"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"පුද්ගලික DNS සේවාදායකයට ප්රවේශ වීමට නොහැකිය"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"සීමිත සම්බන්ධතාව"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"අන්තර්ජාලය නැත"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"පිරීම අවශ්යයි"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 9559975..e4b4848 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"K dispozícii prostredníctvom %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Prihláste sa klepnutím"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Pripojené, žiadny internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"K súkromnému serveru DNS sa nepodarilo získať prístup"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Obmedzené pripojenie"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Žiadny internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Vyžaduje sa prihlásenie"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 62e4ad1..3e181c2 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Na voljo prek: %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Dotaknite se, če se želite registrirati"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Vzpostavljena povezava, brez interneta"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Do zasebnega strežnika DNS ni mogoče dostopati"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Omejena povezava"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Brez internetne povezave"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Zahtevana je prijava"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 0498f31..5380302 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"E mundshme përmes %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Trokit për t\'u regjistruar"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"U lidh, por nuk ka internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Serveri privat DNS nuk mund të qaset"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Lidhje e kufizuar"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Nuk ka internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Kërkohet identifikimi"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 3157dee..d68c9e8 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Доступна је преко приступне тачке %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Додирните да бисте се регистровали"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Веза је успостављена, нема интернета"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Приступ приватном DNS серверу није успео"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Ограничена веза"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Нема интернета"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Треба да се пријавите"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 31517b8..45841f0 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Tillgängligt via %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Tryck för att logga in"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Ansluten, inget internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Det går inte att komma åt den privata DNS-servern."</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Begränsad anslutning"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Inget internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Inloggning krävs"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index d73084f..a8e73d7 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Inapatikana kupitia %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Gusa ili ujisajili"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Imeunganishwa, hakuna intaneti"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Seva ya faragha ya DNS haiwezi kufikiwa"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Muunganisho hafifu"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Hakuna intaneti"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Unahitaji kuingia katika akaunti"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index de70d08..83fe954 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s வழியாகக் கிடைக்கிறது"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"பதிவு செய்யத் தட்டவும்"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"இணைக்கப்பட்டுள்ளது, ஆனால் இண்டர்நெட் இல்லை"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"தனிப்பட்ட DNS சேவையகத்தை அணுக இயலாது"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"வரம்பிற்கு உட்பட்ட இணைப்பு"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"இணைய இணைப்பு இல்லை"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"உள்நுழைய வேண்டும்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 03ae94f..ad0f673 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s ద్వారా అందుబాటులో ఉంది"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"సైన్ అప్ చేయడానికి నొక్కండి"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"కనెక్ట్ చేయబడింది, ఇంటర్నెట్ లేదు"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"ప్రైవేట్ DNS సర్వర్ను యాక్సెస్ చేయడం సాధ్యపడదు"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"పరిమిత కనెక్షన్"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"ఇంటర్నెట్ లేదు"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"సైన్ ఇన్ చేయాలి"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 1cfe45e..c9e232d 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"พร้อมใช้งานผ่านทาง %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"แตะเพื่อลงชื่อสมัครใช้"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"เชื่อมต่อแล้ว ไม่พบอินเทอร์เน็ต"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"เข้าถึงเซิร์ฟเวอร์ DNS ไม่ได้"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"การเชื่อมต่อที่จำกัด"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"ไม่มีอินเทอร์เน็ต"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"ต้องลงชื่อเข้าใช้"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 8b3118b..74307f8 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Available sa pamamagitan ng %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"I-tap para mag-sign up"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Nakakonekta, walang internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Hindi ma-access ang pribadong DNS server"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Limitadong koneksyon"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Walang internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Kinakailangang mag-sign in"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 5891c79..b95d941 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s üzerinden kullanılabilir"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Kaydolmak için dokunun"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Bağlı, internet yok"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Gizli DNS sunucusuna erişilemiyor"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Sınırlı bağlantı"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"İnternet yok"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Oturum açılması gerekiyor"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 0bca307..61a0c3e 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Доступ через %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Торкніться, щоб увійти"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Під’єднано, але немає доступу до Інтернету"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Немає доступу до приватного DNS-сервера"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Обмежене з’єднання"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Немає Інтернету"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Потрібно ввійти в обліковий запис"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 1e27b48..f21e891 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -43,6 +43,8 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"دستیاب بذریعہ %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"سائن اپ کے لیے تھپتھپائیں"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"منسلک، انٹرنیٹ نہیں ہے"</string>
+ <!-- no translation found for private_dns_broken (7356676011023412490) -->
+ <skip />
<string name="wifi_limited_connection" msgid="7717855024753201527">"محدود کنکشن"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"انٹرنیٹ نہیں ہے"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"سائن ان درکار ہے"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 487100c..a0a79e3 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s orqali ishlaydi"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Yozilish uchun bosing"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Ulangan, lekin internet aloqasi yo‘q"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Xususiy DNS server ishlamayapti"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Cheklangan aloqa"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Internet yo‘q"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Hisob bilan kirish zarur"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index c247617..3723b83 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Có sẵn qua %1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Nhấn để đăng ký"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Đã kết nối, không có Internet"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Không thể truy cập máy chủ DNS riêng tư"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Kết nối giới hạn"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Không có Internet"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Yêu cầu đăng nhập"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index dddc107..f1200ee 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"可通过%1$s连接"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"点按即可注册"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"已连接,但无法访问互联网"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"无法访问私人 DNS 服务器"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"网络连接受限"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"无法访问互联网"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"必须登录"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 8560e22..57ab472 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"可透過 %1$s 連線"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"輕按即可登入"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"已連線,但沒有互聯網"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"無法存取私人 DNS 伺服器"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"連線受限"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"沒有互聯網連線"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"必須登入"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 404aa19..630619b 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"可透過 %1$s 使用"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"輕觸即可註冊"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"已連線,沒有網際網路"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"無法存取私人 DNS 伺服器"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"連線能力受限"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"沒有網際網路連線"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"必須登入"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 542332f..ede336e 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -43,6 +43,7 @@
<string name="available_via_passpoint" msgid="1617440946846329613">"Iyatholakala nge-%1$s"</string>
<string name="tap_to_sign_up" msgid="6449724763052579434">"Thepha ukuze ubhalisele"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"Kuxhunyiwe, ayikho i-inthanethi"</string>
+ <string name="private_dns_broken" msgid="7356676011023412490">"Iseva eyimfihlo ye-DNS ayikwazi ukufinyelelwa"</string>
<string name="wifi_limited_connection" msgid="7717855024753201527">"Iqoqo elikhawulelwe"</string>
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Ayikho i-inthanethi"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Ukungena ngemvume kuyadingeka"</string>
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index f7fc0c5..0c3254a 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -228,5 +228,6 @@
VALIDATORS.put(Secure.GLOBAL_ACTIONS_PANEL_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.AWARE_LOCK_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DISPLAY_DENSITY_FORCED, NON_NEGATIVE_INTEGER_VALIDATOR);
+ VALIDATORS.put(Secure.TAP_GESTURE, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index 8fb879d..1e75fe7 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -248,7 +248,7 @@
Bundle args = new Bundle();
args.putInt(Settings.CALL_METHOD_USER_KEY,
ActivityManager.getService().getCurrentUser().id);
- Bundle b = provider.call(resolveCallingPackage(), Settings.AUTHORITY,
+ Bundle b = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
Settings.CALL_METHOD_DELETE_CONFIG, compositeKey, args);
success = (b != null && b.getInt(SettingsProvider.RESULT_ROWS_DELETED) == 1);
} catch (RemoteException e) {
@@ -264,7 +264,7 @@
Bundle args = new Bundle();
args.putInt(Settings.CALL_METHOD_USER_KEY,
ActivityManager.getService().getCurrentUser().id);
- Bundle b = provider.call(resolveCallingPackage(), Settings.AUTHORITY,
+ Bundle b = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
Settings.CALL_METHOD_LIST_CONFIG, null, args);
if (b != null) {
Map<String, String> flagsToValues =
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index fdc987f..7765935 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2115,10 +2115,7 @@
}
private static String getSettingPrefix(Bundle args) {
- String prefix = (args != null) ? args.getString(Settings.CALL_METHOD_PREFIX_KEY) : null;
- // Append '/' to ensure we only match properties with this exact prefix.
- // i.e. "foo" should match "foo/property" but not "foobar/property"
- return prefix != null ? prefix + "/" : null;
+ return (args != null) ? args.getString(Settings.CALL_METHOD_PREFIX_KEY) : null;
}
private static boolean getSettingMakeDefault(Bundle args) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
index 36360a3..3b3ca5b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
@@ -309,7 +309,7 @@
try {
Bundle arg = new Bundle();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- Bundle result = provider.call(resolveCallingPackage(), Settings.AUTHORITY,
+ Bundle result = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
callListCommand, null, arg);
lines.addAll(result.getStringArrayList(SettingsProvider.RESULT_SETTINGS_LIST));
Collections.sort(lines);
@@ -334,7 +334,7 @@
try {
Bundle arg = new Bundle();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- Bundle b = provider.call(resolveCallingPackage(), Settings.AUTHORITY,
+ Bundle b = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
callGetCommand, key, arg);
if (b != null) {
result = b.getPairValue();
@@ -372,7 +372,7 @@
if (makeDefault) {
arg.putBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY, true);
}
- provider.call(resolveCallingPackage(), Settings.AUTHORITY,
+ provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
callPutCommand, key, arg);
} catch (RemoteException e) {
throw new RuntimeException("Failed in IPC", e);
@@ -396,7 +396,7 @@
try {
Bundle arg = new Bundle();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- Bundle result = provider.call(resolveCallingPackage(), Settings.AUTHORITY,
+ Bundle result = provider.call(resolveCallingPackage(), null, Settings.AUTHORITY,
callDeleteCommand, key, arg);
return result.getInt(SettingsProvider.RESULT_ROWS_DELETED);
} catch (RemoteException e) {
@@ -423,7 +423,7 @@
}
String packageName = mPackageName != null ? mPackageName : resolveCallingPackage();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- provider.call(packageName, Settings.AUTHORITY, callResetCommand, null, arg);
+ provider.call(packageName, null, Settings.AUTHORITY, callResetCommand, null, arg);
} catch (RemoteException e) {
throw new RuntimeException("Failed in IPC", e);
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 9255c87..10d990a 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -732,7 +732,8 @@
Settings.Secure.SILENCE_GESTURE,
Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
- Settings.Secure.FACE_UNLOCK_RE_ENROLL);
+ Settings.Secure.FACE_UNLOCK_RE_ENROLL,
+ Settings.Secure.TAP_GESTURE);
@Test
public void systemSettingsBackedUpOrBlacklisted() {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index 3f56ff0..8825f12 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -67,13 +67,11 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (args != null && args.length > 0 && args[0].equals("--config")) {
- dumpConfig(pw);
- return;
- }
-
dumpServices(((SystemUIApplication) getApplication()).getServices(), fd, pw, args);
- dumpConfig(pw);
+
+ if (args == null || args.length == 0 || args[0].equals("--config")) {
+ dumpConfig(pw);
+ }
}
static void dumpServices(
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
index ff4711c..98d7f8b 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -23,6 +23,7 @@
import android.os.Looper
import android.os.Message
import android.os.UserHandle
+import android.text.TextUtils
import android.util.Log
import android.util.SparseArray
import com.android.internal.annotations.VisibleForTesting
@@ -55,7 +56,8 @@
* a given broadcast.
*
* Use only for IntentFilters with actions and optionally categories. It does not support,
- * permissions, schemes or data types. Cannot be used for getting sticky broadcasts.
+ * permissions, schemes, data types or data authorities.
+ * Cannot be used for getting sticky broadcasts.
*/
@Singleton
open class BroadcastDispatcher @Inject constructor (
@@ -72,11 +74,14 @@
*
* @param receiver A receiver to dispatch the [Intent]
* @param filter A filter to determine what broadcasts should be dispatched to this receiver.
- * It will only take into account actions and categories for filtering.
+ * It will only take into account actions and categories for filtering. It must
+ * have at least one action.
* @param handler A handler to dispatch [BroadcastReceiver.onReceive]. By default, it is the
* main handler. Pass `null` to use the default.
* @param user A user handle to determine which broadcast should be dispatched to this receiver.
* By default, it is the current user.
+ * @throws IllegalArgumentException if the filter has other constraints that are not actions or
+ * categories or the filter has no actions.
*/
@JvmOverloads
fun registerReceiver(
@@ -85,12 +90,23 @@
handler: Handler? = mainHandler,
user: UserHandle = context.user
) {
+ checkFilter(filter)
this.handler
.obtainMessage(MSG_ADD_RECEIVER,
ReceiverData(receiver, filter, handler ?: mainHandler, user))
.sendToTarget()
}
+ private fun checkFilter(filter: IntentFilter) {
+ val sb = StringBuilder()
+ if (filter.countActions() == 0) sb.append("Filter must contain at least one action. ")
+ if (filter.countDataAuthorities() != 0) sb.append("Filter cannot contain DataAuthorities. ")
+ if (filter.countDataPaths() != 0) sb.append("Filter cannot contain DataPaths. ")
+ if (filter.countDataSchemes() != 0) sb.append("Filter cannot contain DataSchemes. ")
+ if (filter.countDataTypes() != 0) sb.append("Filter cannot contain DataTypes. ")
+ if (!TextUtils.isEmpty(sb)) throw IllegalArgumentException(sb.toString())
+ }
+
/**
* Unregister receiver for all users.
* <br>
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/dagger/ActivityBinder.java
rename to packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 4be610f..61ded13 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -30,7 +30,7 @@
* Services and Activities that are injectable should go here.
*/
@Module
-public abstract class ActivityBinder {
+public abstract class DefaultActivityBinder {
/** Inject into TunerActivity. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ComponentBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultComponentBinder.java
similarity index 72%
rename from packages/SystemUI/src/com/android/systemui/dagger/ComponentBinder.java
rename to packages/SystemUI/src/com/android/systemui/dagger/DefaultComponentBinder.java
index 4e4c06e..d8989ee 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ComponentBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultComponentBinder.java
@@ -16,16 +16,13 @@
package com.android.systemui.dagger;
-import dagger.Binds;
import dagger.Module;
/**
* Dagger Module that collects related sub-modules together.
+ *
+ * See {@link ContextComponentResolver}
*/
-@Module(includes = {ActivityBinder.class, ServiceBinder.class, SystemUIBinder.class})
-public abstract class ComponentBinder {
- /** */
- @Binds
- public abstract ContextComponentHelper bindComponentHelper(
- ContextComponentResolver componentHelper);
+@Module(includes = {DefaultActivityBinder.class, DefaultServiceBinder.class, SystemUIBinder.class})
+public abstract class DefaultComponentBinder {
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ServiceBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/dagger/ServiceBinder.java
rename to packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
index 1f2c0a1..14bb80c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ServiceBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
@@ -31,7 +31,7 @@
* Services that are injectable should go here.
*/
@Module
-public abstract class ServiceBinder {
+public abstract class DefaultServiceBinder {
/** */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 738f539..27c526b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -24,6 +24,7 @@
import com.android.systemui.power.PowerUI;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsModule;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.leak.GarbageMonitor;
import com.android.systemui.volume.VolumeUI;
@@ -80,6 +81,12 @@
@ClassKey(ScreenDecorations.class)
public abstract SystemUI bindScreenDecorations(ScreenDecorations sysui);
+ /** Inject into StatusBar. */
+ @Binds
+ @IntoMap
+ @ClassKey(StatusBar.class)
+ public abstract SystemUI bindsStatusBar(StatusBar sysui);
+
/** Inject into VolumeUI. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index c95b50b..7b8d3bc 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -21,7 +21,6 @@
import androidx.annotation.Nullable;
-import com.android.systemui.SystemUI;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
import com.android.systemui.power.EnhancedEstimates;
@@ -39,8 +38,6 @@
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
-import dagger.multibindings.ClassKey;
-import dagger.multibindings.IntoMap;
/**
* A dagger module for injecting default implementations of components of System UI that may be
@@ -74,11 +71,6 @@
@Binds
abstract ShadeController provideShadeController(StatusBar statusBar);
- @Binds
- @IntoMap
- @ClassKey(StatusBar.class)
- public abstract SystemUI providesStatusBar(StatusBar statusBar);
-
@Singleton
@Provides
@Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 4e60f19..ca8e53d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -30,6 +30,7 @@
import javax.inject.Singleton;
+import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@@ -38,9 +39,12 @@
* implementation.
*/
@Module(includes = {AssistModule.class,
- ComponentBinder.class,
PeopleHubModule.class})
public abstract class SystemUIModule {
+ /** */
+ @Binds
+ public abstract ContextComponentHelper bindComponentHelper(
+ ContextComponentResolver componentHelper);
@Singleton
@Provides
@@ -56,7 +60,6 @@
keyguardUpdateMonitor);
}
-
@Singleton
@Provides
static SysUiState provideSysUiState() {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
index 113c9c8..83d956c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
@@ -37,6 +37,7 @@
*/
@Singleton
@Component(modules = {
+ DefaultComponentBinder.class,
DependencyProvider.class,
DependencyBinder.class,
SystemServicesModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/log/RichEvent.java b/packages/SystemUI/src/com/android/systemui/log/RichEvent.java
index 89b7a818..acf761ed 100644
--- a/packages/SystemUI/src/com/android/systemui/log/RichEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/log/RichEvent.java
@@ -67,7 +67,7 @@
private B mBuilder = getBuilder();
protected int mType = UNINITIALIZED;
protected String mReason;
- protected @Level int mLogLevel;
+ protected @Level int mLogLevel = VERBOSE;
/**
* Get the log-specific builder.
diff --git a/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java b/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
index a6e10e6..f094cb9 100644
--- a/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
@@ -120,9 +120,9 @@
}
/**
- * @return user-readable string of the given event
+ * @return user-readable string of the given event with timestamp
*/
- public String eventToString(Event event) {
+ public String eventToTimestampedString(Event event) {
StringBuilder sb = new StringBuilder();
sb.append(SysuiLog.DATE_FORMAT.format(event.getTimestamp()));
sb.append(" ");
@@ -131,13 +131,20 @@
}
/**
+ * @return user-readable string of the given event without a timestamp
+ */
+ public String eventToString(Event event) {
+ return event.getMessage();
+ }
+
+ /**
* only call on this method if you have the mDataLock
*/
private void dumpTimelineLocked(PrintWriter pw) {
pw.println("\tTimeline:");
for (Event event : mTimeline) {
- pw.println("\t" + eventToString(event));
+ pw.println("\t" + eventToTimestampedString(event));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 0988e34..d668665 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -163,7 +163,7 @@
if (!isPlaybackActive(state.getState())) {
clearCurrentMediaNotification();
}
- dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */);
+ findAndUpdateMediaNotifications();
}
}
@@ -200,6 +200,16 @@
mEntryManager = notificationEntryManager;
notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
+ public void onPendingEntryAdded(NotificationEntry entry) {
+ findAndUpdateMediaNotifications();
+ }
+
+ @Override
+ public void onPreEntryUpdated(NotificationEntry entry) {
+ findAndUpdateMediaNotifications();
+ }
+
+ @Override
public void onEntryRemoved(
NotificationEntry entry,
NotificationVisibility visibility,
@@ -272,16 +282,12 @@
boolean metaDataChanged = false;
synchronized (mEntryManager.getNotificationData()) {
- ArrayList<NotificationEntry> activeNotifications =
- mEntryManager.getNotificationData().getActiveNotifications();
- final int N = activeNotifications.size();
+ Set<NotificationEntry> allNotifications = mEntryManager.getAllNotifs();
// Promote the media notification with a controller in 'playing' state, if any.
NotificationEntry mediaNotification = null;
MediaController controller = null;
- for (int i = 0; i < N; i++) {
- final NotificationEntry entry = activeNotifications.get(i);
-
+ for (NotificationEntry entry : allNotifications) {
if (entry.isMediaNotification()) {
final MediaSession.Token token =
entry.getSbn().getNotification().extras.getParcelable(
@@ -319,8 +325,7 @@
// now to see if we have one like this
final String pkg = aController.getPackageName();
- for (int i = 0; i < N; i++) {
- final NotificationEntry entry = activeNotifications.get(i);
+ for (NotificationEntry entry : allNotifications) {
if (entry.getSbn().getPackageName().equals(pkg)) {
if (DEBUG_MEDIA) {
Log.v(TAG, "DEBUG_MEDIA: found controller matching "
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index bde097a..404087d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -53,8 +53,10 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -561,6 +563,15 @@
}
/**
+ * @return all notification we're currently aware of (both pending and visible notifications)
+ */
+ public Set<NotificationEntry> getAllNotifs() {
+ Set<NotificationEntry> allNotifs = new HashSet<>(mPendingNotifications.values());
+ allNotifs.addAll(mNotificationData.getActiveNotifications());
+ return allNotifs;
+ }
+
+ /**
* Gets the pending or visible notification entry with the given key. Returns null if
* notification doesn't exist.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 179375e..4e91e4c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -240,7 +240,7 @@
* @return Alpha from 0 to 1.
*/
private float getClockAlpha(int y) {
- float alphaKeyguard = Math.max(0, y / Math.max(1f, getExpandedPreferredClockY()));
+ float alphaKeyguard = Math.max(0, y / Math.max(1f, getClockY(1f)));
alphaKeyguard = Interpolators.ACCELERATE.getInterpolation(alphaKeyguard);
return MathUtils.lerp(alphaKeyguard, 1f, mDarkAmount);
}
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 971843e..afc147a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1528,7 +1528,6 @@
.start();
}
}
- mMediaManager.findAndUpdateMediaNotifications();
}
private void updateReportRejectedTouchVisibility() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index f2d2fae..2c99668 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy;
+import static android.os.UserManager.SWITCHABILITY_STATUS_OK;
+
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import static com.android.systemui.DejankUtils.whitelistIpcs;
@@ -196,7 +198,10 @@
}
ArrayList<UserRecord> records = new ArrayList<>(infos.size());
int currentId = ActivityManager.getCurrentUser();
- boolean canSwitchUsers = mUserManager.canSwitchUsers();
+ // Check user switchability of the foreground user since SystemUI is running in
+ // User 0
+ boolean canSwitchUsers = mUserManager.getUserSwitchability(
+ UserHandle.of(ActivityManager.getCurrentUser())) == SWITCHABILITY_STATUS_OK;
UserInfo currentUserInfo = null;
UserRecord guestRecord = null;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
index 2bff548..ead14e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
@@ -21,6 +21,7 @@
import android.content.IntentFilter
import android.os.Handler
import android.os.Looper
+import android.os.PatternMatcher
import android.os.UserHandle
import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
@@ -33,6 +34,7 @@
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
+import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
@@ -48,6 +50,10 @@
val user1 = UserHandle.of(1)
fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+ const val TEST_ACTION = "TEST_ACTION"
+ const val TEST_SCHEME = "TEST_SCHEME"
+ const val TEST_PATH = "TEST_PATH"
+ const val TEST_TYPE = "test/type"
}
@Mock
@@ -83,6 +89,11 @@
Handler(testableLooper.looper),
testableLooper.looper,
mapOf(0 to mockUBRUser0, 1 to mockUBRUser1))
+
+ // These should be valid filters
+ `when`(intentFilter.countActions()).thenReturn(1)
+ `when`(intentFilterOther.countActions()).thenReturn(1)
+ `when`(mockContext.user).thenReturn(user0)
}
@Test
@@ -129,6 +140,44 @@
verify(mockUBRUser1, never()).unregisterReceiver(broadcastReceiver)
}
+ @Test(expected = IllegalArgumentException::class)
+ fun testFilterMustContainActions() {
+ val testFilter = IntentFilter()
+ broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun testFilterMustNotContainDataScheme() {
+ val testFilter = IntentFilter(TEST_ACTION).apply {
+ addDataScheme(TEST_SCHEME)
+ }
+ broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun testFilterMustNotContainDataAuthority() {
+ val testFilter = IntentFilter(TEST_ACTION).apply {
+ addDataAuthority(mock(IntentFilter.AuthorityEntry::class.java))
+ }
+ broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun testFilterMustNotContainDataPath() {
+ val testFilter = IntentFilter(TEST_ACTION).apply {
+ addDataPath(TEST_PATH, PatternMatcher.PATTERN_LITERAL)
+ }
+ broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun testFilterMustNotContainDataType() {
+ val testFilter = IntentFilter(TEST_ACTION).apply {
+ addDataType(TEST_TYPE)
+ }
+ broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter)
+ }
+
private class TestBroadcastDispatcher(
context: Context,
mainHandler: Handler,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
index a1822c7..f5d6f22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
@@ -50,19 +50,14 @@
fun testBindViewModelToViewBoundary() {
val fakePerson1 = fakePersonViewModel("name")
val fakeViewModel = PeopleHubViewModel(sequenceOf(fakePerson1), true)
-
val fakePersonViewAdapter1 = FakeDataListener<PersonViewModel?>()
val fakePersonViewAdapter2 = FakeDataListener<PersonViewModel?>()
-
val mockClickView = mock(View::class.java)
-
`when`(mockViewBoundary.associatedViewForClickAnimation).thenReturn(mockClickView)
`when`(mockViewBoundary.personViewAdapters)
.thenReturn(sequenceOf(fakePersonViewAdapter1, fakePersonViewAdapter2))
-
val mockFactory = mock(PeopleHubViewModelFactory::class.java)
`when`(mockFactory.createWithAssociatedClickView(any())).thenReturn(fakeViewModel)
-
val mockSubscription = mock(Subscription::class.java)
val fakeFactoryDataSource = object : DataSource<PeopleHubViewModelFactory> {
override fun registerListener(
@@ -82,6 +77,7 @@
verify(mockFactory).createWithAssociatedClickView(mockClickView)
}
+ @Test
fun testViewModelDataSourceTransformsModel() {
val fakeClickIntent = PendingIntent.getActivity(context, 0, Intent("action"), 0)
val fakePerson = fakePersonModel("id", "name", fakeClickIntent)
@@ -99,16 +95,17 @@
val mockClickView = mock(View::class.java)
factoryDataSource.registerListener(fakeListener)
+
val viewModel = (fakeListener.lastSeen as Maybe.Just).value
.createWithAssociatedClickView(mockClickView)
assertThat(viewModel.isVisible).isTrue()
-
val people = viewModel.people.toList()
assertThat(people.size).isEqualTo(1)
assertThat(people[0].name).isEqualTo("name")
assertThat(people[0].icon).isSameAs(fakePerson.avatar)
people[0].onClick()
+
verify(mockActivityStarter).startPendingIntentDismissingKeyguard(
same(fakeClickIntent),
any(),
@@ -117,16 +114,20 @@
}
}
+/** Works around Mockito matchers returning `null` and breaking non-nullable Kotlin code. */
private inline fun <reified T : Any> any(): T {
return Mockito.any() ?: createInstance(T::class)
}
+/** Works around Mockito matchers returning `null` and breaking non-nullable Kotlin code. */
private inline fun <reified T : Any> same(value: T): T {
return Mockito.same(value) ?: createInstance(T::class)
}
+/** Creates an instance of the given class. */
private fun <T : Any> createInstance(clazz: KClass<T>): T = castNull()
+/** Tricks the Kotlin compiler into assigning `null` to a non-nullable variable. */
@Suppress("UNCHECKED_CAST")
private fun <T> castNull(): T = null as T
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index ca69c18..2bfe287 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -21,6 +21,7 @@
"src/**/*.java",
":framework-tethering-shared-srcs",
":services-tethering-shared-srcs",
+ ":servicescore-tethering-src",
],
static_libs: [
"androidx.annotation_annotation",
@@ -67,9 +68,17 @@
// This group will be removed when tethering migration is done.
filegroup {
- name: "tethering-services-srcs",
+ name: "tethering-servicescore-srcs",
srcs: [
+ "src/com/android/server/connectivity/tethering/EntitlementManager.java",
"src/com/android/server/connectivity/tethering/TetheringConfiguration.java",
+ ],
+}
+
+// This group will be removed when tethering migration is done.
+filegroup {
+ name: "tethering-servicesnet-srcs",
+ srcs: [
"src/android/net/dhcp/DhcpServerCallbacks.java",
"src/android/net/dhcp/DhcpServingParamsParcelExt.java",
"src/android/net/ip/IpServer.java",
diff --git a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
similarity index 99%
rename from services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
rename to packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
index f952bce..6b0f1de 100644
--- a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -87,7 +87,6 @@
private static final int EVENT_MAYBE_RUN_PROVISIONING = 3;
private static final int EVENT_GET_ENTITLEMENT_VALUE = 4;
-
// The ArraySet contains enabled downstream types, ex:
// {@link ConnectivityManager.TETHERING_WIFI}
// {@link ConnectivityManager.TETHERING_USB}
@@ -112,7 +111,6 @@
public EntitlementManager(Context ctx, StateMachine tetherMasterSM, SharedLog log,
int permissionChangeMessageCode, MockableSystemProperties systemProperties) {
-
mContext = ctx;
mLog = log.forSubComponent(TAG);
mCurrentTethers = new ArraySet<Integer>();
@@ -138,7 +136,7 @@
/**
* Ui entitlement check fails in |downstream|.
*
- * @param downstream tethering type from ConnectivityManager.TETHERING_{@code *}.
+ * @param downstream tethering type from ConnectivityManager.TETHERING_{@code *}.
*/
void onUiEntitlementFailed(int downstream);
}
@@ -662,7 +660,6 @@
private void handleGetLatestTetheringEntitlementValue(int downstream, ResultReceiver receiver,
boolean showEntitlementUi) {
-
final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
if (!isTetherProvisioningRequired(config)) {
receiver.send(TETHER_ERROR_NO_ERROR, null);
diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp
index da62107..5564bd6 100644
--- a/packages/Tethering/tests/unit/Android.bp
+++ b/packages/Tethering/tests/unit/Android.bp
@@ -17,7 +17,10 @@
android_test {
name: "TetheringTests",
certificate: "platform",
- srcs: ["src/**/*.java"],
+ srcs: [
+ ":servicescore-tethering-src",
+ "src/**/*.java",
+ ],
test_suites: ["device-tests"],
static_libs: [
"androidx.test.rules",
@@ -42,6 +45,7 @@
filegroup {
name: "tethering-tests-src",
srcs: [
+ "src/com/android/server/connectivity/tethering/EntitlementManagerTest.java",
"src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java",
"src/android/net/dhcp/DhcpServingParamsParcelExtTest.java",
"src/android/net/ip/IpServerTest.java",
diff --git a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
similarity index 100%
rename from tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
rename to packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 3067beb..c865384 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -80,6 +80,7 @@
":vold_aidl",
":gsiservice_aidl",
":platform-compat-config",
+ ":tethering-servicescore-srcs",
"java/com/android/server/EventLogTags.logtags",
"java/com/android/server/am/EventLogTags.logtags",
"java/com/android/server/policy/EventLogTags.logtags",
@@ -155,3 +156,11 @@
name: "protolog.conf.json.gz",
src: ":services.core.json.gz",
}
+
+// TODO: this should be removed after tethering migration done.
+filegroup {
+ name: "servicescore-tethering-src",
+ srcs: [
+ "java/com/android/server/connectivity/MockableSystemProperties.java",
+ ],
+}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 0a63bf8..7712f1b 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -2051,6 +2051,18 @@
* Note: must be constructed with lock held.
*/
private UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
+ // translate expireIn value into expireAt
+ long elapsedRealtime = SystemClock.elapsedRealtime();
+ long expireAt;
+ // Check for > Long.MAX_VALUE overflow (elapsedRealtime > 0):
+ if (request.getExpireIn() > Long.MAX_VALUE - elapsedRealtime) {
+ expireAt = Long.MAX_VALUE;
+ } else {
+ expireAt = Math.max(request.getExpireIn() + elapsedRealtime, 0);
+ }
+ request.setExpireAt(Math.min(request.getExpireAt(), expireAt));
+ request.setExpireIn(Long.MAX_VALUE);
+
mProvider = provider;
mRealRequest = request;
mRequest = request;
diff --git a/services/core/java/com/android/server/NetworkScorerAppManager.java b/services/core/java/com/android/server/NetworkScorerAppManager.java
index bfd4247..3bcb36f 100644
--- a/services/core/java/com/android/server/NetworkScorerAppManager.java
+++ b/services/core/java/com/android/server/NetworkScorerAppManager.java
@@ -18,10 +18,10 @@
import android.Manifest.permission;
import android.annotation.Nullable;
-import android.app.AppOpsManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.PermissionChecker;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
@@ -210,14 +210,9 @@
}
private boolean canAccessLocation(int uid, String packageName) {
- final PackageManager pm = mContext.getPackageManager();
- final AppOpsManager appOpsManager =
- (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
- return isLocationModeEnabled()
- && pm.checkPermission(permission.ACCESS_COARSE_LOCATION, packageName)
- == PackageManager.PERMISSION_GRANTED
- && appOpsManager.noteOp(AppOpsManager.OP_COARSE_LOCATION, uid, packageName)
- == AppOpsManager.MODE_ALLOWED;
+ return isLocationModeEnabled() && PermissionChecker.checkPermissionForPreflight(mContext,
+ permission.ACCESS_COARSE_LOCATION, PermissionChecker.PID_UNKNOWN, uid, packageName)
+ == PermissionChecker.PERMISSION_GRANTED;
}
private boolean isLocationModeEnabled() {
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index e3dc3b7..7f51aa9 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -51,6 +51,8 @@
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
/**
* Find the best Service, and bind to it.
@@ -64,6 +66,7 @@
public static final String EXTRA_SERVICE_VERSION = "serviceVersion";
public static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser";
+ private static final long BLOCKING_BINDER_TIMEOUT_MS = 30 * 1000;
/** Function to run on binder interface. */
public interface BinderRunner {
@@ -402,7 +405,7 @@
return defaultValue;
}
});
- } catch (InterruptedException e) {
+ } catch (InterruptedException | TimeoutException e) {
return defaultValue;
}
}
@@ -439,7 +442,8 @@
}
}
- private <T> T runOnHandlerBlocking(Callable<T> c) throws InterruptedException {
+ private <T> T runOnHandlerBlocking(Callable<T> c)
+ throws InterruptedException, TimeoutException {
if (Looper.myLooper() == mHandler.getLooper()) {
try {
return c.call();
@@ -451,7 +455,12 @@
FutureTask<T> task = new FutureTask<>(c);
mHandler.post(task);
try {
- return task.get();
+ // timeout will unblock callers, in particular if the caller is a binder thread to
+ // help reduce binder contention. this will still result in blocking the handler
+ // thread which may result in ANRs, but should make problems slightly more rare.
+ // the underlying solution is simply not to use this API at all, but that would
+ // require large refactors to very legacy code.
+ return task.get(BLOCKING_BINDER_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (ExecutionException e) {
// Function cannot throw exception, this should never happen
throw new IllegalStateException(e);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 11cfe12..5df4543 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7758,7 +7758,7 @@
holder = getContentProviderExternalUnchecked(name, null, callingUid,
"*checkContentProviderUriPermission*", userId);
if (holder != null) {
- return holder.provider.checkUriPermission(null, uri, callingUid, modeFlags);
+ return holder.provider.checkUriPermission(null, null, uri, callingUid, modeFlags);
}
} catch (RemoteException e) {
Log.w(TAG, "Content provider dead retrieving " + uri, e);
@@ -7923,7 +7923,7 @@
sCallerIdentity.set(new Identity(
token, Binder.getCallingPid(), Binder.getCallingUid()));
try {
- pfd = cph.provider.openFile(null, uri, "r", null, token);
+ pfd = cph.provider.openFile(null, null, uri, "r", null, token);
} catch (FileNotFoundException e) {
// do nothing; pfd will be returned null
} finally {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index f7ac040..6010b1dc 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -57,6 +57,13 @@
private final @NonNull AudioService mAudioService;
private final @NonNull Context mContext;
+ /** Forced device usage for communications sent to AudioSystem */
+ private int mForcedUseForComm;
+ /**
+ * Externally reported force device usage state returned by getters: always consistent
+ * with requests by setters */
+ private int mForcedUseForCommExt;
+
// Manages all connected devices, only ever accessed on the message loop
private final AudioDeviceInventory mDeviceInventory;
// Manages notifications to BT service
@@ -64,34 +71,24 @@
//-------------------------------------------------------------------
- /**
- * Lock to guard:
- * - any changes to the message queue: enqueueing or removing any message
- * - state of A2DP enabled
- * - force use for communication + SCO changes
- */
- private final Object mDeviceBrokerLock = new Object();
-
- @GuardedBy("mDeviceBrokerLock")
+ // we use a different lock than mDeviceStateLock so as not to create
+ // lock contention between enqueueing a message and handling them
+ private static final Object sLastDeviceConnectionMsgTimeLock = new Object();
+ @GuardedBy("sLastDeviceConnectionMsgTimeLock")
private static long sLastDeviceConnectMsgTime = 0;
+ // General lock to be taken whenever the state of the audio devices is to be checked or changed
+ private final Object mDeviceStateLock = new Object();
- /** Request to override default use of A2DP for media */
- @GuardedBy("mDeviceBrokerLock")
+ // Request to override default use of A2DP for media.
+ @GuardedBy("mDeviceStateLock")
private boolean mBluetoothA2dpEnabled;
- /** Forced device usage for communications sent to AudioSystem */
- @GuardedBy("mDeviceBrokerLock")
- private int mForcedUseForComm;
- /**
- * Externally reported force device usage state returned by getters: always consistent
- * with requests by setters */
- @GuardedBy("mDeviceBrokerLock")
- private int mForcedUseForCommExt;
-
+ // lock always taken when accessing AudioService.mSetModeDeathHandlers
+ // TODO do not "share" the lock between AudioService and BtHelpr, see b/123769055
+ /*package*/ final Object mSetModeLock = new Object();
//-------------------------------------------------------------------
- /** Normal constructor used by AudioService */
/*package*/ AudioDeviceBroker(@NonNull Context context, @NonNull AudioService service) {
mContext = context;
mAudioService = service;
@@ -130,37 +127,38 @@
// All post* methods are asynchronous
/*package*/ void onSystemReady() {
- mBtHelper.onSystemReady();
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.onSystemReady();
+ }
+ }
}
/*package*/ void onAudioServerDied() {
// Restore forced usage for communications and record
- synchronized (mDeviceBrokerLock) {
+ synchronized (mDeviceStateLock) {
AudioSystem.setParameters(
"BT_SCO=" + (mForcedUseForComm == AudioSystem.FORCE_BT_SCO ? "on" : "off"));
onSetForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, "onAudioServerDied");
onSetForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm, "onAudioServerDied");
-
- // restore devices
- sendMsgNoDelay(MSG_RESTORE_DEVICES, SENDMSG_REPLACE);
}
+ // restore devices
+ sendMsgNoDelay(MSG_RESTORE_DEVICES, SENDMSG_REPLACE);
}
/*package*/ void setForceUse_Async(int useCase, int config, String eventSource) {
- synchronized (mDeviceBrokerLock) {
- sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
- useCase, config, eventSource);
- }
+ sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
+ useCase, config, eventSource);
}
/*package*/ void toggleHdmiIfConnected_Async() {
- synchronized (mDeviceBrokerLock) {
- sendMsgNoDelay(MSG_TOGGLE_HDMI, SENDMSG_QUEUE);
- }
+ sendMsgNoDelay(MSG_TOGGLE_HDMI, SENDMSG_QUEUE);
}
/*package*/ void disconnectAllBluetoothProfiles() {
+ synchronized (mDeviceStateLock) {
mBtHelper.disconnectAllBluetoothProfiles();
+ }
}
/**
@@ -170,11 +168,15 @@
* @param intent
*/
/*package*/ void receiveBtEvent(@NonNull Intent intent) {
- mBtHelper.receiveBtEvent(intent);
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.receiveBtEvent(intent);
+ }
+ }
}
/*package*/ void setBluetoothA2dpOn_Async(boolean on, String source) {
- synchronized (mDeviceBrokerLock) {
+ synchronized (mDeviceStateLock) {
if (mBluetoothA2dpEnabled == on) {
return;
}
@@ -194,7 +196,7 @@
* @return true if speakerphone state changed
*/
/*package*/ boolean setSpeakerphoneOn(boolean on, String eventSource) {
- synchronized (mDeviceBrokerLock) {
+ synchronized (mDeviceStateLock) {
final boolean wasOn = isSpeakerphoneOn();
if (on) {
if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
@@ -212,7 +214,7 @@
}
/*package*/ boolean isSpeakerphoneOn() {
- synchronized (mDeviceBrokerLock) {
+ synchronized (mDeviceStateLock) {
return (mForcedUseForCommExt == AudioSystem.FORCE_SPEAKER);
}
}
@@ -221,7 +223,9 @@
@AudioService.ConnectionState int state, String address, String name,
String caller) {
//TODO move logging here just like in setBluetooth* methods
- mDeviceInventory.setWiredDeviceConnectionState(type, state, address, name, caller);
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.setWiredDeviceConnectionState(type, state, address, name, caller);
+ }
}
private static final class BtDeviceConnectionInfo {
@@ -255,24 +259,27 @@
final BtDeviceConnectionInfo info = new BtDeviceConnectionInfo(device, state, profile,
suppressNoisyIntent, a2dpVolume);
- synchronized (mDeviceBrokerLock) {
- // when receiving a request to change the connection state of a device, this last
- // request is the source of truth, so cancel all previous requests
- mBrokerHandler.removeMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION,
- device);
- mBrokerHandler.removeMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION,
- device);
- mBrokerHandler.removeMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED,
- device);
- mBrokerHandler.removeMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
- device);
+ // when receiving a request to change the connection state of a device, this last request
+ // is the source of truth, so cancel all previous requests
+ removeAllA2dpConnectionEvents(device);
- sendLMsgNoDelay(
- state == BluetoothProfile.STATE_CONNECTED
- ? MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION
- : MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION,
- SENDMSG_QUEUE, info);
- }
+ sendLMsgNoDelay(
+ state == BluetoothProfile.STATE_CONNECTED
+ ? MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION
+ : MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION,
+ SENDMSG_QUEUE, info);
+ }
+
+ /** remove all previously scheduled connection and disconnection events for the given device */
+ private void removeAllA2dpConnectionEvents(@NonNull BluetoothDevice device) {
+ mBrokerHandler.removeMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION,
+ device);
+ mBrokerHandler.removeMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION,
+ device);
+ mBrokerHandler.removeMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED,
+ device);
+ mBrokerHandler.removeMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
+ device);
}
private static final class HearingAidDeviceConnectionInfo {
@@ -298,31 +305,28 @@
boolean suppressNoisyIntent, int musicDevice, @NonNull String eventSource) {
final HearingAidDeviceConnectionInfo info = new HearingAidDeviceConnectionInfo(
device, state, suppressNoisyIntent, musicDevice, eventSource);
- synchronized (mDeviceBrokerLock) {
- sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
- }
+ sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
}
// never called by system components
/*package*/ void setBluetoothScoOnByApp(boolean on) {
- synchronized (mDeviceBrokerLock) {
+ synchronized (mDeviceStateLock) {
mForcedUseForCommExt = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
}
}
/*package*/ boolean isBluetoothScoOnForApp() {
- synchronized (mDeviceBrokerLock) {
+ synchronized (mDeviceStateLock) {
return mForcedUseForCommExt == AudioSystem.FORCE_BT_SCO;
}
}
/*package*/ void setBluetoothScoOn(boolean on, String eventSource) {
//Log.i(TAG, "setBluetoothScoOnInt: " + on + " " + eventSource);
- final boolean isBtScoOn = mBtHelper.isBluetoothScoOn();
- synchronized (mDeviceBrokerLock) {
+ synchronized (mDeviceStateLock) {
if (on) {
// do not accept SCO ON if SCO audio is not connected
- if (!isBtScoOn) {
+ if (!mBtHelper.isBluetoothScoOn()) {
mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
return;
}
@@ -342,55 +346,58 @@
}
/*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
- return mDeviceInventory.startWatchingRoutes(observer);
-
+ synchronized (mDeviceStateLock) {
+ return mDeviceInventory.startWatchingRoutes(observer);
+ }
}
/*package*/ AudioRoutesInfo getCurAudioRoutes() {
- return mDeviceInventory.getCurAudioRoutes();
+ synchronized (mDeviceStateLock) {
+ return mDeviceInventory.getCurAudioRoutes();
+ }
}
/*package*/ boolean isAvrcpAbsoluteVolumeSupported() {
- return mBtHelper.isAvrcpAbsoluteVolumeSupported();
+ synchronized (mDeviceStateLock) {
+ return mBtHelper.isAvrcpAbsoluteVolumeSupported();
+ }
}
/*package*/ boolean isBluetoothA2dpOn() {
- synchronized (mDeviceBrokerLock) {
+ synchronized (mDeviceStateLock) {
return mBluetoothA2dpEnabled;
}
}
/*package*/ void postSetAvrcpAbsoluteVolumeIndex(int index) {
- synchronized (mDeviceBrokerLock) {
- sendIMsgNoDelay(MSG_I_SET_AVRCP_ABSOLUTE_VOLUME, SENDMSG_REPLACE, index);
- }
+ sendIMsgNoDelay(MSG_I_SET_AVRCP_ABSOLUTE_VOLUME, SENDMSG_REPLACE, index);
}
/*package*/ void postSetHearingAidVolumeIndex(int index, int streamType) {
- synchronized (mDeviceBrokerLock) {
- sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType);
- }
+ sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType);
}
/*package*/ void postDisconnectBluetoothSco(int exceptPid) {
- synchronized (mDeviceBrokerLock) {
- sendIMsgNoDelay(MSG_I_DISCONNECT_BT_SCO, SENDMSG_REPLACE, exceptPid);
- }
+ sendIMsgNoDelay(MSG_I_DISCONNECT_BT_SCO, SENDMSG_REPLACE, exceptPid);
}
/*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) {
- synchronized (mDeviceBrokerLock) {
- sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device);
+ sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device);
+ }
+
+ @GuardedBy("mSetModeLock")
+ /*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode,
+ @NonNull String eventSource) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource);
}
}
- /*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode,
- @NonNull String eventSource) {
- mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource);
- }
-
+ @GuardedBy("mSetModeLock")
/*package*/ void stopBluetoothScoForClient_Sync(IBinder cb, @NonNull String eventSource) {
- mBtHelper.stopBluetoothScoForClient(cb, eventSource);
+ synchronized (mDeviceStateLock) {
+ mBtHelper.stopBluetoothScoForClient(cb, eventSource);
+ }
}
//---------------------------------------------------------------------
@@ -453,109 +460,77 @@
//---------------------------------------------------------------------
// Message handling on behalf of helper classes
/*package*/ void postBroadcastScoConnectionState(int state) {
- synchronized (mDeviceBrokerLock) {
- sendIMsgNoDelay(MSG_I_BROADCAST_BT_CONNECTION_STATE, SENDMSG_QUEUE, state);
- }
+ sendIMsgNoDelay(MSG_I_BROADCAST_BT_CONNECTION_STATE, SENDMSG_QUEUE, state);
}
/*package*/ void postBroadcastBecomingNoisy() {
- synchronized (mDeviceBrokerLock) {
- sendMsgNoDelay(MSG_BROADCAST_AUDIO_BECOMING_NOISY, SENDMSG_REPLACE);
- }
+ sendMsgNoDelay(MSG_BROADCAST_AUDIO_BECOMING_NOISY, SENDMSG_REPLACE);
}
/*package*/ void postA2dpSinkConnection(@AudioService.BtProfileConnectionState int state,
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) {
- synchronized (mDeviceBrokerLock) {
- sendILMsg(state == BluetoothA2dp.STATE_CONNECTED
- ? MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED
- : MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
- SENDMSG_QUEUE,
- state, btDeviceInfo, delay);
- }
+ sendILMsg(state == BluetoothA2dp.STATE_CONNECTED
+ ? MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED
+ : MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
+ SENDMSG_QUEUE,
+ state, btDeviceInfo, delay);
}
/*package*/ void postA2dpSourceConnection(@AudioService.BtProfileConnectionState int state,
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) {
- synchronized (mDeviceBrokerLock) {
- sendILMsg(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE,
- state, btDeviceInfo, delay);
- }
+ sendILMsg(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE,
+ state, btDeviceInfo, delay);
}
/*package*/ void postSetWiredDeviceConnectionState(
AudioDeviceInventory.WiredDeviceConnectionState connectionState, int delay) {
- synchronized (mDeviceBrokerLock) {
- sendLMsg(MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE, SENDMSG_QUEUE,
- connectionState, delay);
- }
+ sendLMsg(MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE, SENDMSG_QUEUE, connectionState, delay);
}
/*package*/ void postSetHearingAidConnectionState(
@AudioService.BtProfileConnectionState int state,
@NonNull BluetoothDevice device, int delay) {
- synchronized (mDeviceBrokerLock) {
- sendILMsg(MSG_IL_SET_HEARING_AID_CONNECTION_STATE, SENDMSG_QUEUE,
- state,
- device,
- delay);
- }
+ sendILMsg(MSG_IL_SET_HEARING_AID_CONNECTION_STATE, SENDMSG_QUEUE,
+ state,
+ device,
+ delay);
}
/*package*/ void postDisconnectA2dp() {
- synchronized (mDeviceBrokerLock) {
- sendMsgNoDelay(MSG_DISCONNECT_A2DP, SENDMSG_QUEUE);
- }
+ sendMsgNoDelay(MSG_DISCONNECT_A2DP, SENDMSG_QUEUE);
}
/*package*/ void postDisconnectA2dpSink() {
- synchronized (mDeviceBrokerLock) {
- sendMsgNoDelay(MSG_DISCONNECT_A2DP_SINK, SENDMSG_QUEUE);
- }
+ sendMsgNoDelay(MSG_DISCONNECT_A2DP_SINK, SENDMSG_QUEUE);
}
/*package*/ void postDisconnectHearingAid() {
- synchronized (mDeviceBrokerLock) {
- sendMsgNoDelay(MSG_DISCONNECT_BT_HEARING_AID, SENDMSG_QUEUE);
- }
+ sendMsgNoDelay(MSG_DISCONNECT_BT_HEARING_AID, SENDMSG_QUEUE);
}
/*package*/ void postDisconnectHeadset() {
- synchronized (mDeviceBrokerLock) {
- sendMsgNoDelay(MSG_DISCONNECT_BT_HEADSET, SENDMSG_QUEUE);
- }
+ sendMsgNoDelay(MSG_DISCONNECT_BT_HEADSET, SENDMSG_QUEUE);
}
/*package*/ void postBtA2dpProfileConnected(BluetoothA2dp a2dpProfile) {
- synchronized (mDeviceBrokerLock) {
- sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP, SENDMSG_QUEUE, a2dpProfile);
- }
+ sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP, SENDMSG_QUEUE, a2dpProfile);
}
/*package*/ void postBtA2dpSinkProfileConnected(BluetoothProfile profile) {
- synchronized (mDeviceBrokerLock) {
- sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK, SENDMSG_QUEUE, profile);
- }
+ sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK, SENDMSG_QUEUE, profile);
}
/*package*/ void postBtHeasetProfileConnected(BluetoothHeadset headsetProfile) {
- synchronized (mDeviceBrokerLock) {
- sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET, SENDMSG_QUEUE,
- headsetProfile);
- }
+ sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET, SENDMSG_QUEUE, headsetProfile);
}
/*package*/ void postBtHearingAidProfileConnected(BluetoothHearingAid hearingAidProfile) {
- synchronized (mDeviceBrokerLock) {
- sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID, SENDMSG_QUEUE,
- hearingAidProfile);
- }
+ sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID, SENDMSG_QUEUE,
+ hearingAidProfile);
}
/*package*/ void postScoClientDied(Object obj) {
- synchronized (mDeviceBrokerLock) {
- sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj);
- }
+ sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj);
}
//---------------------------------------------------------------------
@@ -570,7 +545,7 @@
.append(") from u/pid:").append(Binder.getCallingUid()).append("/")
.append(Binder.getCallingPid()).append(" src:").append(source).toString();
- synchronized (mDeviceBrokerLock) {
+ synchronized (mDeviceStateLock) {
mBluetoothA2dpEnabled = on;
mBrokerHandler.removeMessages(MSG_IIL_SET_FORCE_BT_A2DP_USE);
onSetForceUse(
@@ -582,85 +557,71 @@
/*package*/ boolean handleDeviceConnection(boolean connect, int device, String address,
String deviceName) {
- return mDeviceInventory.handleDeviceConnection(connect, device, address, deviceName);
+ synchronized (mDeviceStateLock) {
+ return mDeviceInventory.handleDeviceConnection(connect, device, address, deviceName);
+ }
}
/*package*/ void postSetA2dpSourceConnectionState(@BluetoothProfile.BtProfileState int state,
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) {
final int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0;
- synchronized (mDeviceBrokerLock) {
- sendILMsgNoDelay(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, state,
- btDeviceInfo);
- }
+ sendILMsgNoDelay(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, state,
+ btDeviceInfo);
}
/*package*/ void handleFailureToConnectToBtHeadsetService(int delay) {
- synchronized (mDeviceBrokerLock) {
- sendMsg(MSG_BT_HEADSET_CNCT_FAILED, SENDMSG_REPLACE, delay);
- }
+ sendMsg(MSG_BT_HEADSET_CNCT_FAILED, SENDMSG_REPLACE, delay);
}
/*package*/ void handleCancelFailureToConnectToBtHeadsetService() {
- synchronized (mDeviceBrokerLock) {
- mBrokerHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
- }
+ mBrokerHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
}
/*package*/ void postReportNewRoutes() {
- synchronized (mDeviceBrokerLock) {
- sendMsgNoDelay(MSG_REPORT_NEW_ROUTES, SENDMSG_NOOP);
- }
+ sendMsgNoDelay(MSG_REPORT_NEW_ROUTES, SENDMSG_NOOP);
}
/*package*/ void cancelA2dpDockTimeout() {
- synchronized (mDeviceBrokerLock) {
- mBrokerHandler.removeMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
- }
+ mBrokerHandler.removeMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
}
- // FIXME: used by?
/*package*/ void postA2dpActiveDeviceChange(
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) {
- synchronized (mDeviceBrokerLock) {
- sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo);
- }
+ sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo);
}
/*package*/ boolean hasScheduledA2dpDockTimeout() {
- synchronized (mDeviceBrokerLock) {
- return mBrokerHandler.hasMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
- }
+ return mBrokerHandler.hasMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
}
// must be called synchronized on mConnectedDevices
/*package*/ boolean hasScheduledA2dpSinkConnectionState(BluetoothDevice btDevice) {
- synchronized (mDeviceBrokerLock) {
- return (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED,
- new BtHelper.BluetoothA2dpDeviceInfo(btDevice))
- || mBrokerHandler.hasMessages(
- MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
- new BtHelper.BluetoothA2dpDeviceInfo(btDevice)));
- }
+ return (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED,
+ new BtHelper.BluetoothA2dpDeviceInfo(btDevice))
+ || mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
+ new BtHelper.BluetoothA2dpDeviceInfo(btDevice)));
}
/*package*/ void setA2dpDockTimeout(String address, int a2dpCodec, int delayMs) {
- synchronized (mDeviceBrokerLock) {
- sendILMsg(MSG_IL_BTA2DP_DOCK_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs);
- }
+ sendILMsg(MSG_IL_BTA2DP_DOCK_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs);
}
/*package*/ void setAvrcpAbsoluteVolumeSupported(boolean supported) {
- mBtHelper.setAvrcpAbsoluteVolumeSupported(supported);
+ synchronized (mDeviceStateLock) {
+ mBtHelper.setAvrcpAbsoluteVolumeSupported(supported);
+ }
}
/*package*/ boolean getBluetoothA2dpEnabled() {
- synchronized (mDeviceBrokerLock) {
+ synchronized (mDeviceStateLock) {
return mBluetoothA2dpEnabled;
}
}
/*package*/ int getA2dpCodec(@NonNull BluetoothDevice device) {
- return mBtHelper.getA2dpCodec(device);
+ synchronized (mDeviceStateLock) {
+ return mBtHelper.getA2dpCodec(device);
+ }
}
/*package*/ void dump(PrintWriter pw, String prefix) {
@@ -748,101 +709,156 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_RESTORE_DEVICES:
- mDeviceInventory.onRestoreDevices();
- mBtHelper.onAudioServerDiedRestoreA2dp();
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onRestoreDevices();
+ mBtHelper.onAudioServerDiedRestoreA2dp();
+ }
break;
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
- mDeviceInventory.onSetWiredDeviceConnectionState(
- (AudioDeviceInventory.WiredDeviceConnectionState) msg.obj);
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onSetWiredDeviceConnectionState(
+ (AudioDeviceInventory.WiredDeviceConnectionState) msg.obj);
+ }
break;
case MSG_I_BROADCAST_BT_CONNECTION_STATE:
- mBtHelper.onBroadcastScoConnectionState(msg.arg1);
+ synchronized (mDeviceStateLock) {
+ mBtHelper.onBroadcastScoConnectionState(msg.arg1);
+ }
break;
case MSG_IIL_SET_FORCE_USE: // intended fall-through
case MSG_IIL_SET_FORCE_BT_A2DP_USE:
onSetForceUse(msg.arg1, msg.arg2, (String) msg.obj);
break;
case MSG_REPORT_NEW_ROUTES:
- mDeviceInventory.onReportNewRoutes();
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onReportNewRoutes();
+ }
break;
case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED:
case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
- mDeviceInventory.onSetA2dpSinkConnectionState(
- (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onSetA2dpSinkConnectionState(
+ (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
+ }
break;
case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
- mDeviceInventory.onSetA2dpSourceConnectionState(
- (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onSetA2dpSourceConnectionState(
+ (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
+ }
break;
case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
- mDeviceInventory.onSetHearingAidConnectionState(
- (BluetoothDevice) msg.obj, msg.arg1,
- mAudioService.getHearingAidStreamType());
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onSetHearingAidConnectionState(
+ (BluetoothDevice) msg.obj, msg.arg1,
+ mAudioService.getHearingAidStreamType());
+ }
break;
case MSG_BT_HEADSET_CNCT_FAILED:
- mBtHelper.resetBluetoothSco();
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.resetBluetoothSco();
+ }
+ }
break;
case MSG_IL_BTA2DP_DOCK_TIMEOUT:
// msg.obj == address of BTA2DP device
- mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1);
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1);
+ }
break;
case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
final int a2dpCodec;
final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
- // FIXME why isn't the codec coming with the request? codec should be
- // provided by BT when it calls
- // AudioManager.handleBluetoothA2dpDeviceConfigChange(BluetoothDevice)
- a2dpCodec = mBtHelper.getA2dpCodec(btDevice);
- mDeviceInventory.onBluetoothA2dpActiveDeviceChange(
- new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec),
- BtHelper.EVENT_DEVICE_CONFIG_CHANGE);
+ synchronized (mDeviceStateLock) {
+ a2dpCodec = mBtHelper.getA2dpCodec(btDevice);
+ mDeviceInventory.onBluetoothA2dpActiveDeviceChange(
+ new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec),
+ BtHelper.EVENT_DEVICE_CONFIG_CHANGE);
+ }
break;
case MSG_BROADCAST_AUDIO_BECOMING_NOISY:
onSendBecomingNoisyIntent();
break;
case MSG_II_SET_HEARING_AID_VOLUME:
- mBtHelper.setHearingAidVolume(msg.arg1, msg.arg2);
+ synchronized (mDeviceStateLock) {
+ mBtHelper.setHearingAidVolume(msg.arg1, msg.arg2);
+ }
break;
case MSG_I_SET_AVRCP_ABSOLUTE_VOLUME:
- mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1);
+ synchronized (mDeviceStateLock) {
+ mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1);
+ }
break;
case MSG_I_DISCONNECT_BT_SCO:
- mBtHelper.disconnectBluetoothSco(msg.arg1);
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.disconnectBluetoothSco(msg.arg1);
+ }
+ }
break;
case MSG_L_SCOCLIENT_DIED:
- mBtHelper.scoClientDied(msg.obj);
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.scoClientDied(msg.obj);
+ }
+ }
break;
case MSG_TOGGLE_HDMI:
- mDeviceInventory.onToggleHdmi();
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onToggleHdmi();
+ }
break;
case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
- mDeviceInventory.onBluetoothA2dpActiveDeviceChange(
- (BtHelper.BluetoothA2dpDeviceInfo) msg.obj,
- BtHelper.EVENT_ACTIVE_DEVICE_CHANGE);
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onBluetoothA2dpActiveDeviceChange(
+ (BtHelper.BluetoothA2dpDeviceInfo) msg.obj,
+ BtHelper.EVENT_ACTIVE_DEVICE_CHANGE);
+ }
break;
case MSG_DISCONNECT_A2DP:
- mDeviceInventory.disconnectA2dp();
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.disconnectA2dp();
+ }
break;
case MSG_DISCONNECT_A2DP_SINK:
- mDeviceInventory.disconnectA2dpSink();
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.disconnectA2dpSink();
+ }
break;
case MSG_DISCONNECT_BT_HEARING_AID:
- mDeviceInventory.disconnectHearingAid();
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.disconnectHearingAid();
+ }
break;
case MSG_DISCONNECT_BT_HEADSET:
- mBtHelper.disconnectHeadset();
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.disconnectHeadset();
+ }
+ }
break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP:
- mBtHelper.onA2dpProfileConnected((BluetoothA2dp) msg.obj);
+ synchronized (mDeviceStateLock) {
+ mBtHelper.onA2dpProfileConnected((BluetoothA2dp) msg.obj);
+ }
break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK:
- mBtHelper.onA2dpSinkProfileConnected((BluetoothProfile) msg.obj);
+ synchronized (mDeviceStateLock) {
+ mBtHelper.onA2dpSinkProfileConnected((BluetoothProfile) msg.obj);
+ }
break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID:
- mBtHelper.onHearingAidProfileConnected((BluetoothHearingAid) msg.obj);
+ synchronized (mDeviceStateLock) {
+ mBtHelper.onHearingAidProfileConnected((BluetoothHearingAid) msg.obj);
+ }
break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET:
- mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj);
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj);
+ }
+ }
break;
case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION:
case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION: {
@@ -855,9 +871,11 @@
+ " addr=" + info.mDevice.getAddress()
+ " prof=" + info.mProfile + " supprNoisy=" + info.mSupprNoisy
+ " vol=" + info.mVolume)).printLog(TAG));
- mDeviceInventory.setBluetoothA2dpDeviceConnectionState(
- info.mDevice, info.mState, info.mProfile, info.mSupprNoisy,
- AudioSystem.DEVICE_NONE, info.mVolume);
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.setBluetoothA2dpDeviceConnectionState(
+ info.mDevice, info.mState, info.mProfile, info.mSupprNoisy,
+ AudioSystem.DEVICE_NONE, info.mVolume);
+ }
} break;
case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT: {
final HearingAidDeviceConnectionInfo info =
@@ -867,8 +885,10 @@
+ " addr=" + info.mDevice.getAddress()
+ " supprNoisy=" + info.mSupprNoisy
+ " src=" + info.mEventSource)).printLog(TAG));
- mDeviceInventory.setBluetoothHearingAidDeviceConnectionState(
- info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice);
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.setBluetoothHearingAidDeviceConnectionState(
+ info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice);
+ }
} break;
default:
Log.wtf(TAG, "Invalid message " + msg.what);
@@ -953,57 +973,46 @@
/** If the msg is already queued, queue this one and leave the old. */
private static final int SENDMSG_QUEUE = 2;
- @GuardedBy("mDeviceBrokerLock")
private void sendMsg(int msg, int existingMsgPolicy, int delay) {
sendIILMsg(msg, existingMsgPolicy, 0, 0, null, delay);
}
- @GuardedBy("mDeviceBrokerLock")
private void sendILMsg(int msg, int existingMsgPolicy, int arg, Object obj, int delay) {
sendIILMsg(msg, existingMsgPolicy, arg, 0, obj, delay);
}
- @GuardedBy("mDeviceBrokerLock")
private void sendLMsg(int msg, int existingMsgPolicy, Object obj, int delay) {
sendIILMsg(msg, existingMsgPolicy, 0, 0, obj, delay);
}
- @GuardedBy("mDeviceBrokerLock")
private void sendIMsg(int msg, int existingMsgPolicy, int arg, int delay) {
sendIILMsg(msg, existingMsgPolicy, arg, 0, null, delay);
}
- @GuardedBy("mDeviceBrokerLock")
private void sendMsgNoDelay(int msg, int existingMsgPolicy) {
sendIILMsg(msg, existingMsgPolicy, 0, 0, null, 0);
}
- @GuardedBy("mDeviceBrokerLock")
private void sendIMsgNoDelay(int msg, int existingMsgPolicy, int arg) {
sendIILMsg(msg, existingMsgPolicy, arg, 0, null, 0);
}
- @GuardedBy("mDeviceBrokerLock")
private void sendIIMsgNoDelay(int msg, int existingMsgPolicy, int arg1, int arg2) {
sendIILMsg(msg, existingMsgPolicy, arg1, arg2, null, 0);
}
- @GuardedBy("mDeviceBrokerLock")
private void sendILMsgNoDelay(int msg, int existingMsgPolicy, int arg, Object obj) {
sendIILMsg(msg, existingMsgPolicy, arg, 0, obj, 0);
}
- @GuardedBy("mDeviceBrokerLock")
private void sendLMsgNoDelay(int msg, int existingMsgPolicy, Object obj) {
sendIILMsg(msg, existingMsgPolicy, 0, 0, obj, 0);
}
- @GuardedBy("mDeviceBrokerLock")
private void sendIILMsgNoDelay(int msg, int existingMsgPolicy, int arg1, int arg2, Object obj) {
sendIILMsg(msg, existingMsgPolicy, arg1, arg2, obj, 0);
}
- @GuardedBy("mDeviceBrokerLock")
private void sendIILMsg(int msg, int existingMsgPolicy, int arg1, int arg2, Object obj,
int delay) {
if (existingMsgPolicy == SENDMSG_REPLACE) {
@@ -1022,29 +1031,31 @@
Binder.restoreCallingIdentity(identity);
}
- long time = SystemClock.uptimeMillis() + delay;
+ synchronized (sLastDeviceConnectionMsgTimeLock) {
+ long time = SystemClock.uptimeMillis() + delay;
- switch (msg) {
- case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
- case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED:
- case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
- case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
- case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
- case MSG_IL_BTA2DP_DOCK_TIMEOUT:
- case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
- case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
- if (sLastDeviceConnectMsgTime >= time) {
- // add a little delay to make sure messages are ordered as expected
- time = sLastDeviceConnectMsgTime + 30;
- }
- sLastDeviceConnectMsgTime = time;
- break;
- default:
- break;
+ switch (msg) {
+ case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
+ case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED:
+ case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
+ case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
+ case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
+ case MSG_IL_BTA2DP_DOCK_TIMEOUT:
+ case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
+ case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
+ if (sLastDeviceConnectMsgTime >= time) {
+ // add a little delay to make sure messages are ordered as expected
+ time = sLastDeviceConnectMsgTime + 30;
+ }
+ sLastDeviceConnectMsgTime = time;
+ break;
+ default:
+ break;
+ }
+
+ mBrokerHandler.sendMessageAtTime(mBrokerHandler.obtainMessage(msg, arg1, arg2, obj),
+ time);
}
-
- mBrokerHandler.sendMessageAtTime(mBrokerHandler.obtainMessage(msg, arg1, arg2, obj),
- time);
}
//-------------------------------------------------------------
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 3933fb2..90973a8 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -159,6 +159,7 @@
}
// only public for mocking/spying
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@VisibleForTesting
public void onSetA2dpSinkConnectionState(@NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo,
@AudioService.BtProfileConnectionState int state) {
@@ -283,6 +284,7 @@
}
}
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ void onBluetoothA2dpActiveDeviceChange(
@NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int event) {
final BluetoothDevice btDevice = btInfo.getBtDevice();
@@ -555,6 +557,7 @@
}
// only public for mocking/spying
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@VisibleForTesting
public void setBluetoothA2dpDeviceConnectionState(
@NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index cc50e37..0d493b8 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -470,11 +470,12 @@
// List of binder death handlers for setMode() client processes.
// The last process to have called setMode() is at the top of the list.
- private final ArrayList<SetModeDeathHandler> mSetModeDeathHandlers =
+ // package-private so it can be accessed in AudioDeviceBroker.getSetModeDeathHandlers
+ //TODO candidate to be moved to separate class that handles synchronization
+ @GuardedBy("mDeviceBroker.mSetModeLock")
+ /*package*/ final ArrayList<SetModeDeathHandler> mSetModeDeathHandlers =
new ArrayList<SetModeDeathHandler>();
- private volatile int mCurrentModeOwnerPid = 0;
-
// true if boot sequence has been completed
private boolean mSystemReady;
// true if Intent.ACTION_USER_SWITCHED has ever been received
@@ -3191,10 +3192,15 @@
* @return 0 if nobody owns the mode
*/
/*package*/ int getModeOwnerPid() {
- return mCurrentModeOwnerPid;
+ int modeOwnerPid = 0;
+ try {
+ modeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
+ } catch (Exception e) {
+ // nothing to do, modeOwnerPid is not modified
+ }
+ return modeOwnerPid;
}
-
private class SetModeDeathHandler implements IBinder.DeathRecipient {
private IBinder mCb; // To be notified of client's death
private int mPid;
@@ -3208,7 +3214,7 @@
public void binderDied() {
int oldModeOwnerPid = 0;
int newModeOwnerPid = 0;
- synchronized (mSetModeDeathHandlers) {
+ synchronized (mDeviceBroker.mSetModeLock) {
Log.w(TAG, "setMode() client died");
if (!mSetModeDeathHandlers.isEmpty()) {
oldModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
@@ -3219,15 +3225,11 @@
} else {
newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid, TAG);
}
-
- if (newModeOwnerPid != oldModeOwnerPid) {
- mCurrentModeOwnerPid = newModeOwnerPid;
- // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all SCO
- // connections not started by the application changing the mode when pid changes
- if (newModeOwnerPid != 0) {
- mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid);
- }
- }
+ }
+ // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
+ // SCO connections not started by the application changing the mode when pid changes
+ if ((newModeOwnerPid != oldModeOwnerPid) && (newModeOwnerPid != 0)) {
+ mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid);
}
}
@@ -3250,17 +3252,15 @@
/** @see AudioManager#setMode(int) */
public void setMode(int mode, IBinder cb, String callingPackage) {
- if (DEBUG_MODE) {
- Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")");
- }
+ if (DEBUG_MODE) { Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")"); }
if (!checkAudioSettingsPermission("setMode()")) {
return;
}
- if ((mode == AudioSystem.MODE_IN_CALL)
- && (mContext.checkCallingOrSelfPermission(
+ if ( (mode == AudioSystem.MODE_IN_CALL) &&
+ (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE)
- != PackageManager.PERMISSION_GRANTED)) {
+ != PackageManager.PERMISSION_GRANTED)) {
Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
return;
@@ -3272,7 +3272,7 @@
int oldModeOwnerPid = 0;
int newModeOwnerPid = 0;
- synchronized (mSetModeDeathHandlers) {
+ synchronized (mDeviceBroker.mSetModeLock) {
if (!mSetModeDeathHandlers.isEmpty()) {
oldModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
}
@@ -3280,21 +3280,17 @@
mode = mMode;
}
newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid(), callingPackage);
-
- if (newModeOwnerPid != oldModeOwnerPid) {
- mCurrentModeOwnerPid = newModeOwnerPid;
- // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
- // SCO connections not started by the application changing the mode when pid changes
- if (newModeOwnerPid != 0) {
- mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid);
- }
- }
+ }
+ // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
+ // SCO connections not started by the application changing the mode when pid changes
+ if ((newModeOwnerPid != oldModeOwnerPid) && (newModeOwnerPid != 0)) {
+ mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid);
}
}
// setModeInt() returns a valid PID if the audio mode was successfully set to
// any mode other than NORMAL.
- @GuardedBy("mSetModeDeathHandlers")
+ @GuardedBy("mDeviceBroker.mSetModeLock")
private int setModeInt(int mode, IBinder cb, int pid, String caller) {
if (DEBUG_MODE) { Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid + ", caller="
+ caller + ")"); }
@@ -3633,7 +3629,9 @@
!mSystemReady) {
return;
}
- mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource);
+ synchronized (mDeviceBroker.mSetModeLock) {
+ mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource);
+ }
}
/** @see AudioManager#stopBluetoothSco() */
@@ -3645,7 +3643,9 @@
final String eventSource = new StringBuilder("stopBluetoothSco()")
.append(") from u/pid:").append(Binder.getCallingUid()).append("/")
.append(Binder.getCallingPid()).toString();
- mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource);
+ synchronized (mDeviceBroker.mSetModeLock) {
+ mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource);
+ }
}
@@ -4406,7 +4406,7 @@
// NOTE: Locking order for synchronized objects related to volume or ringer mode management:
// 1 mScoclient OR mSafeMediaVolumeState
- // 2 mSetModeDeathHandlers
+ // 2 mSetModeLock
// 3 mSettingsLock
// 4 VolumeStreamState.class
private class VolumeStreamState {
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 625b6b6..9f1a6bd 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -171,6 +171,8 @@
//----------------------------------------------------------------------
// Interface for AudioDeviceBroker
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void onSystemReady() {
mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR;
resetBluetoothSco();
@@ -243,6 +245,8 @@
return mapBluetoothCodecToAudioFormat(btCodecConfig.getCodecType());
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void receiveBtEvent(Intent intent) {
final String action = intent.getAction();
if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
@@ -329,6 +333,8 @@
*
* @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept
*/
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void disconnectBluetoothSco(int exceptPid) {
checkScoAudioState();
if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
@@ -337,6 +343,8 @@
clearAllScoClients(exceptPid, true);
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode,
@NonNull String eventSource) {
ScoClient client = getScoClient(cb, true);
@@ -356,6 +364,8 @@
Binder.restoreCallingIdentity(ident);
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void stopBluetoothScoForClient(IBinder cb,
@NonNull String eventSource) {
ScoClient client = getScoClient(cb, false);
@@ -413,6 +423,8 @@
mDeviceBroker.postDisconnectHearingAid();
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void resetBluetoothSco() {
clearAllScoClients(0, false);
mScoAudioState = SCO_STATE_INACTIVE;
@@ -421,6 +433,8 @@
mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco");
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void disconnectHeadset() {
setBtScoActiveDevice(null);
mBluetoothHeadset = null;
@@ -466,6 +480,8 @@
/*eventSource*/ "mBluetoothProfileServiceListener");
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void onHeadsetProfileConnected(BluetoothHeadset headset) {
// Discard timeout message
mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService();
@@ -552,6 +568,8 @@
return result;
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
private void setBtScoActiveDevice(BluetoothDevice btDevice) {
Log.i(TAG, "setBtScoActiveDevice: " + mBluetoothHeadsetDevice + " -> " + btDevice);
@@ -634,6 +652,8 @@
};
//----------------------------------------------------------------------
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void scoClientDied(Object obj) {
final ScoClient client = (ScoClient) obj;
Log.w(TAG, "SCO client died");
@@ -664,6 +684,8 @@
mDeviceBroker.postScoClientDied(this);
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ // @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
void incCount(int scoAudioMode) {
if (!requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode)) {
@@ -683,6 +705,8 @@
mStartcount++;
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ // @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
void decCount() {
if (mStartcount == 0) {
@@ -702,6 +726,8 @@
}
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ // @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
void clearCount(boolean stopSco) {
if (mStartcount != 0) {
@@ -738,6 +764,8 @@
return count;
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
private boolean requestScoState(int state, int scoAudioMode) {
checkScoAudioState();
@@ -931,6 +959,8 @@
return null;
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
private void clearAllScoClients(int exceptPid, boolean stopSco) {
ScoClient savedClient = null;
diff --git a/services/core/java/com/android/server/display/color/DisplayTransformManager.java b/services/core/java/com/android/server/display/color/DisplayTransformManager.java
index d5706a5..3b0069c 100644
--- a/services/core/java/com/android/server/display/color/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/color/DisplayTransformManager.java
@@ -111,6 +111,8 @@
@GuardedBy("mDaltonizerModeLock")
private int mDaltonizerMode = -1;
+ private static final IBinder sFlinger = ServiceManager.getService(SURFACE_FLINGER);
+
/* package */ DisplayTransformManager() {
}
@@ -195,25 +197,22 @@
* Propagates the provided color transformation matrix to the SurfaceFlinger.
*/
private static void applyColorMatrix(float[] m) {
- final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER);
- if (flinger != null) {
- final Parcel data = Parcel.obtain();
- data.writeInterfaceToken("android.ui.ISurfaceComposer");
- if (m != null) {
- data.writeInt(1);
- for (int i = 0; i < 16; i++) {
- data.writeFloat(m[i]);
- }
- } else {
- data.writeInt(0);
+ final Parcel data = Parcel.obtain();
+ data.writeInterfaceToken("android.ui.ISurfaceComposer");
+ if (m != null) {
+ data.writeInt(1);
+ for (int i = 0; i < 16; i++) {
+ data.writeFloat(m[i]);
}
- try {
- flinger.transact(SURFACE_FLINGER_TRANSACTION_COLOR_MATRIX, data, null, 0);
- } catch (RemoteException ex) {
- Slog.e(TAG, "Failed to set color transform", ex);
- } finally {
- data.recycle();
- }
+ } else {
+ data.writeInt(0);
+ }
+ try {
+ sFlinger.transact(SURFACE_FLINGER_TRANSACTION_COLOR_MATRIX, data, null, 0);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed to set color transform", ex);
+ } finally {
+ data.recycle();
}
}
@@ -221,18 +220,15 @@
* Propagates the provided Daltonization mode to the SurfaceFlinger.
*/
private static void applyDaltonizerMode(int mode) {
- final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER);
- if (flinger != null) {
- final Parcel data = Parcel.obtain();
- data.writeInterfaceToken("android.ui.ISurfaceComposer");
- data.writeInt(mode);
- try {
- flinger.transact(SURFACE_FLINGER_TRANSACTION_DALTONIZER, data, null, 0);
- } catch (RemoteException ex) {
- Slog.e(TAG, "Failed to set Daltonizer mode", ex);
- } finally {
- data.recycle();
- }
+ final Parcel data = Parcel.obtain();
+ data.writeInterfaceToken("android.ui.ISurfaceComposer");
+ data.writeInt(mode);
+ try {
+ sFlinger.transact(SURFACE_FLINGER_TRANSACTION_DALTONIZER, data, null, 0);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed to set Daltonizer mode", ex);
+ } finally {
+ data.recycle();
}
}
@@ -286,20 +282,17 @@
* #SURFACE_FLINGER_TRANSACTION_QUERY_COLOR_MANAGED}.
*/
public boolean isDeviceColorManaged() {
- final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER);
- if (flinger != null) {
- final Parcel data = Parcel.obtain();
- final Parcel reply = Parcel.obtain();
- data.writeInterfaceToken("android.ui.ISurfaceComposer");
- try {
- flinger.transact(SURFACE_FLINGER_TRANSACTION_QUERY_COLOR_MANAGED, data, reply, 0);
- return reply.readBoolean();
- } catch (RemoteException ex) {
- Slog.e(TAG, "Failed to query wide color support", ex);
- } finally {
- data.recycle();
- reply.recycle();
- }
+ final Parcel data = Parcel.obtain();
+ final Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken("android.ui.ISurfaceComposer");
+ try {
+ sFlinger.transact(SURFACE_FLINGER_TRANSACTION_QUERY_COLOR_MANAGED, data, reply, 0);
+ return reply.readBoolean();
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed to query wide color support", ex);
+ } finally {
+ data.recycle();
+ reply.recycle();
}
return false;
}
@@ -309,18 +302,15 @@
*/
private void applySaturation(float saturation) {
SystemProperties.set(PERSISTENT_PROPERTY_SATURATION, Float.toString(saturation));
- final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER);
- if (flinger != null) {
- final Parcel data = Parcel.obtain();
- data.writeInterfaceToken("android.ui.ISurfaceComposer");
- data.writeFloat(saturation);
- try {
- flinger.transact(SURFACE_FLINGER_TRANSACTION_SATURATION, data, null, 0);
- } catch (RemoteException ex) {
- Slog.e(TAG, "Failed to set saturation", ex);
- } finally {
- data.recycle();
- }
+ final Parcel data = Parcel.obtain();
+ data.writeInterfaceToken("android.ui.ISurfaceComposer");
+ data.writeFloat(saturation);
+ try {
+ sFlinger.transact(SURFACE_FLINGER_TRANSACTION_SATURATION, data, null, 0);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed to set saturation", ex);
+ } finally {
+ data.recycle();
}
}
@@ -334,21 +324,18 @@
Integer.toString(compositionColorMode));
}
- final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER);
- if (flinger != null) {
- final Parcel data = Parcel.obtain();
- data.writeInterfaceToken("android.ui.ISurfaceComposer");
- data.writeInt(color);
- if (compositionColorMode != Display.COLOR_MODE_INVALID) {
- data.writeInt(compositionColorMode);
- }
- try {
- flinger.transact(SURFACE_FLINGER_TRANSACTION_DISPLAY_COLOR, data, null, 0);
- } catch (RemoteException ex) {
- Slog.e(TAG, "Failed to set display color", ex);
- } finally {
- data.recycle();
- }
+ final Parcel data = Parcel.obtain();
+ data.writeInterfaceToken("android.ui.ISurfaceComposer");
+ data.writeInt(color);
+ if (compositionColorMode != Display.COLOR_MODE_INVALID) {
+ data.writeInt(compositionColorMode);
+ }
+ try {
+ sFlinger.transact(SURFACE_FLINGER_TRANSACTION_DISPLAY_COLOR, data, null, 0);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed to set display color", ex);
+ } finally {
+ data.recycle();
}
}
diff --git a/services/core/java/com/android/server/location/GeocoderProxy.java b/services/core/java/com/android/server/location/GeocoderProxy.java
index d9602b8..e6f0ed9 100644
--- a/services/core/java/com/android/server/location/GeocoderProxy.java
+++ b/services/core/java/com/android/server/location/GeocoderProxy.java
@@ -21,7 +21,7 @@
import android.location.GeocoderParams;
import android.location.IGeocodeProvider;
-import com.android.server.FgThread;
+import com.android.internal.os.BackgroundThread;
import com.android.server.ServiceWatcher;
import java.util.List;
@@ -53,7 +53,7 @@
int initialPackageNamesResId) {
mServiceWatcher = new ServiceWatcher(context, TAG, SERVICE_ACTION, overlaySwitchResId,
defaultServicePackageNameResId, initialPackageNamesResId,
- FgThread.getHandler());
+ BackgroundThread.getHandler());
}
private boolean bind() {
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index c8179a7..dc0cd18 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -41,7 +41,6 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.FgThread;
-import com.android.server.compat.CompatConfig;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -131,11 +130,11 @@
private static class FeatureConfigImpl implements FeatureConfig {
private static final String FILTERING_ENABLED_NAME = "package_query_filtering_enabled";
+ private final PackageManagerService.Injector mInjector;
private volatile boolean mFeatureEnabled = false;
- private CompatConfig mCompatibility;
private FeatureConfigImpl(PackageManagerService.Injector injector) {
- mCompatibility = injector.getCompatibility();
+ mInjector = injector;
}
@Override
@@ -158,7 +157,7 @@
@Override
public boolean packageIsEnabled(PackageParser.Package pkg) {
- return mCompatibility.isChangeEnabled(
+ return mInjector.getCompatibility().isChangeEnabled(
PackageManager.FILTER_APPLICATION_QUERY, pkg.applicationInfo);
}
}
@@ -263,10 +262,10 @@
* Grants access based on an interaction between a calling and target package, granting
* visibility of the caller from the target.
*
- * @param callingPackage the package initiating the interaction
- * @param targetPackage the package being interacted with and thus gaining visibility of the
- * initiating package.
- * @param userId the user in which this interaction was taking place
+ * @param callingPackage the package initiating the interaction
+ * @param targetPackage the package being interacted with and thus gaining visibility of the
+ * initiating package.
+ * @param userId the user in which this interaction was taking place
*/
public void grantImplicitAccess(
String callingPackage, String targetPackage, int userId) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 74a85d5..b36958a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -299,7 +299,7 @@
import com.android.server.SystemConfig;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.Watchdog;
-import com.android.server.compat.CompatConfig;
+import com.android.server.compat.PlatformCompat;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.Settings.DatabaseVersion;
@@ -837,7 +837,7 @@
private final Singleton<StorageManager> mStorageManagerProducer;
private final Singleton<AppOpsManager> mAppOpsManagerProducer;
private final Singleton<AppsFilter> mAppsFilterProducer;
- private final Singleton<CompatConfig> mPlatformCompatProducer;
+ private final Singleton<PlatformCompat> mPlatformCompatProducer;
Injector(Context context, Object lock, Installer installer,
Object installLock, PackageAbiHelper abiHelper,
@@ -855,7 +855,7 @@
Producer<StorageManager> storageManagerProducer,
Producer<AppOpsManager> appOpsManagerProducer,
Producer<AppsFilter> appsFilterProducer,
- Producer<CompatConfig> platformCompatProducer) {
+ Producer<PlatformCompat> platformCompatProducer) {
mContext = context;
mLock = lock;
mInstaller = installer;
@@ -966,7 +966,7 @@
return mAppsFilterProducer.get(this, mPackageManager);
}
- public CompatConfig getCompatibility() {
+ public PlatformCompat getCompatibility() {
return mPlatformCompatProducer.get(this, mPackageManager);
}
}
@@ -2356,7 +2356,7 @@
new Injector.SystemServiceProducer<>(StorageManager.class),
new Injector.SystemServiceProducer<>(AppOpsManager.class),
(i, pm) -> AppsFilter.create(i),
- (i, pm) -> CompatConfig.get());
+ (i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"));
PackageManagerService m = new PackageManagerService(injector, factoryTest, onlyCore);
t.traceEnd(); // "create package manager"
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index ff0dc54..f87175d 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2354,7 +2354,7 @@
mMovedToFront = true;
}
- if (launchStack.topTask() == null) {
+ if (launchStack != null && launchStack.topTask() == null) {
// The task does not need to be reparented to the launch stack. Remove the
// launch stack if there is no activity in it.
Slog.w(TAG, "Removing an empty stack: " + launchStack);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index a783ee9..6e238b3 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1920,10 +1920,18 @@
vf.set(displayFrames.mStable);
if (adjust == SOFT_INPUT_ADJUST_RESIZE) {
- cf.bottom = displayFrames.mContent.bottom;
+ // cf.bottom should not be below the stable bottom, or the content might be obscured
+ // by the navigation bar.
+ if (cf.bottom > displayFrames.mContent.bottom) {
+ cf.bottom = displayFrames.mContent.bottom;
+ }
} else {
- cf.bottom = displayFrames.mDock.bottom;
- vf.bottom = displayFrames.mContent.bottom;
+ if (cf.bottom > displayFrames.mDock.bottom) {
+ cf.bottom = displayFrames.mDock.bottom;
+ }
+ if (vf.bottom > displayFrames.mContent.bottom) {
+ vf.bottom = displayFrames.mContent.bottom;
+ }
}
} else {
dcf.set(displayFrames.mSystem);
diff --git a/services/core/java/com/android/server/wm/PolicyControl.java b/services/core/java/com/android/server/wm/PolicyControl.java
index 4c8ce9e..0f92bc8 100644
--- a/services/core/java/com/android/server/wm/PolicyControl.java
+++ b/services/core/java/com/android/server/wm/PolicyControl.java
@@ -26,6 +26,8 @@
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -51,7 +53,8 @@
private static final String TAG = "PolicyControl";
private static final boolean DEBUG = false;
- private static final String NAME_IMMERSIVE_FULL = "immersive.full";
+ @VisibleForTesting
+ static final String NAME_IMMERSIVE_FULL = "immersive.full";
private static final String NAME_IMMERSIVE_STATUS = "immersive.status";
private static final String NAME_IMMERSIVE_NAVIGATION = "immersive.navigation";
private static final String NAME_IMMERSIVE_PRECONFIRMATIONS = "immersive.preconfirms";
@@ -67,15 +70,19 @@
: (attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility);
if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) {
vis |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
- | View.SYSTEM_UI_FLAG_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+ | View.SYSTEM_UI_FLAG_FULLSCREEN;
+ if (attrs.isFullscreen()) {
+ vis |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+ }
vis &= ~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.STATUS_BAR_TRANSLUCENT);
}
if (sImmersiveNavigationFilter != null && sImmersiveNavigationFilter.matches(attrs)) {
vis |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+ if (attrs.isFullscreen()) {
+ vis |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+ }
vis &= ~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.NAVIGATION_BAR_TRANSLUCENT);
}
@@ -144,7 +151,8 @@
}
}
- private static void setFilters(String value) {
+ @VisibleForTesting
+ static void setFilters(String value) {
if (DEBUG) Slog.d(TAG, "setFilters: " + value);
sImmersiveStatusFilter = null;
sImmersiveNavigationFilter = null;
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index a8c7682..64c7935 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -190,7 +190,7 @@
}
}
-static jlong vibratorPerformEffect(JNIEnv* env, jclass, jlong effect, jint strength,
+static jlong vibratorPerformEffect(JNIEnv* env, jclass, jlong effect, jlong strength,
jobject vibration) {
Status status;
uint32_t lengthMs;
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 2ab8189..e24dec5 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -1,7 +1,7 @@
java_library_static {
name: "services.net",
srcs: [
- ":tethering-services-srcs",
+ ":tethering-servicesnet-srcs",
"java/**/*.java",
],
static_libs: [
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index 4d653b9..d34f783 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -23,6 +23,7 @@
<option name="test-file-name" value="JobTestApp.apk" />
<option name="test-file-name" value="ConnTestApp.apk" />
<option name="test-file-name" value="SuspendTestApp.apk" />
+ <option name="test-file-name" value="SimpleServiceTestApp.apk" />
</target_preparer>
<option name="test-tag" value="FrameworksServicesTests" />
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index 1ad7b6e..79af34d 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -191,7 +191,7 @@
@After
public void tearDown() throws Exception {
mHandlerThread.quitSafely();
- LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.removeServiceForTest(PermissionManagerServiceInternal.class);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
index 8965152..129d263 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
@@ -19,20 +19,34 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.app.ActivityManager;
import android.app.ActivityManager.RecentTaskInfo;
import android.app.IActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.os.IBinder;
+import android.os.IRemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.support.test.uiautomator.UiDevice;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.FlakyTest;
import org.junit.Before;
import org.junit.Test;
+import java.io.IOException;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* Tests for {@link ActivityManager}.
@@ -43,12 +57,22 @@
@FlakyTest(detail = "Promote to presubmit if stable")
@Presubmit
public class ActivityManagerTest {
+ private static final String TAG = "ActivityManagerTest";
+
+ private static final String TEST_APP = "com.android.servicestests.apps.simpleservicetestapp";
+ private static final String TEST_CLASS = TEST_APP + ".SimpleService";
+ private static final int TEST_LOOPS = 100;
+ private static final long AWAIT_TIMEOUT = 2000;
+ private static final long CHECK_INTERVAL = 100;
private IActivityManager mService;
+ private IRemoteCallback mCallback;
+ private Context mContext;
@Before
public void setUp() throws Exception {
mService = ActivityManager.getService();
+ mContext = InstrumentationRegistry.getTargetContext();
}
@Test
@@ -72,4 +96,112 @@
}
}
}
+
+ @Test
+ public void testServiceUnbindAndKilling() {
+ for (int i = TEST_LOOPS; i > 0; i--) {
+ runOnce(i);
+ }
+ }
+
+ private void runOnce(long yieldDuration) {
+ final PackageManager pm = mContext.getPackageManager();
+ int uid = 0;
+ try {
+ uid = pm.getPackageUid(TEST_APP, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ Intent intent = new Intent();
+ intent.setClassName(TEST_APP, TEST_CLASS);
+
+ // Create a service connection with auto creation.
+ CountDownLatch latch = new CountDownLatch(1);
+ final MyServiceConnection autoConnection = new MyServiceConnection(latch);
+ mContext.bindService(intent, autoConnection, Context.BIND_AUTO_CREATE);
+ try {
+ assertTrue("Timeout to bind to service " + intent.getComponent(),
+ latch.await(AWAIT_TIMEOUT, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ fail("Unable to bind to service " + intent.getComponent());
+ }
+
+ // Create a service connection without any flags.
+ intent = new Intent();
+ intent.setClassName(TEST_APP, TEST_CLASS);
+ latch = new CountDownLatch(1);
+ MyServiceConnection otherConnection = new MyServiceConnection(latch);
+ mContext.bindService(intent, otherConnection, 0);
+ try {
+ assertTrue("Timeout to bind to service " + intent.getComponent(),
+ latch.await(AWAIT_TIMEOUT, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ fail("Unable to bind to service " + intent.getComponent());
+ }
+
+ // Inform the remote process to kill itself
+ try {
+ mCallback.sendResult(null);
+ // It's basically a test for race condition, we expect the bringDownServiceLocked()
+ // would find out the hosting process is dead - to do this, technically we should
+ // do killing and unbinding simultaneously; but in reality, the killing would take
+ // a little while, before the signal really kills it; so we do it in the same thread,
+ // and even wait a while after sending killing signal.
+ Thread.sleep(yieldDuration);
+ } catch (RemoteException | InterruptedException e) {
+ fail("Unable to kill the process");
+ }
+ // Now unbind that auto connection, this should be equivalent to stopService
+ mContext.unbindService(autoConnection);
+
+ // Now we don't expect the system_server crashes.
+
+ // Wait for the target process dies
+ long total = 0;
+ for (; total < AWAIT_TIMEOUT; total += CHECK_INTERVAL) {
+ try {
+ if (!targetPackageIsRunning(mContext, uid)) {
+ break;
+ }
+ Thread.sleep(CHECK_INTERVAL);
+ } catch (InterruptedException e) {
+ }
+ }
+ assertTrue("Timeout to wait for the target package dies", total < AWAIT_TIMEOUT);
+ mCallback = null;
+ }
+
+ private boolean targetPackageIsRunning(Context context, int uid) {
+ final String result = runShellCommand(
+ String.format("cmd activity get-uid-state %d", uid));
+ return !result.contains("(NONEXISTENT)");
+ }
+
+ private static String runShellCommand(String cmd) {
+ try {
+ return UiDevice.getInstance(
+ InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private class MyServiceConnection implements ServiceConnection {
+ private CountDownLatch mLatch;
+
+ MyServiceConnection(CountDownLatch latch) {
+ this.mLatch = latch;
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mCallback = IRemoteCallback.Stub.asInterface(service);
+ mLatch.countDown();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
index 29a8dad..5c2ad94 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
@@ -126,22 +126,6 @@
doTestConnectionDisconnectionReconnection(AudioService.BECOMING_NOISY_DELAY_MS / 2);
}
- /**
- * Verify connecting an A2DP sink will call into AudioService to unmute media
- */
- @Test
- public void testA2dpConnectionUnmutesMedia() throws Exception {
- Log.i(TAG, "testA2dpConnectionUnmutesMedia");
- Assert.assertNotNull("invalid null BT device", mFakeBtDevice);
-
- mAudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(mFakeBtDevice,
- BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1);
- Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
- verify(mMockAudioService, times(1)).postAccessoryPlugMediaUnmute(
- ArgumentMatchers.eq(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
-
- }
-
private void doTestConnectionDisconnectionReconnection(int delayAfterDisconnection)
throws Exception {
when(mMockAudioService.getDeviceForStream(AudioManager.STREAM_MUSIC))
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp b/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp
new file mode 100644
index 0000000..5cbd39c
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+ name: "SimpleServiceTestApp",
+
+ test_suites: ["device-tests"],
+
+ srcs: ["**/*.java"],
+
+ platform_apis: true,
+ certificate: "platform",
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..8789992
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.servicestests.apps.simpleservicetestapp">
+
+ <application>
+ <service android:name=".SimpleService"
+ android:exported="true" />
+ </application>
+
+</manifest>
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java
new file mode 100644
index 0000000..75f71d6
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.servicestests.apps.simpleservicetestapp;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.Process;
+
+public class SimpleService extends Service {
+ private final IRemoteCallback.Stub mBinder = new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle bundle) {
+ Process.killProcess(Process.myPid());
+ }
+ };
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 80439cf..a1322b9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -62,7 +62,6 @@
import android.provider.Settings;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
-
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.TestableContentResolver;
import android.util.ArrayMap;
@@ -162,11 +161,11 @@
when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider);
contentResolver.addProvider(TEST_AUTHORITY, testContentProvider);
- when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI)))
+ when(mTestIContentProvider.canonicalize(any(), any(), eq(SOUND_URI)))
.thenReturn(CANONICAL_SOUND_URI);
- when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
.thenReturn(CANONICAL_SOUND_URI);
- when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
.thenReturn(SOUND_URI);
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
@@ -465,7 +464,7 @@
// Testing that in restore we are given the canonical version
loadStreamXml(baos, true, UserHandle.USER_SYSTEM);
- verify(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI));
+ verify(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
}
@Test
@@ -475,11 +474,11 @@
.appendQueryParameter("title", "Test")
.appendQueryParameter("canonical", "1")
.build();
- when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
.thenReturn(canonicalBasedOnLocal);
- when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
.thenReturn(localUri);
- when(mTestIContentProvider.uncanonicalize(any(), eq(canonicalBasedOnLocal)))
+ when(mTestIContentProvider.uncanonicalize(any(), any(), eq(canonicalBasedOnLocal)))
.thenReturn(localUri);
NotificationChannel channel =
@@ -499,9 +498,9 @@
@Test
public void testRestoreXml_withNonExistentCanonicalizedSoundUri() throws Exception {
Thread.sleep(3000);
- when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
.thenReturn(null);
- when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
.thenReturn(null);
NotificationChannel channel =
@@ -526,7 +525,7 @@
@Test
public void testRestoreXml_withUncanonicalizedNonLocalSoundUri() throws Exception {
// Not a local uncanonicalized uri, simulating that it fails to exist locally
- when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI))).thenReturn(null);
+ when(mTestIContentProvider.canonicalize(any(), any(), eq(SOUND_URI))).thenReturn(null);
String id = "id";
String backupWithUncanonicalizedSoundUri = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 2abd340..8774b63 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -132,11 +132,11 @@
when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider);
contentResolver.addProvider(TEST_AUTHORITY, testContentProvider);
- when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI)))
+ when(mTestIContentProvider.canonicalize(any(), any(), eq(SOUND_URI)))
.thenReturn(CANONICAL_SOUND_URI);
- when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
.thenReturn(CANONICAL_SOUND_URI);
- when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
.thenReturn(SOUND_URI);
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
index 3b336eb..aceed86 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
@@ -12,6 +12,7 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -109,8 +110,8 @@
mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken);
TestableLooper.get(this).processAllMessages();
- verify(mIContentProvider).call(anyString(), anyString(), eq(SliceProvider.METHOD_PIN),
- eq(null), argThat(b -> {
+ verify(mIContentProvider).call(anyString(), nullable(String.class), anyString(),
+ eq(SliceProvider.METHOD_PIN), eq(null), argThat(b -> {
assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI));
return true;
}));
@@ -167,8 +168,8 @@
// Throw exception when trying to pin
doAnswer(invocation -> {
throw new Exception("Pin failed");
- }).when(mIContentProvider).call(
- anyString(), anyString(), anyString(), eq(null), any());
+ }).when(mIContentProvider).call(anyString(), nullable(String.class), anyString(),
+ anyString(), eq(null), any());
TestableLooper.get(this).processAllMessages();
@@ -176,8 +177,8 @@
mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken);
TestableLooper.get(this).processAllMessages();
- verify(mIContentProvider).call(anyString(), anyString(), eq(SliceProvider.METHOD_PIN),
- eq(null), argThat(b -> {
+ verify(mIContentProvider).call(anyString(), nullable(String.class), anyString(),
+ eq(SliceProvider.METHOD_PIN), eq(null), argThat(b -> {
assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI));
return true;
}));
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 2a3731a..67b7a66 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -24,6 +24,7 @@
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
+import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
@@ -56,15 +57,23 @@
import android.view.DisplayInfo;
import android.view.WindowManager;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.utils.WmDisplayCutout;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+/**
+ * Tests for the {@link DisplayPolicy} class.
+ *
+ * Build/Install/Run:
+ * atest WmTests:DisplayPolicyLayoutTests
+ */
@SmallTest
@Presubmit
@RunWith(WindowTestRunner.class)
@@ -93,6 +102,12 @@
attrs.format = PixelFormat.TRANSLUCENT;
}
+ @After
+ public void tearDown() {
+ PolicyControl.setFilters("");
+ mWindow.getDisplayContent().mInputMethodTarget = null;
+ }
+
public void setRotation(int rotation) {
mRotation = rotation;
updateDisplayFrames();
@@ -393,6 +408,105 @@
assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
}
+ @FlakyTest(bugId = 129711077)
+ @Test
+ public void layoutWindowLw_withImmersive_SoftInputAdjustResize() {
+ synchronized (mWm.mGlobalLock) {
+ mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_RESIZE;
+ mWindow.mAttrs.flags = 0;
+ mWindow.mAttrs.systemUiVisibility =
+ SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | SYSTEM_UI_FLAG_FULLSCREEN | SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+
+ addWindow(mWindow);
+
+ mWindow.getDisplayContent().mInputMethodTarget = mWindow;
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mFrames.mContent.bottom = mFrames.mVoiceContent.bottom = INPUT_METHOD_WINDOW_TOP;
+ mFrames.mCurrent.bottom = INPUT_METHOD_WINDOW_TOP;
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ int bottomInset = mFrames.mDisplayHeight - INPUT_METHOD_WINDOW_TOP;
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), 0, 0);
+ assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, bottomInset);
+ }
+ }
+
+ @FlakyTest(bugId = 129711077)
+ @Test
+ public void layoutWindowLw_withImmersive_SoftInputAdjustNothing() {
+ synchronized (mWm.mGlobalLock) {
+ mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_NOTHING;
+ mWindow.mAttrs.flags = 0;
+ mWindow.mAttrs.systemUiVisibility =
+ SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | SYSTEM_UI_FLAG_FULLSCREEN | SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+
+ addWindow(mWindow);
+
+ mWindow.getDisplayContent().mInputMethodTarget = mWindow;
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mFrames.mContent.bottom = mFrames.mVoiceContent.bottom = INPUT_METHOD_WINDOW_TOP;
+ mFrames.mCurrent.bottom = INPUT_METHOD_WINDOW_TOP;
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), 0, 0);
+ assertInsetByTopBottom(mWindow.getVisibleFrameLw(), 0, 0);
+ }
+ }
+
+ @FlakyTest(bugId = 129711077)
+ @Test
+ public void layoutWindowLw_withForceImmersive_fullscreen() {
+ synchronized (mWm.mGlobalLock) {
+ mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_RESIZE;
+ mWindow.mAttrs.flags = 0;
+ mWindow.mAttrs.systemUiVisibility = 0;
+ PolicyControl.setFilters(PolicyControl.NAME_IMMERSIVE_FULL + "=*");
+
+ addWindow(mWindow);
+
+ mWindow.getDisplayContent().mInputMethodTarget = mWindow;
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mFrames.mContent.bottom = mFrames.mVoiceContent.bottom = INPUT_METHOD_WINDOW_TOP;
+ mFrames.mCurrent.bottom = INPUT_METHOD_WINDOW_TOP;
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ int bottomInset = mFrames.mDisplayHeight - INPUT_METHOD_WINDOW_TOP;
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), 0, 0);
+ assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, bottomInset);
+ }
+ }
+
+ @FlakyTest(bugId = 129711077)
+ @Test
+ public void layoutWindowLw_withForceImmersive_nonFullscreen() {
+ synchronized (mWm.mGlobalLock) {
+ mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_RESIZE;
+ mWindow.mAttrs.flags = 0;
+ mWindow.mAttrs.systemUiVisibility = 0;
+ mWindow.mAttrs.width = DISPLAY_WIDTH / 2;
+ mWindow.mAttrs.height = DISPLAY_HEIGHT / 2;
+ PolicyControl.setFilters(PolicyControl.NAME_IMMERSIVE_FULL + "=*");
+
+ addWindow(mWindow);
+
+ mWindow.getDisplayContent().mInputMethodTarget = mWindow;
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mFrames.mContent.bottom = mFrames.mVoiceContent.bottom = INPUT_METHOD_WINDOW_TOP;
+ mFrames.mCurrent.bottom = INPUT_METHOD_WINDOW_TOP;
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ int bottomInset = mFrames.mDisplayHeight - INPUT_METHOD_WINDOW_TOP;
+ assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, bottomInset);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, bottomInset);
+ assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, bottomInset);
+ }
+ }
+
@Test
public void layoutHint_appWindow() {
// Initialize DisplayFrames
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index 2933b4a..d4558dc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -62,6 +62,7 @@
static final int STATUS_BAR_HEIGHT = 10;
static final int NAV_BAR_HEIGHT = 15;
static final int DISPLAY_CUTOUT_HEIGHT = 8;
+ static final int INPUT_METHOD_WINDOW_TOP = 585;
DisplayPolicy mDisplayPolicy;
diff --git a/services/usage/java/com/android/server/usage/TEST_MAPPING b/services/usage/java/com/android/server/usage/TEST_MAPPING
new file mode 100644
index 0000000..7b53d09
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/TEST_MAPPING
@@ -0,0 +1,33 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.app.usage"
+ }
+ ]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.usage"
+ },
+ {
+ "exclude-filter": "com.android.server.usage.StorageStatsServiceTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsUsageStatsTestCases",
+ "options": [
+ {
+ "include-filter": "android.app.usage.cts.UsageStatsTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index bc29b59..dc95f16 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -16,6 +16,7 @@
package android.provider;
+import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -3943,10 +3944,11 @@
}
/**
- * Contains received SMS cell broadcast messages. More details are available in 3GPP TS 23.041.
+ * Contains received cell broadcast messages. More details are available in 3GPP TS 23.041.
* @hide
*/
@SystemApi
+ @TestApi
public static final class CellBroadcasts implements BaseColumns {
/**
@@ -3957,11 +3959,44 @@
/**
* The {@code content://} URI for this table.
+ * Only privileged framework components running on phone or network stack uid can
+ * query or modify this table.
*/
@NonNull
public static final Uri CONTENT_URI = Uri.parse("content://cellbroadcasts");
/**
+ * The {@code content://} URI for query cellbroadcast message history.
+ * query results include following entries
+ * <ul>
+ * <li>{@link #_ID}</li>
+ * <li>{@link #SLOT_INDEX}</li>
+ * <li>{@link #GEOGRAPHICAL_SCOPE}</li>
+ * <li>{@link #PLMN}</li>
+ * <li>{@link #LAC}</li>
+ * <li>{@link #CID}</li>
+ * <li>{@link #SERIAL_NUMBER}</li>
+ * <li>{@link #SERVICE_CATEGORY}</li>
+ * <li>{@link #LANGUAGE_CODE}</li>
+ * <li>{@link #MESSAGE_BODY}</li>
+ * <li>{@link #DELIVERY_TIME}</li>
+ * <li>{@link #MESSAGE_READ}</li>
+ * <li>{@link #MESSAGE_FORMAT}</li>
+ * <li>{@link #MESSAGE_PRIORITY}</li>
+ * <li>{@link #ETWS_WARNING_TYPE}</li>
+ * <li>{@link #CMAS_MESSAGE_CLASS}</li>
+ * <li>{@link #CMAS_CATEGORY}</li>
+ * <li>{@link #CMAS_RESPONSE_TYPE}</li>
+ * <li>{@link #CMAS_SEVERITY}</li>
+ * <li>{@link #CMAS_URGENCY}</li>
+ * <li>{@link #CMAS_CERTAINTY}</li>
+ * </ul>
+ */
+ @RequiresPermission(Manifest.permission.READ_CELL_BROADCASTS)
+ @NonNull
+ public static final Uri MESSAGE_HISTORY_URI = Uri.parse("content://cellbroadcasts/history");
+
+ /**
* The subscription which received this cell broadcast message.
* @deprecated use {@link #SLOT_INDEX} instead.
* <P>Type: INTEGER</P>
@@ -3972,7 +4007,6 @@
/**
* The slot which received this cell broadcast message.
* <P>Type: INTEGER</P>
- * @hide
*/
public static final String SLOT_INDEX = "slot_index";
@@ -4150,14 +4184,12 @@
/**
* The timestamp in millisecond of when the device received the message.
* <P>Type: BIGINT</P>
- * @hide
*/
public static final String RECEIVED_TIME = "received_time";
/**
* Indicates that whether the message has been broadcasted to the application.
* <P>Type: BOOLEAN</P>
- * @hide
*/
public static final String MESSAGE_BROADCASTED = "message_broadcasted";
@@ -4193,7 +4225,6 @@
* "circle|0,0|100;polygon|0,0|0,1.5|1,1|1,0;circle|100.123,100|200.123"
*
* <P>Type: TEXT</P>
- * @hide
*/
public static final String GEOMETRIES = "geometries";
@@ -4205,7 +4236,6 @@
* for the alert.
*
* <P>Type: INTEGER</P>
- * @hide
*/
public static final String MAXIMUM_WAIT_TIME = "maximum_wait_time";
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 58e15b9..7bab223 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3224,6 +3224,14 @@
public static final String KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY =
"disconnect_cause_play_busytone_int_array";
+ /**
+ * Flag specifying whether to prevent sending CLIR activation("*31#") and deactivation("#31#")
+ * code only without dialing number.
+ * When {@code true}, these are prevented, {@code false} otherwise.
+ */
+ public static final String KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL =
+ "prevent_clir_activation_and_deactivation_code_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -3657,6 +3665,7 @@
sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, null);
sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY,
new int[] {4 /* BUSY */});
+ sDefaults.putBoolean(KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL, false);
}
/**
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 8425ec1..b5e91d0 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -49,7 +49,6 @@
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
-import android.os.Message;
import android.os.ParcelUuid;
import android.os.Process;
import android.os.RemoteException;
@@ -2053,13 +2052,13 @@
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static boolean isValidSlotIndex(int slotIndex) {
- return slotIndex >= 0 && slotIndex < TelephonyManager.getDefault().getSupportedModemCount();
+ return slotIndex >= 0 && slotIndex < TelephonyManager.getDefault().getActiveModemCount();
}
/** @hide */
@UnsupportedAppUsage
public static boolean isValidPhoneId(int phoneId) {
- return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getSupportedModemCount();
+ return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getActiveModemCount();
}
/** @hide */
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 2eb4809..8455e3d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1873,7 +1873,12 @@
if (telephony == null) return null;
try {
- return telephony.getMeidForSlot(slotIndex, getOpPackageName());
+ String meid = telephony.getMeidForSlot(slotIndex, getOpPackageName());
+ if (TextUtils.isEmpty(meid)) {
+ Log.d(TAG, "getMeid: return null because MEID is not available");
+ return null;
+ }
+ return meid;
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -9507,10 +9512,12 @@
}
/**
- * Resets telephony manager settings back to factory defaults.
+ * Resets Telephony and IMS settings back to factory defaults.
*
* @hide
*/
+ @SystemApi
+ @RequiresPermission(Manifest.permission.CONNECTIVITY_INTERNAL)
public void factoryReset(int subId) {
try {
Log.d(TAG, "factoryReset: subId=" + subId);
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 2161dcb..a5d6266 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1191,7 +1191,7 @@
&& !other.canHandleType(TYPE_DUN)
&& Objects.equals(this.mApnName, other.mApnName)
&& !typeSameAny(this, other)
- && xorEquals(this.mProxyAddress, other.mProxyAddress)
+ && xorEqualsString(this.mProxyAddress, other.mProxyAddress)
&& xorEqualsInt(this.mProxyPort, other.mProxyPort)
&& xorEquals(this.mProtocol, other.mProtocol)
&& xorEquals(this.mRoamingProtocol, other.mRoamingProtocol)
@@ -1200,7 +1200,7 @@
&& Objects.equals(this.mMvnoType, other.mMvnoType)
&& Objects.equals(this.mMvnoMatchData, other.mMvnoMatchData)
&& xorEquals(this.mMmsc, other.mMmsc)
- && xorEquals(this.mMmsProxyAddress, other.mMmsProxyAddress)
+ && xorEqualsString(this.mMmsProxyAddress, other.mMmsProxyAddress)
&& xorEqualsInt(this.mMmsProxyPort, other.mMmsProxyPort))
&& Objects.equals(this.mNetworkTypeBitmask, other.mNetworkTypeBitmask)
&& Objects.equals(mApnSetId, other.mApnSetId)
@@ -1213,6 +1213,11 @@
return first == null || second == null || first.equals(second);
}
+ // Equal or one is null.
+ private boolean xorEqualsString(String first, String second) {
+ return TextUtils.isEmpty(first) || TextUtils.isEmpty(second) || first.equals(second);
+ }
+
// Equal or one is not specified.
private boolean xorEqualsInt(int first, int second) {
return first == UNSPECIFIED_INT || second == UNSPECIFIED_INT
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 2fad847..ed292be 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -25,14 +25,13 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.telephony.AccessNetworkConstants;
+import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsRegistrationCallback;
@@ -42,6 +41,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.ITelephony;
import java.lang.annotation.Retention;
@@ -49,6 +49,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* A manager for the MmTel (Multimedia Telephony) feature of an IMS network, given an associated
@@ -64,8 +65,6 @@
@SystemApi
public class ImsMmTelManager {
- private static final String TAG = "ImsMmTelManager";
-
/**
* @hide
*/
@@ -311,7 +310,7 @@
}
}
- private int mSubId;
+ private final int mSubId;
/**
* Create an instance of {@link ImsMmTelManager} for the subscription id specified.
@@ -366,10 +365,6 @@
if (executor == null) {
throw new IllegalArgumentException("Must include a non-null Executor.");
}
- if (!isImsAvailableOnDevice()) {
- throw new ImsException("IMS not available on device.",
- ImsException.CODE_ERROR_UNSUPPORTED_OPERATION);
- }
c.setExecutor(executor);
try {
getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder());
@@ -378,7 +373,7 @@
// Rethrow as runtime error to keep API compatible.
throw new IllegalArgumentException(e.getMessage());
} else {
- throw new RuntimeException(e.getMessage());
+ throw new ImsException(e.getMessage(), e.errorCode);
}
} catch (RemoteException | IllegalStateException e) {
throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
@@ -441,10 +436,6 @@
if (executor == null) {
throw new IllegalArgumentException("Must include a non-null Executor.");
}
- if (!isImsAvailableOnDevice()) {
- throw new ImsException("IMS not available on device.",
- ImsException.CODE_ERROR_UNSUPPORTED_OPERATION);
- }
c.setExecutor(executor);
try {
getITelephony().registerMmTelCapabilityCallback(mSubId, c.getBinder());
@@ -453,7 +444,7 @@
// Rethrow as runtime error to keep API compatible.
throw new IllegalArgumentException(e.getMessage());
} else {
- throw new RuntimeException(e.getMessage());
+ throw new ImsException(e.getMessage(), e.errorCode);
}
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
@@ -618,6 +609,46 @@
}
/**
+ * Query whether or not the requested MmTel capability is supported by the carrier on the
+ * specified network transport.
+ * <p>
+ * This is a configuration option and does not change. The only time this may change is if a
+ * new IMS configuration is loaded when there is a
+ * {@link CarrierConfigManager#ACTION_CARRIER_CONFIG_CHANGED} broadcast for this subscription.
+ * @param capability The capability that is being queried for support on the carrier network.
+ * @param transportType The transport type of the capability to check support for.
+ * @param callback A consumer containing a Boolean result specifying whether or not the
+ * capability is supported on this carrier network for the transport specified.
+ * @param executor The executor that the callback will be called with.
+ * @throws ImsException if the subscription is no longer valid or the IMS service is not
+ * available.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void isSupported(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ @AccessNetworkConstants.TransportType int transportType,
+ @NonNull Consumer<Boolean> callback,
+ @NonNull @CallbackExecutor Executor executor) throws ImsException {
+ if (callback == null) {
+ throw new IllegalArgumentException("Must include a non-null Consumer.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
+ }
+ try {
+ getITelephony().isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ executor.execute(() -> callback.accept(result == 1));
+ }
+ }, capability, transportType);
+ } catch (ServiceSpecificException sse) {
+ throw new ImsException(sse.getMessage(), sse.errorCode);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
* The user's setting for whether or not they have enabled the "Video Calling" setting.
*
* @throws IllegalArgumentException if the subscription associated with this operation is not
@@ -940,7 +971,7 @@
* @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- boolean isTtyOverVolteEnabled() {
+ public boolean isTtyOverVolteEnabled() {
try {
return getITelephony().isTtyOverVolteEnabled(mSubId);
} catch (ServiceSpecificException e) {
@@ -955,20 +986,39 @@
}
}
- private static boolean isImsAvailableOnDevice() {
- IPackageManager pm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
- if (pm == null) {
- // For some reason package manger is not available.. This will fail internally anyways,
- // so do not throw error and allow.
- return true;
+ /**
+ * Get the status of the MmTel Feature registered on this subscription.
+ * @param callback A callback containing an Integer describing the current state of the
+ * MmTel feature, Which will be one of the following:
+ * {@link ImsFeature#STATE_UNAVAILABLE},
+ * {@link ImsFeature#STATE_INITIALIZING},
+ * {@link ImsFeature#STATE_READY}. Will be called using the executor
+ * specified when the service state has been retrieved from the IMS service.
+ * @param executor The executor that will be used to call the callback.
+ * @throws ImsException if the IMS service associated with this subscription is not available or
+ * the IMS service is not available.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void getFeatureState(@NonNull @ImsFeature.ImsState Consumer<Integer> callback,
+ @NonNull @CallbackExecutor Executor executor) throws ImsException {
+ if (callback == null) {
+ throw new IllegalArgumentException("Must include a non-null Consumer.");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must include a non-null Executor.");
}
try {
- return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS, 0);
+ getITelephony().getImsMmTelFeatureState(mSubId, new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ executor.execute(() -> callback.accept(result));
+ }
+ });
+ } catch (ServiceSpecificException sse) {
+ throw new ImsException(sse.getMessage(), sse.errorCode);
} catch (RemoteException e) {
- // For some reason package manger is not available.. This will fail internally anyways,
- // so do not throw error and allow.
+ e.rethrowAsRuntimeException();
}
- return true;
}
private static ITelephony getITelephony() {
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index ceb4704..8b27b6f 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -362,6 +362,25 @@
@Retention(RetentionPolicy.SOURCE)
public @interface ProcessCallResult {}
+ /**
+ * If the flag is present and true, it indicates that the incoming call is for USSD.
+ * <p>
+ * This is an optional boolean flag.
+ */
+ public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD";
+
+ /**
+ * If this flag is present and true, this call is marked as an unknown dialing call instead
+ * of an incoming call. An example of such a call is a call that is originated by sending
+ * commands (like AT commands) directly to the modem without Android involvement or dialing
+ * calls appearing over IMS when the modem does a silent redial from circuit-switched to IMS in
+ * certain situations.
+ * <p>
+ * This is an optional boolean flag.
+ */
+ public static final String EXTRA_IS_UNKNOWN_CALL =
+ "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL";
+
private IImsMmTelListener mListener;
/**
@@ -410,6 +429,8 @@
/**
* Notify the framework of an incoming call.
* @param c The {@link ImsCallSessionImplBase} of the new incoming call.
+ * @param extras A bundle containing extra parameters related to the call. See
+ * {@link #EXTRA_IS_UNKNOWN_CALL} and {@link #EXTRA_IS_USSD} above.
*/
public final void notifyIncomingCall(@NonNull ImsCallSessionImplBase c,
@NonNull Bundle extras) {
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index f79a5c6..b2f9add 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -867,6 +867,11 @@
String getImsService(int slotId, boolean isCarrierImsService);
/**
+ * Get the MmTelFeature state attached to this subscription id.
+ */
+ void getImsMmTelFeatureState(int subId, IIntegerConsumer callback);
+
+ /**
* Set the network selection mode to automatic.
*
* @param subId the id of the subscription to update.
@@ -1815,6 +1820,12 @@
boolean isAvailable(int subId, int capability, int regTech);
/**
+ * Return whether or not the MmTel capability is supported for the requested transport type.
+ */
+ void isMmTelCapabilitySupported(int subId, IIntegerConsumer callback, int capability,
+ int transportType);
+
+ /**
* Returns true if the user's setting for 4G LTE is enabled, for the subscription specified.
*/
boolean isAdvancedCallingSettingEnabled(int subId);
diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java
index 4d8c7d9..9d3e120 100644
--- a/test-mock/src/android/test/mock/MockContentProvider.java
+++ b/test-mock/src/android/test/mock/MockContentProvider.java
@@ -56,21 +56,22 @@
*/
private class InversionIContentProvider implements IContentProvider {
@Override
- public ContentProviderResult[] applyBatch(String callingPackage, String authority,
+ public ContentProviderResult[] applyBatch(String callingPackage,
+ @Nullable String featureId, String authority,
ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException {
return MockContentProvider.this.applyBatch(authority, operations);
}
@Override
- public int bulkInsert(String callingPackage, Uri url, ContentValues[] initialValues)
- throws RemoteException {
+ public int bulkInsert(String callingPackage, @Nullable String featureId, Uri url,
+ ContentValues[] initialValues) throws RemoteException {
return MockContentProvider.this.bulkInsert(url, initialValues);
}
@Override
- public int delete(String callingPackage, Uri url, String selection, String[] selectionArgs)
- throws RemoteException {
+ public int delete(String callingPackage, @Nullable String featureId, Uri url,
+ String selection, String[] selectionArgs) throws RemoteException {
return MockContentProvider.this.delete(url, selection, selectionArgs);
}
@@ -80,42 +81,42 @@
}
@Override
- public Uri insert(String callingPackage, Uri url, ContentValues initialValues)
- throws RemoteException {
+ public Uri insert(String callingPackage, @Nullable String featureId, Uri url,
+ ContentValues initialValues) throws RemoteException {
return MockContentProvider.this.insert(url, initialValues);
}
@Override
- public AssetFileDescriptor openAssetFile(
- String callingPackage, Uri url, String mode, ICancellationSignal signal)
+ public AssetFileDescriptor openAssetFile(String callingPackage,
+ @Nullable String featureId, Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
return MockContentProvider.this.openAssetFile(url, mode);
}
@Override
- public ParcelFileDescriptor openFile(
- String callingPackage, Uri url, String mode, ICancellationSignal signal,
- IBinder callerToken) throws RemoteException, FileNotFoundException {
+ public ParcelFileDescriptor openFile(String callingPackage, @Nullable String featureId,
+ Uri url, String mode, ICancellationSignal signal, IBinder callerToken)
+ throws RemoteException, FileNotFoundException {
return MockContentProvider.this.openFile(url, mode);
}
@Override
- public Cursor query(String callingPackage, Uri url, @Nullable String[] projection,
- @Nullable Bundle queryArgs,
- @Nullable ICancellationSignal cancellationSignal)
- throws RemoteException {
+ public Cursor query(String callingPackage, @Nullable String featureId, Uri url,
+ @Nullable String[] projection, @Nullable Bundle queryArgs,
+ @Nullable ICancellationSignal cancellationSignal) throws RemoteException {
return MockContentProvider.this.query(url, projection, queryArgs, null);
}
@Override
- public int update(String callingPackage, Uri url, ContentValues values, String selection,
- String[] selectionArgs) throws RemoteException {
+ public int update(String callingPackage, @Nullable String featureId, Uri url,
+ ContentValues values, String selection, String[] selectionArgs)
+ throws RemoteException {
return MockContentProvider.this.update(url, values, selection, selectionArgs);
}
@Override
- public Bundle call(String callingPackage, String authority, String method, String request,
- Bundle args) throws RemoteException {
+ public Bundle call(String callingPackage, @Nullable String featureId, String authority,
+ String method, String request, Bundle args) throws RemoteException {
return MockContentProvider.this.call(authority, method, request, args);
}
@@ -130,9 +131,9 @@
}
@Override
- public AssetFileDescriptor openTypedAssetFile(String callingPackage, Uri url,
- String mimeType, Bundle opts, ICancellationSignal signal)
- throws RemoteException, FileNotFoundException {
+ public AssetFileDescriptor openTypedAssetFile(String callingPackage,
+ @Nullable String featureId, Uri url, String mimeType, Bundle opts,
+ ICancellationSignal signal) throws RemoteException, FileNotFoundException {
return MockContentProvider.this.openTypedAssetFile(url, mimeType, opts);
}
@@ -142,23 +143,26 @@
}
@Override
- public Uri canonicalize(String callingPkg, Uri uri) throws RemoteException {
+ public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri)
+ throws RemoteException {
return MockContentProvider.this.canonicalize(uri);
}
@Override
- public Uri uncanonicalize(String callingPkg, Uri uri) throws RemoteException {
+ public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri)
+ throws RemoteException {
return MockContentProvider.this.uncanonicalize(uri);
}
@Override
- public boolean refresh(String callingPkg, Uri url, Bundle args,
- ICancellationSignal cancellationSignal) throws RemoteException {
+ public boolean refresh(String callingPkg, @Nullable String featureId, Uri url,
+ Bundle args, ICancellationSignal cancellationSignal) throws RemoteException {
return MockContentProvider.this.refresh(url, args);
}
@Override
- public int checkUriPermission(String callingPkg, Uri uri, int uid, int modeFlags) {
+ public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri uri,
+ int uid, int modeFlags) {
return MockContentProvider.this.checkUriPermission(uri, uid, modeFlags);
}
}
diff --git a/test-mock/src/android/test/mock/MockIContentProvider.java b/test-mock/src/android/test/mock/MockIContentProvider.java
index b072d74..e512b52 100644
--- a/test-mock/src/android/test/mock/MockIContentProvider.java
+++ b/test-mock/src/android/test/mock/MockIContentProvider.java
@@ -16,14 +16,12 @@
package android.test.mock;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentValues;
import android.content.EntityIterator;
import android.content.IContentProvider;
-import android.content.Intent;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
@@ -45,14 +43,15 @@
*/
public class MockIContentProvider implements IContentProvider {
@Override
- public int bulkInsert(String callingPackage, Uri url, ContentValues[] initialValues) {
+ public int bulkInsert(String callingPackage, @Nullable String featureId, Uri url,
+ ContentValues[] initialValues) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
@SuppressWarnings("unused")
- public int delete(String callingPackage, Uri url, String selection, String[] selectionArgs)
- throws RemoteException {
+ public int delete(String callingPackage, @Nullable String featureId, Uri url,
+ String selection, String[] selectionArgs) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -63,33 +62,33 @@
@Override
@SuppressWarnings("unused")
- public Uri insert(String callingPackage, Uri url, ContentValues initialValues)
- throws RemoteException {
+ public Uri insert(String callingPackage, @Nullable String featureId, Uri url,
+ ContentValues initialValues) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public ParcelFileDescriptor openFile(
- String callingPackage, Uri url, String mode, ICancellationSignal signal,
- IBinder callerToken) {
+ public ParcelFileDescriptor openFile(String callingPackage, @Nullable String featureId,
+ Uri url, String mode, ICancellationSignal signal, IBinder callerToken) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public AssetFileDescriptor openAssetFile(
- String callingPackage, Uri uri, String mode, ICancellationSignal signal) {
+ public AssetFileDescriptor openAssetFile(String callingPackage, @Nullable String featureId,
+ Uri uri, String mode, ICancellationSignal signal) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public ContentProviderResult[] applyBatch(String callingPackage, String authority,
- ArrayList<ContentProviderOperation> operations) {
+ public ContentProviderResult[] applyBatch(String callingPackage, @Nullable String featureId,
+ String authority, ArrayList<ContentProviderOperation> operations) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public Cursor query(String callingPackage, Uri url, @Nullable String[] projection,
- @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) {
+ public Cursor query(String callingPackage, @Nullable String featureId, Uri url,
+ @Nullable String[] projection, @Nullable Bundle queryArgs,
+ @Nullable ICancellationSignal cancellationSignal) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -99,14 +98,14 @@
}
@Override
- public int update(String callingPackage, Uri url, ContentValues values, String selection,
- String[] selectionArgs) throws RemoteException {
+ public int update(String callingPackage, @Nullable String featureId, Uri url,
+ ContentValues values, String selection, String[] selectionArgs) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public Bundle call(String callingPackage, String authority, String method, String request,
- Bundle args) throws RemoteException {
+ public Bundle call(String callingPackage, @Nullable String featureId, String authority,
+ String method, String request, Bundle args) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -121,8 +120,9 @@
}
@Override
- public AssetFileDescriptor openTypedAssetFile(String callingPackage, Uri url, String mimeType,
- Bundle opts, ICancellationSignal signal) throws RemoteException, FileNotFoundException {
+ public AssetFileDescriptor openTypedAssetFile(String callingPackage,
+ @Nullable String featureId, Uri url, String mimeType, Bundle opts,
+ ICancellationSignal signal) throws RemoteException, FileNotFoundException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -132,24 +132,27 @@
}
@Override
- public Uri canonicalize(String callingPkg, Uri uri) throws RemoteException {
+ public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri)
+ throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public Uri uncanonicalize(String callingPkg, Uri uri) throws RemoteException {
+ public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri)
+ throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
- public boolean refresh(String callingPkg, Uri url, Bundle args,
+ public boolean refresh(String callingPkg, @Nullable String featureId, Uri url, Bundle args,
ICancellationSignal cancellationSignal) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
/** {@hide} */
@Override
- public int checkUriPermission(String callingPkg, Uri uri, int uid, int modeFlags) {
+ public int checkUriPermission(String callingPkg, @Nullable String featureId, Uri uri, int uid,
+ int modeFlags) {
throw new UnsupportedOperationException("unimplemented mock method call");
}
}
diff --git a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
index 81937e6..ead4a28 100644
--- a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
+++ b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
@@ -94,6 +94,8 @@
boolean sawServices = false;
for (String line : res.split("\n")) {
if (line.contains("framework.jar")) {
+ sawFramework = true; // Legacy
+ } else if (line.contains("framework-minus-apex.jar")) {
sawFramework = true;
} else if (line.contains("services.jar")) {
sawServices = true;
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 19be132..023df70 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -34,6 +34,7 @@
import android.net.wifi.ITxPacketCountListener;
import android.net.wifi.IOnWifiUsabilityStatsListener;
import android.net.wifi.ScanResult;
+import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiActivityEnergyInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
@@ -142,7 +143,8 @@
boolean stopSoftAp();
- int startLocalOnlyHotspot(in ILocalOnlyHotspotCallback callback, String packageName);
+ int startLocalOnlyHotspot(in ILocalOnlyHotspotCallback callback, String packageName,
+ in SoftApConfiguration customConfig);
void stopLocalOnlyHotspot();
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.aidl b/wifi/java/android/net/wifi/SoftApConfiguration.aidl
new file mode 100644
index 0000000..1d06f45
--- /dev/null
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+parcelable SoftApConfiguration;
\ No newline at end of file
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
new file mode 100644
index 0000000..4cc8653
--- /dev/null
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.MacAddress;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * WiFi configuration for a soft access point (a.k.a. Soft AP, SAP, Hotspot).
+ *
+ * This is input for the framework provided by a client app, i.e. it exposes knobs to instruct the
+ * framework how it should open a hotspot. It is not meant to describe the network as it will be
+ * seen by clients; this role is currently served by {@link WifiConfiguration} (see
+ * {@link WifiManager.LocalOnlyHotspotReservation#getWifiConfiguration()}).
+ *
+ * System apps can use this to configure a local-only hotspot using
+ * {@link WifiManager#startLocalOnlyHotspot(SoftApConfiguration, Executor,
+ * WifiManager.LocalOnlyHotspotCallback)}.
+ *
+ * Instances of this class are immutable; use {@link SoftApConfiguration.Builder} and its methods to
+ * create a new instance.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SoftApConfiguration implements Parcelable {
+ /**
+ * SSID for the AP, or null for a framework-determined SSID.
+ */
+ private final @Nullable String mSsid;
+ /**
+ * BSSID for the AP, or null to use a framework-determined BSSID.
+ */
+ private final @Nullable MacAddress mBssid;
+ /**
+ * Pre-shared key for WPA2-PSK encryption (non-null enables WPA2-PSK).
+ */
+ private final @Nullable String mWpa2Passphrase;
+
+ /** Private constructor for Builder and Parcelable implementation. */
+ private SoftApConfiguration(
+ @Nullable String ssid, @Nullable MacAddress bssid, String wpa2Passphrase) {
+ mSsid = ssid;
+ mBssid = bssid;
+ mWpa2Passphrase = wpa2Passphrase;
+ }
+
+ @Override
+ public boolean equals(Object otherObj) {
+ if (this == otherObj) {
+ return true;
+ }
+ if (!(otherObj instanceof SoftApConfiguration)) {
+ return false;
+ }
+ SoftApConfiguration other = (SoftApConfiguration) otherObj;
+ return Objects.equals(mSsid, other.mSsid)
+ && Objects.equals(mBssid, other.mBssid)
+ && Objects.equals(mWpa2Passphrase, other.mWpa2Passphrase);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSsid, mBssid, mWpa2Passphrase);
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mSsid);
+ dest.writeParcelable(mBssid, flags);
+ dest.writeString(mWpa2Passphrase);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public static final Creator<SoftApConfiguration> CREATOR = new Creator<SoftApConfiguration>() {
+ @Override
+ public SoftApConfiguration createFromParcel(Parcel in) {
+ return new SoftApConfiguration(
+ in.readString(),
+ in.readParcelable(MacAddress.class.getClassLoader()),
+ in.readString());
+ }
+
+ @Override
+ public SoftApConfiguration[] newArray(int size) {
+ return new SoftApConfiguration[size];
+ }
+ };
+
+ @Nullable
+ public String getSsid() {
+ return mSsid;
+ }
+
+ @Nullable
+ public MacAddress getBssid() {
+ return mBssid;
+ }
+
+ @Nullable
+ public String getWpa2Passphrase() {
+ return mWpa2Passphrase;
+ }
+
+ /**
+ * Builds a {@link SoftApConfiguration}, which allows an app to configure various aspects of a
+ * Soft AP.
+ *
+ * All fields are optional. By default, SSID and BSSID are automatically chosen by the
+ * framework, and an open network is created.
+ */
+ public static final class Builder {
+ private String mSsid;
+ private MacAddress mBssid;
+ private String mWpa2Passphrase;
+
+ /**
+ * Constructs a Builder with default values (see {@link Builder}).
+ */
+ public Builder() {
+ mSsid = null;
+ mBssid = null;
+ mWpa2Passphrase = null;
+ }
+
+ /**
+ * Constructs a Builder initialized from an existing {@link SoftApConfiguration} instance.
+ */
+ public Builder(@NonNull SoftApConfiguration other) {
+ Objects.requireNonNull(other);
+
+ mSsid = other.mSsid;
+ mBssid = other.mBssid;
+ mWpa2Passphrase = other.mWpa2Passphrase;
+ }
+
+ /**
+ * Builds the {@link SoftApConfiguration}.
+ *
+ * @return A new {@link SoftApConfiguration}, as configured by previous method calls.
+ */
+ @NonNull
+ public SoftApConfiguration build() {
+ return new SoftApConfiguration(mSsid, mBssid, mWpa2Passphrase);
+ }
+
+ /**
+ * Specifies an SSID for the AP.
+ *
+ * @param ssid SSID of valid Unicode characters, or null to have the SSID automatically
+ * chosen by the framework.
+ * @return Builder for chaining.
+ * @throws IllegalArgumentException when the SSID is empty or not valid Unicode.
+ */
+ @NonNull
+ public Builder setSsid(@Nullable String ssid) {
+ if (ssid != null) {
+ Preconditions.checkStringNotEmpty(ssid);
+ Preconditions.checkArgument(StandardCharsets.UTF_8.newEncoder().canEncode(ssid));
+ }
+ mSsid = ssid;
+ return this;
+ }
+
+ /**
+ * Specifies a BSSID for the AP.
+ *
+ * @param bssid BSSID, or null to have the BSSID chosen by the framework. The caller is
+ * responsible for avoiding collisions.
+ * @return Builder for chaining.
+ * @throws IllegalArgumentException when the given BSSID is the all-zero or broadcast MAC
+ * address.
+ */
+ @NonNull
+ public Builder setBssid(@Nullable MacAddress bssid) {
+ if (bssid != null) {
+ Preconditions.checkArgument(!bssid.equals(MacAddress.ALL_ZEROS_ADDRESS));
+ Preconditions.checkArgument(!bssid.equals(MacAddress.BROADCAST_ADDRESS));
+ }
+ mBssid = bssid;
+ return this;
+ }
+
+ /**
+ * Specifies that this AP should use WPA2-PSK with the given passphrase. When set to null
+ * and no other encryption method is configured, an open network is created.
+ *
+ * @param passphrase The passphrase to use, or null to unset a previously-set WPA2-PSK
+ * configuration.
+ * @return Builder for chaining.
+ * @throws IllegalArgumentException when the passphrase is the empty string
+ */
+ @NonNull
+ public Builder setWpa2Passphrase(@Nullable String passphrase) {
+ if (passphrase != null) {
+ Preconditions.checkStringNotEmpty(passphrase);
+ }
+ mWpa2Passphrase = passphrase;
+ return this;
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 3bedddc..30c24d3 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -34,6 +34,7 @@
import android.os.Parcelable;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.BackupUtils;
import android.util.Log;
@@ -730,6 +731,14 @@
public String lastUpdateName;
/**
+ * The carrier ID identifies the operator who provides this network configuration.
+ * see {@link TelephonyManager#getSimCarrierId()}
+ * @hide
+ */
+ @SystemApi
+ public int carrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+
+ /**
* @hide
* Status of user approval for connection
*/
@@ -1046,10 +1055,10 @@
/**
* @hide
- * The wall clock time of when |mRandomizedMacAddress| last changed.
- * Used to determine when we should re-randomize in aggressive mode.
+ * The wall clock time of when |mRandomizedMacAddress| should be re-randomized in aggressive
+ * randomization mode.
*/
- public long randomizedMacLastModifiedTimeMs = 0;
+ public long randomizedMacExpirationTimeMs = 0;
/**
* @hide
@@ -1850,6 +1859,7 @@
.append(" PRIO: ").append(this.priority)
.append(" HIDDEN: ").append(this.hiddenSSID)
.append(" PMF: ").append(this.requirePMF)
+ .append("CarrierId: ").append(this.carrierId)
.append('\n');
@@ -1910,8 +1920,9 @@
}
sbuf.append(" macRandomizationSetting: ").append(macRandomizationSetting).append("\n");
sbuf.append(" mRandomizedMacAddress: ").append(mRandomizedMacAddress).append("\n");
- sbuf.append(" randomizedMacLastModifiedTimeMs: ").append(randomizedMacLastModifiedTimeMs)
- .append("\n");
+ sbuf.append(" randomizedMacExpirationTimeMs: ")
+ .append(randomizedMacExpirationTimeMs == 0 ? "<none>"
+ : TimeUtils.logTimeOfDay(randomizedMacExpirationTimeMs)).append("\n");
sbuf.append(" KeyMgmt:");
for (int k = 0; k < this.allowedKeyManagement.size(); k++) {
if (this.allowedKeyManagement.get(k)) {
@@ -2439,9 +2450,10 @@
recentFailure.setAssociationStatus(source.recentFailure.getAssociationStatus());
mRandomizedMacAddress = source.mRandomizedMacAddress;
macRandomizationSetting = source.macRandomizationSetting;
- randomizedMacLastModifiedTimeMs = source.randomizedMacLastModifiedTimeMs;
+ randomizedMacExpirationTimeMs = source.randomizedMacExpirationTimeMs;
requirePMF = source.requirePMF;
updateIdentifier = source.updateIdentifier;
+ carrierId = source.carrierId;
}
}
@@ -2515,7 +2527,8 @@
dest.writeParcelable(mRandomizedMacAddress, flags);
dest.writeInt(macRandomizationSetting);
dest.writeInt(osu ? 1 : 0);
- dest.writeLong(randomizedMacLastModifiedTimeMs);
+ dest.writeLong(randomizedMacExpirationTimeMs);
+ dest.writeInt(carrierId);
}
/** Implement the Parcelable interface {@hide} */
@@ -2591,7 +2604,8 @@
config.mRandomizedMacAddress = in.readParcelable(null);
config.macRandomizationSetting = in.readInt();
config.osu = in.readInt() != 0;
- config.randomizedMacLastModifiedTimeMs = in.readLong();
+ config.randomizedMacExpirationTimeMs = in.readLong();
+ config.carrierId = in.readInt();
return config;
}
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index f8c2011..7b99a2b 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -1263,4 +1263,23 @@
public @Ocsp int getOcsp() {
return mOcsp;
}
+
+ /**
+ * If the current authentication method needs SIM card.
+ * @return true if the credential information require SIM card for current authentication
+ * method, otherwise it returns false.
+ * @hide
+ */
+ public boolean requireSimCredential() {
+ if (mEapMethod == Eap.SIM || mEapMethod == Eap.AKA || mEapMethod == Eap.AKA_PRIME) {
+ return true;
+ }
+ if (mEapMethod == Eap.PEAP) {
+ if (mPhase2Method == Phase2.SIM || mPhase2Method == Phase2.AKA
+ || mPhase2Method == Phase2.AKA_PRIME) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 8f71b0b..380ebf1 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -71,6 +71,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -2747,13 +2748,6 @@
}
}
- private Executor executorForHandler(@Nullable Handler handler) {
- if (handler == null) {
- return mContext.getMainExecutor();
- }
- return new HandlerExecutor(handler);
- }
-
/**
* Request a local only hotspot that an application can use to communicate between co-located
* devices connected to the created WiFi hotspot. The network created by this method will not
@@ -2809,9 +2803,59 @@
* @param handler Handler to be used for callbacks. If the caller passes a null Handler, the
* main thread will be used.
*/
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.CHANGE_WIFI_STATE,
+ android.Manifest.permission.ACCESS_FINE_LOCATION})
public void startLocalOnlyHotspot(LocalOnlyHotspotCallback callback,
@Nullable Handler handler) {
- Executor executor = executorForHandler(handler);
+ Executor executor = handler == null ? null : new HandlerExecutor(handler);
+ startLocalOnlyHotspotInternal(null, executor, callback);
+ }
+
+ /**
+ * Starts a local-only hotspot with a specific configuration applied. See
+ * {@link #startLocalOnlyHotspot(LocalOnlyHotspotCallback, Handler)}.
+ *
+ * Applications need either {@link android.Manifest.permission#NETWORK_SETUP_WIZARD} or
+ * {@link android.Manifest.permission#NETWORK_SETTINGS} to call this method.
+ *
+ * Since custom configuration settings may be incompatible with each other, the hotspot started
+ * through this method cannot coexist with another hotspot created through
+ * startLocalOnlyHotspot. If this is attempted, the first hotspot request wins and others
+ * receive {@link LocalOnlyHotspotCallback#ERROR_GENERIC} through
+ * {@link LocalOnlyHotspotCallback#onFailed}.
+ *
+ * @param config Custom configuration for the hotspot. See {@link SoftApConfiguration}.
+ * @param executor Executor to run callback methods on, or null to use the main thread.
+ * @param callback Callback object for updates about hotspot status, or null for no updates.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
+ public void startLocalOnlyHotspot(@NonNull SoftApConfiguration config,
+ @Nullable Executor executor,
+ @Nullable LocalOnlyHotspotCallback callback) {
+ Objects.requireNonNull(config);
+ startLocalOnlyHotspotInternal(config, executor, callback);
+ }
+
+ /**
+ * Common implementation of both configurable and non-configurable LOHS.
+ *
+ * @param config App-specified configuration, or null. When present, additional privileges are
+ * required, and the hotspot cannot be shared with other clients.
+ * @param executor Executor to run callback methods on, or null to use the main thread.
+ * @param callback Callback object for updates about hotspot status, or null for no updates.
+ */
+ private void startLocalOnlyHotspotInternal(
+ @Nullable SoftApConfiguration config,
+ @Nullable Executor executor,
+ @Nullable LocalOnlyHotspotCallback callback) {
+ if (executor == null) {
+ executor = mContext.getMainExecutor();
+ }
synchronized (mLock) {
LocalOnlyHotspotCallbackProxy proxy =
new LocalOnlyHotspotCallbackProxy(this, executor, callback);
@@ -2821,7 +2865,7 @@
throw new RemoteException("Wifi service is not running");
}
String packageName = mContext.getOpPackageName();
- int returnCode = iWifiManager.startLocalOnlyHotspot(proxy, packageName);
+ int returnCode = iWifiManager.startLocalOnlyHotspot(proxy, packageName, config);
if (returnCode != LocalOnlyHotspotCallback.REQUEST_REGISTERED) {
// Send message to the proxy to make sure we call back on the correct thread
proxy.onHotspotFailed(returnCode);
@@ -2902,7 +2946,8 @@
*/
public void watchLocalOnlyHotspot(LocalOnlyHotspotObserver observer,
@Nullable Handler handler) {
- Executor executor = executorForHandler(handler);
+ Executor executor = handler == null ? mContext.getMainExecutor()
+ : new HandlerExecutor(handler);
synchronized (mLock) {
mLOHSObserverProxy =
new LocalOnlyHotspotObserverProxy(this, executor, observer);
@@ -3484,10 +3529,12 @@
*
* @param manager WifiManager
* @param executor Executor for delivering callbacks.
- * @param callback LocalOnlyHotspotCallback to notify the calling application.
+ * @param callback LocalOnlyHotspotCallback to notify the calling application, or null.
*/
- LocalOnlyHotspotCallbackProxy(WifiManager manager, Executor executor,
- LocalOnlyHotspotCallback callback) {
+ LocalOnlyHotspotCallbackProxy(
+ @NonNull WifiManager manager,
+ @NonNull Executor executor,
+ @Nullable LocalOnlyHotspotCallback callback) {
mWifiManager = new WeakReference<>(manager);
mExecutor = executor;
mCallback = callback;
@@ -3505,6 +3552,7 @@
}
final LocalOnlyHotspotReservation reservation =
manager.new LocalOnlyHotspotReservation(config);
+ if (mCallback == null) return;
mExecutor.execute(() -> mCallback.onStarted(reservation));
}
@@ -3514,6 +3562,7 @@
if (manager == null) return;
Log.w(TAG, "LocalOnlyHotspotCallbackProxy: hotspot stopped");
+ if (mCallback == null) return;
mExecutor.execute(() -> mCallback.onStopped());
}
@@ -3524,6 +3573,7 @@
Log.w(TAG, "LocalOnlyHotspotCallbackProxy: failed to start. reason: "
+ reason);
+ if (mCallback == null) return;
mExecutor.execute(() -> mCallback.onFailed(reason));
}
}
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index 9b529ce..246e96f 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -21,12 +21,15 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.app.ActivityThread;
import android.net.MacAddress;
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import java.nio.charset.CharsetEncoder;
@@ -107,6 +110,12 @@
*/
private int mPriority;
+ /**
+ * The carrier ID identifies the operator who provides this network configuration.
+ * see {@link TelephonyManager#getSimCarrierId()}
+ */
+ private int mCarrierId;
+
public Builder() {
mSsid = null;
mBssid = null;
@@ -121,6 +130,7 @@
mIsUserInteractionRequired = false;
mIsMetered = false;
mPriority = UNASSIGNED_PRIORITY;
+ mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
}
/**
@@ -258,6 +268,23 @@
}
/**
+ * Set the carrier ID of the network operator. The carrier ID associates a Suggested
+ * network with a specific carrier (and therefore SIM). The carrier ID must be provided
+ * for any network which uses the SIM-based authentication: e.g. EAP-SIM, EAP-AKA,
+ * EAP-AKA', and EAP-PEAP with SIM-based phase 2 authentication.
+ * @param carrierId see {@link TelephonyManager#getSimCarrierId()}.
+ * @return Instance of {@link Builder} to enable chaining of the builder method.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING)
+ public @NonNull Builder setCarrierId(int carrierId) {
+ mCarrierId = carrierId;
+ return this;
+ }
+
+ /**
* Specifies whether this represents a hidden network.
* <p>
* <li>If not set, defaults to false (i.e not a hidden network).</li>
@@ -380,6 +407,7 @@
wifiConfiguration.meteredOverride =
mIsMetered ? WifiConfiguration.METERED_OVERRIDE_METERED
: WifiConfiguration.METERED_OVERRIDE_NONE;
+ wifiConfiguration.carrierId = mCarrierId;
return wifiConfiguration;
}
@@ -405,6 +433,7 @@
wifiConfiguration.meteredOverride =
mIsMetered ? WifiConfiguration.METERED_OVERRIDE_METERED
: WifiConfiguration.METERED_OVERRIDE_NONE;
+ mPasspointConfiguration.setCarrierId(mCarrierId);
return wifiConfiguration;
}
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index e9aa076..5befb54 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -24,6 +24,7 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
@@ -398,6 +399,30 @@
}
/**
+ * The carrier ID identifies the operator who provides this network configuration.
+ * see {@link TelephonyManager#getSimCarrierId()}
+ */
+ private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+
+ /**
+ * Set the carrier ID associated with current configuration.
+ * @param carrierId {@code mCarrierId}
+ * @hide
+ */
+ public void setCarrierId(int carrierId) {
+ this.mCarrierId = carrierId;
+ }
+
+ /**
+ * Get the carrier ID associated with current configuration.
+ * @return {@code mCarrierId}
+ * @hide
+ */
+ public int getCarrierId() {
+ return mCarrierId;
+ }
+
+ /**
* Constructor for creating PasspointConfiguration with default values.
*/
public PasspointConfiguration() {}
@@ -438,6 +463,7 @@
mUsageLimitUsageTimePeriodInMinutes = source.mUsageLimitUsageTimePeriodInMinutes;
mServiceFriendlyNames = source.mServiceFriendlyNames;
mAaaServerTrustedNames = source.mAaaServerTrustedNames;
+ mCarrierId = source.mCarrierId;
}
@Override
@@ -466,6 +492,7 @@
bundle.putSerializable("serviceFriendlyNames",
(HashMap<String, String>) mServiceFriendlyNames);
dest.writeBundle(bundle);
+ dest.writeInt(mCarrierId);
}
@Override
@@ -495,6 +522,7 @@
&& mUsageLimitStartTimeInMillis == that.mUsageLimitStartTimeInMillis
&& mUsageLimitDataLimit == that.mUsageLimitDataLimit
&& mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes
+ && mCarrierId == that.mCarrierId
&& (mServiceFriendlyNames == null ? that.mServiceFriendlyNames == null
: mServiceFriendlyNames.equals(that.mServiceFriendlyNames));
}
@@ -505,7 +533,7 @@
mUpdateIdentifier, mCredentialPriority, mSubscriptionCreationTimeInMillis,
mSubscriptionExpirationTimeInMillis, mUsageLimitUsageTimePeriodInMinutes,
mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes,
- mServiceFriendlyNames);
+ mServiceFriendlyNames, mCarrierId);
}
@Override
@@ -558,6 +586,7 @@
if (mServiceFriendlyNames != null) {
builder.append("ServiceFriendlyNames: ").append(mServiceFriendlyNames);
}
+ builder.append("CarrierId:" + mCarrierId);
return builder.toString();
}
@@ -662,6 +691,7 @@
Map<String, String> friendlyNamesMap = (HashMap) bundle.getSerializable(
"serviceFriendlyNames");
config.setServiceFriendlyNames(friendlyNamesMap);
+ config.mCarrierId = in.readInt();
return config;
}
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index 5e6c107..4b7d205 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -32,6 +32,7 @@
import android.net.wifi.ITxPacketCountListener;
import android.net.wifi.IWifiManager;
import android.net.wifi.ScanResult;
+import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiActivityEnergyInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
@@ -293,8 +294,15 @@
throw new UnsupportedOperationException();
}
- @Override
+ /** @deprecated replaced by newer signature */
+ @Deprecated
public int startLocalOnlyHotspot(ILocalOnlyHotspotCallback callback, String packageName) {
+ return startLocalOnlyHotspot(callback, packageName, null);
+ }
+
+ @Override
+ public int startLocalOnlyHotspot(ILocalOnlyHotspotCallback callback, String packageName,
+ SoftApConfiguration customConfig) {
throw new UnsupportedOperationException();
}
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
new file mode 100644
index 0000000..949b479
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.net.MacAddress;
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@SmallTest
+public class SoftApConfigurationTest {
+ private SoftApConfiguration parcelUnparcel(SoftApConfiguration configIn) {
+ Parcel parcel = Parcel.obtain();
+ parcel.writeParcelable(configIn, 0);
+ parcel.setDataPosition(0);
+ SoftApConfiguration configOut =
+ parcel.readParcelable(SoftApConfiguration.class.getClassLoader());
+ parcel.recycle();
+ return configOut;
+ }
+
+ @Test
+ public void testBasicSettings() {
+ SoftApConfiguration original = new SoftApConfiguration.Builder()
+ .setSsid("ssid")
+ .setBssid(MacAddress.fromString("11:22:33:44:55:66"))
+ .build();
+ assertThat(original.getSsid()).isEqualTo("ssid");
+ assertThat(original.getBssid()).isEqualTo(MacAddress.fromString("11:22:33:44:55:66"));
+ assertThat(original.getWpa2Passphrase()).isNull();
+
+ SoftApConfiguration unparceled = parcelUnparcel(original);
+ assertThat(unparceled).isNotSameAs(original);
+ assertThat(unparceled).isEqualTo(original);
+ assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
+
+ SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
+ assertThat(copy).isNotSameAs(original);
+ assertThat(copy).isEqualTo(original);
+ assertThat(copy.hashCode()).isEqualTo(original.hashCode());
+ }
+
+ @Test
+ public void testWpa2() {
+ SoftApConfiguration original = new SoftApConfiguration.Builder()
+ .setWpa2Passphrase("secretsecret")
+ .build();
+ assertThat(original.getWpa2Passphrase()).isEqualTo("secretsecret");
+
+ SoftApConfiguration unparceled = parcelUnparcel(original);
+ assertThat(unparceled).isNotSameAs(original);
+ assertThat(unparceled).isEqualTo(original);
+ assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
+
+ SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
+ assertThat(copy).isNotSameAs(original);
+ assertThat(copy).isEqualTo(original);
+ assertThat(copy.hashCode()).isEqualTo(original.hashCode());
+ }
+}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 8d0579b..d2516a3 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -173,7 +173,7 @@
public void testCreationAndCloseOfLocalOnlyHotspotReservation() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
- anyString())).thenReturn(REQUEST_REGISTERED);
+ anyString(), eq(null))).thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
callback.onStarted(mWifiManager.new LocalOnlyHotspotReservation(mApConfig));
@@ -191,7 +191,7 @@
throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
- anyString())).thenReturn(REQUEST_REGISTERED);
+ anyString(), eq(null))).thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
callback.onStarted(mWifiManager.new LocalOnlyHotspotReservation(mApConfig));
@@ -351,7 +351,7 @@
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
verify(mWifiService)
- .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString());
+ .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), eq(null));
}
/**
@@ -362,7 +362,7 @@
public void testStartLocalOnlyHotspotThrowsSecurityException() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
doThrow(new SecurityException()).when(mWifiService)
- .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString());
+ .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), eq(null));
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
}
@@ -374,7 +374,7 @@
public void testStartLocalOnlyHotspotThrowsIllegalStateException() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
doThrow(new IllegalStateException()).when(mWifiService)
- .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString());
+ .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), eq(null));
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
}
@@ -385,7 +385,7 @@
public void testCorrectLooperIsUsedForHandler() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
- anyString())).thenReturn(ERROR_INCOMPATIBLE_MODE);
+ anyString(), eq(null))).thenReturn(ERROR_INCOMPATIBLE_MODE);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mLooper.dispatchAll();
assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason);
@@ -404,7 +404,7 @@
when(mContext.getMainExecutor()).thenReturn(altLooper.getNewExecutor());
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
- anyString())).thenReturn(ERROR_INCOMPATIBLE_MODE);
+ anyString(), eq(null))).thenReturn(ERROR_INCOMPATIBLE_MODE);
mWifiManager.startLocalOnlyHotspot(callback, null);
altLooper.dispatchAll();
assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason);
@@ -423,7 +423,7 @@
Handler callbackHandler = new Handler(callbackLooper.getLooper());
ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback =
ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class);
- when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString()))
+ when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString(), eq(null)))
.thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, callbackHandler);
callbackLooper.dispatchAll();
@@ -449,7 +449,7 @@
Handler callbackHandler = new Handler(callbackLooper.getLooper());
ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback =
ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class);
- when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString()))
+ when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString(), eq(null)))
.thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, callbackHandler);
callbackLooper.dispatchAll();
@@ -474,7 +474,7 @@
Handler callbackHandler = new Handler(callbackLooper.getLooper());
ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback =
ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class);
- when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString()))
+ when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString(), eq(null)))
.thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, callbackHandler);
callbackLooper.dispatchAll();
@@ -497,7 +497,7 @@
Handler callbackHandler = new Handler(callbackLooper.getLooper());
ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback =
ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class);
- when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString()))
+ when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString(), eq(null)))
.thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, callbackHandler);
callbackLooper.dispatchAll();
@@ -517,7 +517,7 @@
public void testLocalOnlyHotspotCallbackFullOnIncompatibleMode() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
- anyString())).thenReturn(ERROR_INCOMPATIBLE_MODE);
+ anyString(), eq(null))).thenReturn(ERROR_INCOMPATIBLE_MODE);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mLooper.dispatchAll();
assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason);
@@ -533,7 +533,7 @@
public void testLocalOnlyHotspotCallbackFullOnTetheringDisallowed() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
- anyString())).thenReturn(ERROR_TETHERING_DISALLOWED);
+ anyString(), eq(null))).thenReturn(ERROR_TETHERING_DISALLOWED);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mLooper.dispatchAll();
assertEquals(ERROR_TETHERING_DISALLOWED, callback.mFailureReason);
@@ -550,7 +550,7 @@
public void testLocalOnlyHotspotCallbackFullOnSecurityException() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
doThrow(new SecurityException()).when(mWifiService)
- .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString());
+ .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), eq(null));
try {
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
} catch (SecurityException e) {
@@ -571,7 +571,7 @@
public void testLocalOnlyHotspotCallbackFullOnNoChannelError() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
- anyString())).thenReturn(REQUEST_REGISTERED);
+ anyString(), eq(null))).thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mLooper.dispatchAll();
//assertEquals(ERROR_NO_CHANNEL, callback.mFailureReason);
@@ -587,7 +587,7 @@
public void testCancelLocalOnlyHotspotRequestCallsStopOnWifiService() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
- anyString())).thenReturn(REQUEST_REGISTERED);
+ anyString(), eq(null))).thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mWifiManager.cancelLocalOnlyHotspotRequest();
verify(mWifiService).stopLocalOnlyHotspot();
@@ -609,7 +609,7 @@
public void testCallbackAfterLocalOnlyHotspotWasCancelled() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
- anyString())).thenReturn(REQUEST_REGISTERED);
+ anyString(), eq(null))).thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mWifiManager.cancelLocalOnlyHotspotRequest();
verify(mWifiService).stopLocalOnlyHotspot();
@@ -628,7 +628,7 @@
public void testCancelAfterLocalOnlyHotspotCallbackTriggered() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
- anyString())).thenReturn(ERROR_INCOMPATIBLE_MODE);
+ anyString(), eq(null))).thenReturn(ERROR_INCOMPATIBLE_MODE);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mLooper.dispatchAll();
assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason);
@@ -639,6 +639,17 @@
verify(mWifiService, never()).stopLocalOnlyHotspot();
}
+ @Test
+ public void testStartLocalOnlyHotspotForwardsCustomConfig() throws Exception {
+ SoftApConfiguration customConfig = new SoftApConfiguration.Builder()
+ .setSsid("customSsid")
+ .build();
+ TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
+ mWifiManager.startLocalOnlyHotspot(customConfig, mExecutor, callback);
+ verify(mWifiService).startLocalOnlyHotspot(
+ any(ILocalOnlyHotspotCallback.class), anyString(), eq(customConfig));
+ }
+
/**
* Verify the watchLocalOnlyHotspot call goes to WifiServiceImpl.
*/