diff options
230 files changed, 4249 insertions, 1510 deletions
diff --git a/Android.bp b/Android.bp index b26b373943ae..9497085a82ca 100644 --- a/Android.bp +++ b/Android.bp @@ -307,6 +307,7 @@ java_library { "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 @@ framework_docs_only_libs = [ ] 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 57bbd7765678..e31aed083453 100644 --- a/api/current.txt +++ b/api/current.txt @@ -9467,6 +9467,7 @@ package android.content { 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 @@ package android.net.wifi { 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 @@ package android.telephony { 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 e6a3e9bf3e8e..a35148a4cbb2 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4734,6 +4734,24 @@ package android.net.wifi { 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 +4764,7 @@ package android.net.wifi { 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 +4808,7 @@ package android.net.wifi { 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 +4872,10 @@ package android.net.wifi { 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 +6270,21 @@ package android.provider { 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 +8373,7 @@ package android.telephony { 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 +9043,14 @@ package android.telephony.ims { 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 +9577,8 @@ package android.telephony.ims.feature { 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 57a853a42676..a907fa64d9ce 100644 --- a/api/system-lint-baseline.txt +++ b/api/system-lint-baseline.txt @@ -257,6 +257,8 @@ SamShouldBeLast: android.media.session.MediaSessionManager#registerCallback(java 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 a060dfcb8311..1c18ccde3fb9 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -2456,6 +2456,36 @@ package android.provider { 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 55dbc17dba5d..7e278e964ab5 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 @@ public class Content { @Override public void onExecute(IContentProvider provider) throws Exception { - provider.insert(resolveCallingPackage(), mUri, mContentValues); + provider.insert(resolveCallingPackage(), null, mUri, mContentValues); } } @@ -522,7 +522,7 @@ public class Content { @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 @@ public class Content { @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 @@ public class Content { @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 @@ public class Content { @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 @@ public class Content { @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 @@ public class Content { @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 6c3dff24e5fe..91cadc97f192 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -200,6 +200,10 @@ void StatsLogProcessor::resetConfigsLocked(const int64_t timestampNs) { } 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 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { 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 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { 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 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { 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::OnConfigRemoved(const ConfigKey& key) { } } -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 @@ void StatsLogProcessor::flushIfNecessaryLocked( // 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 @@ void StatsLogProcessor::flushIfNecessaryLocked( 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 8292a3a9194a..68b12189aa0e 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -147,6 +147,8 @@ private: sp<AlarmMonitor> mPeriodicAlarmMonitor; + void OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs); + void resetIfConfigTtlExpiredLocked(const int64_t timestampNs); void OnConfigUpdatedLocked( @@ -176,8 +178,7 @@ private: /* 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 460b9e0995c8..69e11ed9b836 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -76,9 +76,9 @@ TEST(StatsLogProcessorTest, TestRateLimitByteSize) { // 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 @@ TEST(StatsLogProcessorTest, TestRateLimitBroadcast) { 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 @@ TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) { 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 5da0fca2f3ed..909315552e3f 100644 --- a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp @@ -271,19 +271,19 @@ TEST(DurationMetricE2eTest, TestWithActivation) { // 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 @@ TEST(DurationMetricE2eTest, TestWithActivation) { // 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 @@ TEST(DurationMetricE2eTest, TestWithActivation) { // 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 f1b6029f0ab0..b6a6492fce75 100644 --- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp @@ -290,14 +290,14 @@ TEST(MetricActivationE2eTest, TestCountMetric) { 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 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { 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 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // 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 455e4bbc0b76..b23bf5da5c8d 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 @@ public class ShellUiAutomatorBridge extends UiAutomatorBridge { 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 7de87938c1f6..17f1a07d6e01 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -53,6 +53,7 @@ import android.os.UserHandle; 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall // 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } } 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } @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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } } - 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall * 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } /** {@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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } /** - * 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall * 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 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall /** {@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 8a4330ec0ede..d2632e78c00c 100644 --- a/core/java/android/content/ContentProviderClient.java +++ b/core/java/android/content/ContentProviderClient.java @@ -80,6 +80,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { mContentResolver = contentResolver; mContentProvider = contentProvider; mPackageName = contentResolver.mPackageName; + mFeatureId = contentResolver.mFeatureId; mAuthority = authority; mStable = stable; @@ -193,7 +195,7 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { 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 cd735d4b10a3..f082690e2ceb 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -83,6 +83,7 @@ abstract public class ContentProviderNative extends Binder implements IContentPr data.enforceInterface(IContentProvider.descriptor); String callingPkg = data.readString(); + String callingFeatureId = data.readString(); Uri url = Uri.CREATOR.createFromParcel(data); // String[] projection @@ -101,7 +102,8 @@ abstract public class ContentProviderNative extends Binder implements IContentPr 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 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { 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 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { 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 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { 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 @@ abstract public class ContentProviderNative extends Binder implements IContentPr 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 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { 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 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { 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 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { 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 @@ abstract public class ContentProviderNative extends Binder implements IContentPr 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 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { 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 @@ abstract public class ContentProviderNative extends Binder implements IContentPr 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 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { 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 @@ abstract public class ContentProviderNative extends Binder implements IContentPr 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 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { 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 @@ abstract public class ContentProviderNative extends Binder implements IContentPr { 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 @@ abstract public class ContentProviderNative extends Binder implements IContentPr 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 @@ abstract public class ContentProviderNative extends Binder implements IContentPr 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 @@ final class ContentProviderProxy implements IContentProvider } @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 @@ final class ContentProviderProxy implements IContentProvider data.writeInterfaceToken(IContentProvider.descriptor); data.writeString(callingPkg); + data.writeString(featureId); url.writeToParcel(data, 0); int length = 0; if (projection != null) { @@ -478,7 +497,8 @@ final class ContentProviderProxy implements IContentProvider } @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 @@ final class ContentProviderProxy implements IContentProvider data.writeInterfaceToken(IContentProvider.descriptor); data.writeString(callingPkg); + data.writeString(featureId); url.writeToParcel(data, 0); values.writeToParcel(data, 0); @@ -501,13 +522,15 @@ final class ContentProviderProxy implements IContentProvider } @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 @@ final class ContentProviderProxy implements IContentProvider } @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 @@ final class ContentProviderProxy implements IContentProvider } @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 @@ final class ContentProviderProxy implements IContentProvider } @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 @@ final class ContentProviderProxy implements IContentProvider } @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 @@ final class ContentProviderProxy implements IContentProvider 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 @@ final class ContentProviderProxy implements IContentProvider } @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 @@ final class ContentProviderProxy implements IContentProvider 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 @@ final class ContentProviderProxy implements IContentProvider } @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 @@ final class ContentProviderProxy implements IContentProvider } @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 @@ final class ContentProviderProxy implements IContentProvider } @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 @@ final class ContentProviderProxy implements IContentProvider } @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 @@ final class ContentProviderProxy implements IContentProvider } @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 @@ final class ContentProviderProxy implements IContentProvider } @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 7f9ea76c95d6..2657cc54b91a 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -649,6 +649,7 @@ public abstract class ContentResolver implements ContentInterface { 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 @@ public abstract class ContentResolver implements ContentInterface { 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 @@ public abstract class ContentResolver implements ContentInterface { 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 @@ public abstract class ContentResolver implements ContentInterface { } 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 @@ public abstract class ContentResolver implements ContentInterface { } 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 @@ public abstract class ContentResolver implements ContentInterface { 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 @@ public abstract class ContentResolver implements ContentInterface { 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 @@ public abstract class ContentResolver implements ContentInterface { 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 @@ public abstract class ContentResolver implements ContentInterface { 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 @@ public abstract class ContentResolver implements ContentInterface { 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 @@ public abstract class ContentResolver implements ContentInterface { } 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 @@ public abstract class ContentResolver implements ContentInterface { } 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 @@ public abstract class ContentResolver implements ContentInterface { } 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 @@ public abstract class ContentResolver implements ContentInterface { } 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 @@ public abstract class ContentResolver implements ContentInterface { 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 @@ public abstract class ContentResolver implements ContentInterface { return mPackageName; } + /** @hide */ + public @Nullable String getFeatureId() { + return mFeatureId; + } + @UnsupportedAppUsage private static volatile IContentService sContentService; @UnsupportedAppUsage @@ -3443,6 +3453,7 @@ public abstract class ContentResolver implements ContentInterface { @UnsupportedAppUsage final String mPackageName; + final @Nullable String mFeatureId; final int mTargetSdkVersion; final ContentInterface mWrapped; @@ -3638,19 +3649,19 @@ public abstract class ContentResolver implements ContentInterface { orientation.value = (extras != null) ? extras.getInt(EXTRA_ORIENTATION, 0) : 0; return afd; }), (ImageDecoder decoder, ImageInfo info, Source source) -> { - decoder.setAllocator(allocator); - - // 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); - } + decoder.setAllocator(allocator); + + // 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.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 fade0ab6bb5c..d2c97c4ebdd3 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 @@ import java.util.ArrayList; * @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) + @Deprecated + @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; - @UnsupportedAppUsage - public int update(String callingPkg, Uri url, ContentValues values, String selection, + @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; - 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#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 ParcelFileDescriptor openFile(String callingPkg, @Nullable String featureId, Uri url, + String mode, ICancellationSignal signal, IBinder callerToken) + throws RemoteException, FileNotFoundException; - public ContentProviderResult[] applyBatch(String callingPkg, String authority, - ArrayList<ContentProviderOperation> operations) + 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 Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri) + throws RemoteException; - public boolean refresh(String callingPkg, Uri url, @Nullable Bundle args, - ICancellationSignal cancellationSignal) 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 71b94ed41351..b7a3c8f2f3be 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1314,7 +1314,8 @@ public class UserManager { } /** - * 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 @@ public class UserManager { 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 @@ public class UserManager { 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 8b8afd549c9f..bb8b041f6b46 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -586,9 +586,8 @@ public final class DeviceConfig { @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 @@ public final class DeviceConfig { } } - 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 2143a0deb723..a80153d691f0 100644 --- a/core/java/android/provider/DocumentsProvider.java +++ b/core/java/android/provider/DocumentsProvider.java @@ -1081,7 +1081,8 @@ public abstract class DocumentsProvider extends ContentProvider { // 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 @@ public abstract class DocumentsProvider extends ContentProvider { 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 @@ public abstract class DocumentsProvider extends ContentProvider { && 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 @@ public abstract class DocumentsProvider extends ContentProvider { 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 @@ public abstract class DocumentsProvider extends ContentProvider { 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 @@ public abstract class DocumentsProvider extends ContentProvider { } } 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 @@ public abstract class DocumentsProvider extends ContentProvider { 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 @@ public abstract class DocumentsProvider extends ContentProvider { 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 @@ public abstract class DocumentsProvider extends ContentProvider { 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 @@ public abstract class DocumentsProvider extends ContentProvider { 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 b5580d4d589b..44ab09ed8ad4 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -78,6 +78,7 @@ import android.util.MemoryIntArray; 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 @@ public final class Settings { 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 @@ public final class Settings { 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 @@ public final class Settings { 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 @@ public final class Settings { 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 @@ public final class Settings { } 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 @@ public final class Settings { } // 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 @@ public final class Settings { } 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 final class Settings { 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 @@ public final class Settings { } 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 @@ public final class Settings { } /** - * 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 @@ public final class Settings { */ @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 @@ public final class Settings { } /** - * 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 @@ public final class Settings { * </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 @@ public final class Settings { * @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 @@ public final class Settings { * * @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 2f0a4ebb84f8..59e9ed1512ee 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -712,6 +712,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mSurfaceAlpha = 1f; synchronized (mSurfaceControlLock) { + mSurface.release(); + if (mRtHandlingPositionUpdates) { mRtReleaseSurfaces = true; return; @@ -725,7 +727,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mTmpTransaction.remove(mBackgroundControl); mBackgroundControl = null; } - mSurface.release(); mTmpTransaction.apply(); } } @@ -1198,7 +1199,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall 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 20dc23492ae5..85bf19f855bb 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -439,7 +439,6 @@ public final class ViewRootImpl implements ViewParent, boolean mReportNextDraw; boolean mFullRedrawNeeded; boolean mNewSurfaceNeeded; - boolean mHasHadWindowFocus; boolean mLastWasImTarget; boolean mForceNextWindowRelayout; CountDownLatch mWindowDrawCountDown; @@ -2123,11 +2122,6 @@ public final class ViewRootImpl implements ViewParent, 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 @@ public final class ViewRootImpl implements ViewParent, 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 @@ public final class ViewRootImpl implements ViewParent, 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 @@ public final class ViewRootImpl implements ViewParent, .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 6420d71216cd..7ee53f26b1f8 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -358,7 +358,7 @@ public final class InputMethodManager { 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 @@ public final class InputMethodManager { * @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 @@ public final class InputMethodManager { 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/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java index 025e27bc45c9..382a254b67a6 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java +++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java @@ -168,9 +168,6 @@ public final class InputMethodDebug { 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 ba26d8db533c..5a8d2c227256 100644 --- a/core/java/com/android/internal/inputmethod/StartInputFlags.java +++ b/core/java/com/android/internal/inputmethod/StartInputFlags.java @@ -30,7 +30,6 @@ import java.lang.annotation.Retention; @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 @@ public @interface StartInputFlags { 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 c0e4e1fe5e7a..3704ccdfb8ea 100644 --- a/core/jni/fd_utils.cpp +++ b/core/jni/fd_utils.cpp @@ -59,8 +59,8 @@ FileDescriptorWhitelist* FileDescriptorWhitelist::Get() { 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 @@ bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const { 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 @@ void FileDescriptorInfo::ReopenOrDetach(fail_fn_t fail_fn) const { 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 @@ FileDescriptorTable::FileDescriptorTable( } 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 @@ void FileDescriptorTable::RestatInternal(std::set<int>& open_fds, fail_fn_t fail // 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 f14f2896d8f5..e140ad2a9a8f 100644 --- a/core/tests/coretests/src/android/content/ContentResolverTest.java +++ b/core/tests/coretests/src/android/content/ContentResolverTest.java @@ -82,22 +82,23 @@ public class ContentResolverTest { 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 @@ public class ContentResolverTest { assertEquals(1280, res.getWidth()); assertEquals(960, res.getHeight()); - assertImageAspectAndContents(res); + assertImageAspectAndContents(1280, 960, res); } @Test @@ -127,7 +128,7 @@ public class ContentResolverTest { assertTrue(res.getWidth() <= 640); assertTrue(res.getHeight() <= 480); - assertImageAspectAndContents(res); + assertImageAspectAndContents(1280, 960, res); } @Test @@ -142,7 +143,7 @@ public class ContentResolverTest { assertTrue(res.getWidth() <= 640); assertTrue(res.getHeight() <= 480); - assertImageAspectAndContents(res); + assertImageAspectAndContents(1280, 960, res); } @Test @@ -157,7 +158,23 @@ public class ContentResolverTest { 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 000000000000..6ae7eb72fab2 --- /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 1f2dfe0fa01c..0c83390f9f1e 100644 --- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java +++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java @@ -388,6 +388,13 @@ public class DeviceConfigTest { 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 1bd8ff6bdea3..5f640bee7ce9 100644 --- a/core/tests/coretests/src/android/provider/TestDocumentsProvider.java +++ b/core/tests/coretests/src/android/provider/TestDocumentsProvider.java @@ -93,12 +93,14 @@ public class TestDocumentsProvider extends DocumentsProvider { } @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 b3f6fdbd9b30..364e4ea6e3ec 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.content.res.Configuration; 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 @@ import org.mockito.quality.Strictness; */ @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 552088f7c478..ba96a06cc852 100644 --- a/graphics/java/android/graphics/fonts/Font.java +++ b/graphics/java/android/graphics/fonts/Font.java @@ -519,12 +519,13 @@ public final class Font { } 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 ec83a1961eff..1eb089d8764c 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -136,7 +136,6 @@ public: 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 b98ffca84dfc..c138a32eacc2 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -95,18 +95,10 @@ public: 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 @@ void Canvas::drawText(const uint16_t* text, int textSize, int start, int count, 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 b90a4a3d68e6..27dfed305a94 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -253,15 +253,6 @@ public: 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 d3db9d83b3e7..c3aae7def593 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -1835,7 +1835,7 @@ public class LocationManager { } try { - return mGnssStatusListenerManager.addListener(listener, new Handler()); + return mGnssStatusListenerManager.addListener(listener, Runnable::run); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2694,9 +2694,9 @@ public class LocationManager { 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/media/java/android/media/Utils.java b/media/java/android/media/Utils.java index 5b62f16ad315..d942bb653127 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 @@ class Utils { } 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 @@ class Utils { 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 @@ class Utils { 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 @@ class Utils { } 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 @@ class Utils { 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 @@ class Utils { } 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 @@ class Utils { 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 @@ class Utils { } 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 @@ class Utils { 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 000000000000..64c0bb53e894 --- /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 0228dc94fb25..2257747c2c8b 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 @@ public final class Tuner implements AutoCloseable { 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 @@ public final class Tuner implements AutoCloseable { * 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 a596d89bdcce..3742f970e8fd 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -132,6 +132,7 @@ cc_library_shared { 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 d499eee5a9da..ba133fc44fb7 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 @@ sp<ITuner> JTuner::getTunerService() { 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 @@ static void android_media_tv_Tuner_native_init(JNIEnv *env) { 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 @@ static void android_media_tv_Tuner_native_setup(JNIEnv *env, jobject thiz) { 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 e7d5924a7dbe..1425542aa648 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 @@ namespace android { 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 @@ private: 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 74bf1a20304a..de353bf58aec 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 @@ import org.mockito.MockitoAnnotations; 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 @@ public class MediaInserterTest extends InstrumentationTestCase { 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 @@ public class MediaInserterTest extends InstrumentationTestCase { 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 @@ public class MediaInserterTest extends InstrumentationTestCase { @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 @@ public class MediaInserterTest extends InstrumentationTestCase { 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 @@ public class MediaInserterTest extends InstrumentationTestCase { 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 @@ public class MediaInserterTest extends InstrumentationTestCase { 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 9791da63359b..45f42f1b5dc6 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 @@ struct XmlDocDeleter { 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 @@ std::string xmlTrim(const std::string& in) { 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 @@ xmlNode* nextSibling(xmlNode* node, const xmlChar* tag) { 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 @@ void copyFont(const XmlDocUniquePtr& xmlDoc, xmlNode* fontNode, AFont* out, 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 @@ bool isFontFileAvailable(const std::string& filePath) { 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 @@ AFont* _Nonnull AFontMatcher_match( 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 @@ AFont* ASystemFontIterator_next(ASystemFontIterator* ite) { } 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 @@ bool AFont_isItalic(const AFont* font) { 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 672879ae6e9d..b2451c91057c 100644 --- a/packages/CarSystemUI/Android.bp +++ b/packages/CarSystemUI/Android.bp @@ -63,6 +63,64 @@ android_library { } +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/CarSystemUI/src/com/android/systemui/CarComponentBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarComponentBinder.java new file mode 100644 index 000000000000..c40eda9fb303 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/CarComponentBinder.java @@ -0,0 +1,32 @@ +/* + * 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; + +import com.android.systemui.dagger.DefaultActivityBinder; +import com.android.systemui.dagger.DefaultServiceBinder; + +import dagger.Module; + +/** + * Supply Activities, Services, and SystemUI Objects for CarSystemUI. + */ +@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 8e0a3eb53e6f..4f7b5d530c96 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.ClassKey; 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 93e553f59dcf..d3a6cde66e84 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java @@ -46,8 +46,6 @@ import javax.inject.Singleton; 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 @@ abstract class CarSystemUIModule { 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 c2847c88785b..51b263ebdb4e 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java @@ -29,6 +29,7 @@ import dagger.Component; @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 63bc66afddf8..98b91ebd8038 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.RemoteException; 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 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks } 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 f59f886d487b..6bed69bdee88 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 @@ public class CarNavigationBarController { } /** 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 @@ public class CarNavigationBarController { } } + /** + * 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 @@ public class CarNavigationBarController { } } } + + 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 24f8b74eed61..c2455088a52b 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 android.widget.LinearLayout; 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 @@ import com.android.systemui.statusbar.phone.StatusBarIconController; 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 @@ public class CarNavigationBarView extends LinearLayout { 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 @@ public class CarNavigationBarView extends LinearLayout { 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 40823abaaead..922bfffcfa22 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 @@ public class CarNavigationButton extends com.android.keyguard.AlphaOptimizedImag 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 601bc4030226..d548fa15fe1a 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.Car; 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 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt 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 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt // 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 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt 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 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt * 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 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt * 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 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt 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 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt 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 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt mNotificationView, PreprocessingManager.getInstance(mContext), carNotificationListener, - carUxRestrictionManagerWrapper, + mCarUxRestrictionManagerWrapper, mNotificationDataManager); mNotificationViewController.enable(); } @@ -895,37 +853,27 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt @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 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt 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 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt @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 a4424260fef5..cd87e78e4be9 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 @@ public class DrivingStateHelper { 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 @@ public class DrivingStateHelper { * 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 0f7c1ee8ea7e..31aced02b15e 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 @@ package com.android.systemui.statusbar.car; 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 @@ public class FullscreenUserSwitcher { 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 @@ public class FullscreenUserSwitcher { 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 @@ public class FullscreenUserSwitcher { 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 @@ public class FullscreenUserSwitcher { } 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 8de1439c3306..a27dd341d449 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 @@ package com.android.systemui.statusbar.car; 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 @@ public class PowerManagerHelper { 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 3b482599b2a0..05657fff70e0 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.Intent; 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 @@ public class UserGridRecyclerView extends RecyclerView { private CarUserManagerHelper mCarUserManagerHelper; private UserManager mUserManager; private Context mContext; + private UserIconProvider mUserIconProvider; private final BroadcastReceiver mUserUpdateReceiver = new BroadcastReceiver() { @Override @@ -80,6 +80,7 @@ public class UserGridRecyclerView extends RecyclerView { 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 @@ public class UserGridRecyclerView extends RecyclerView { @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 @@ public class UserGridRecyclerView extends RecyclerView { } } - private Bitmap getUserRecordIcon(UserRecord userRecord) { + private RoundedBitmapDrawable getCircularUserRecordIcon(UserRecord userRecord) { + Resources resources = mContext.getResources(); + RoundedBitmapDrawable circleIcon; if (userRecord.mIsStartGuestSession) { - return mCarUserManagerHelper.getGuestDefaultIcon(); - } - - if (userRecord.mIsAddUser) { - return UserIcons.convertToBitmap(mContext - .getDrawable(R.drawable.car_add_circle_round)); + 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); } - 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 000000000000..9464eab2085b --- /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 e81be1b0b186..41914d212ee9 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.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL; 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 @@ public class HvacController { 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 @@ public class HvacController { + " 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 class HvacController { */ 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 22c7c7a3d6af..d979bad94677 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.annotation.Nullable; 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.graphics.drawable.Drawable; 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 @@ public class CarVolumeDialogImpl implements VolumeDialog { 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 class CarVolumeDialogImpl implements VolumeDialog { 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 @@ public class CarVolumeDialogImpl implements VolumeDialog { @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 @@ public class CarVolumeDialogImpl implements VolumeDialog { 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 @@ public class CarVolumeDialogImpl implements VolumeDialog { // 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 000000000000..1366568c3a66 --- /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 000000000000..a74bb56d8d75 --- /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 000000000000..8685632f2b63 --- /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 000000000000..0d08ac26d962 --- /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 000000000000..fe59cbf20a13 --- /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 000000000000..901d2006eb12 --- /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 48d34ae7ba47..af96982f5426 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -129,17 +129,17 @@ public class ExternalStorageProvider extends FileSystemProvider { } @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 c4df2e8860b4..b9daf7fcab4e 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 @@ public class TileUtils { 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 245ca140b4f0..f25b5eb294e0 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 bfd31968bd6c..6332c848c5a6 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 8b67b0b1347e..8c72527604a4 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 f3ca337b261d..a7ea1e020f03 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 d35dfe8255d2..cb7db78fd018 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 448de4b8b7dd..75feb326cd77 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 d68c0f3e02ab..677aa24ac43d 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 cb99f64ba498..162042209c46 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 f2f4f5249b74..b1e37a66fa82 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 45b8dd99ec70..911a8315523f 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 b624df056bec..58c2b670630d 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 042e12a14967..3c3d5b811d26 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 4723293f302d..bf5d6cfd472d 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 4f5a965f9ce0..a6dbd5ae215a 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 753dea8855dd..fdba74a4dfd8 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 dd3d27803db3..581adf86d062 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 dd3d27803db3..581adf86d062 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 dd3d27803db3..581adf86d062 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 dd3d27803db3..581adf86d062 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 d9f61d8188f1..e75d7bc19bbf 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 30cb0a1f113f..97cce55c11d8 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 32905dfb9213..7ba1a9435be2 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 79b8a848f870..0e987528d73a 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 38ae9c243f6e..872e9a56ed87 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 a883d6cddf47..6e281fe05486 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 929e101bf610..8c3630a08c9b 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 d3bfc96ea473..6c5834ae793a 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 b5706cea932d..508556ece88a 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 2aa1604cca21..1a3ae3d601e1 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 c2cc4c5d9410..b3f518541c71 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 66c7a16e1bf6..454773ca3538 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 0fc16f847822..0ec154baa0e9 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 1388dbb684fa..4a0af7d27ab5 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 2145adb1036e..cc0ecb8125b6 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 7ebe6b7f8180..0733c1f76a66 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 eede11756e9e..28ed8fc6224a 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 2c64ec2e6705..b0569b0f96f2 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 30d6a4aa1d5e..f7d4fcd317ad 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 6cb5514cc582..4d5c86cafe3a 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 4c78a3864562..20342bc011cd 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 ce086577ec00..7b6d29eb43ad 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 43c92824015b..115be8e07f59 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 253104b38661..ac8bfb1654cb 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 ac44c0db62a3..4e543103fe60 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 dd1ff30faf44..e891b5a902cb 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 28e811153ea1..406a42b2b041 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 93fcaa798998..b305fd90d1cc 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 c12d097232f4..c9e2c1114556 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 9a76a0a8e04c..e06d414c2453 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 bb762951d437..36e632a917f5 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 65a8ca6c1253..1a5a0af7bd5c 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 3d75ad6d158f..751010759fd0 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 0b2a4b0879c4..82bd697a6a52 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 6fde69a38767..9636f0675dd4 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 66ad20e18839..99af7c88598a 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 860e9bf6b1b7..cdf2c7d27134 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 2fff3283d5fd..709e98d5b2c7 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 ff2a5349f40f..abe01984e9ef 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 0cf95753ae18..b372185be7f2 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 caed8c349af7..e7a8f22b52a5 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 04a24f1a9175..6e75fbaa9d3f 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 a206d1ad787c..4ee0a17cec74 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 04a24f1a9175..6e75fbaa9d3f 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 76a56e51ef88..c5725d3262d7 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 6e1d29ad13d8..6e98601fbcd9 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 186c23adaec1..0f67a6517ccc 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 9559975cb156..e4b48487688d 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 62e4ad1f91f4..3e181c2cacb0 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 0498f3123543..53803022f388 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 3157deed7127..d68c9e8a1f22 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 31517b8ab00a..45841f0ee816 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 d73084ffd3e8..a8e73d75dad2 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 de70d085d491..83fe954dcac8 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 03ae94f39aef..ad0f673886c1 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 1cfe45e72a9e..c9e232d45a85 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 8b3118b333d1..74307f8e3912 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 5891c791cec9..b95d941a83b3 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 0bca3077a6ad..61a0c3e6128e 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 1e27b487ca27..f21e891fb9ae 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 487100c94feb..a0a79e38422f 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 c247617b31dc..3723b83db79d 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 dddc1071dd9b..f1200ee13404 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 8560e22c587b..57ab472b5d3f 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 404aa19c87c6..630619bba7ab 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 542332f10454..ede336e831f9 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 f7fc0c5fdc49..0c3254a5ed02 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 @@ public class SecureSettingsValidators { 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 8fb879d3606b..1e75fe70becd 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 @@ public final class DeviceConfigService extends Binder { 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 @@ public final class DeviceConfigService extends Binder { 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 fdc987fb0919..776593590a67 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 @@ public class SettingsProvider extends ContentProvider { } 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 36360a31a4b7..3b3ca5b02417 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 @@ final public class SettingsService extends Binder { 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 @@ final public class SettingsService extends Binder { 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 @@ final public class SettingsService extends Binder { 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 @@ final public class SettingsService extends Binder { 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 @@ final public class SettingsService extends Binder { } 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 9255c8728392..10d990a3d07f 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -732,7 +732,8 @@ public class SettingsBackupTest { 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 3f56ff07b24d..8825f1279796 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java @@ -67,13 +67,11 @@ public class SystemUIService extends Service { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (args != null && args.length > 0 && args[0].equals("--config")) { + dumpServices(((SystemUIApplication) getApplication()).getServices(), fd, pw, args); + + if (args == null || args.length == 0 || args[0].equals("--config")) { dumpConfig(pw); - return; } - - dumpServices(((SystemUIApplication) getApplication()).getServices(), fd, pw, args); - 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 ff4711cb208a..98d7f8b864fb 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.Handler 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 @@ private const val DEBUG = false * 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 @@ open class BroadcastDispatcher @Inject constructor ( * * @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 @@ open class BroadcastDispatcher @Inject constructor ( 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 index 4be610fcd9ee..61ded138134c 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 @@ import dagger.multibindings.IntoMap; * 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 index 4e4c06e9d447..d8989ee624fa 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 index 1f2c0a18f928..14bb80c79e27 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 @@ import dagger.multibindings.IntoMap; * 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 738f53920b98..27c526b22283 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.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.phone.StatusBar; import com.android.systemui.util.leak.GarbageMonitor; import com.android.systemui.volume.VolumeUI; @@ -80,6 +81,12 @@ public abstract class SystemUIBinder { @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 c95b50b195b3..7b8d3bc4a121 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 static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME; 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 javax.inject.Singleton; 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 @@ abstract class SystemUIDefaultModule { @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 4e60f194e552..ca8e53deffdc 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 com.android.systemui.util.sensors.AsyncSensorManager; import javax.inject.Singleton; +import dagger.Binds; import dagger.Module; import dagger.Provides; @@ -38,9 +39,12 @@ import dagger.Provides; * 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 @@ public abstract class SystemUIModule { 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 113c9c845d95..83d956c22fb8 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 @@ import dagger.Component; */ @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 89b7a8181c44..acf761ed3936 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 @@ public abstract class RichEvent extends Event { 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 a6e10e6b345b..f094cb909cf2 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 @@ public class SysuiLog implements Dumpable { } /** - * @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 @@ public class SysuiLog implements Dumpable { } /** + * @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 0988e347945c..d668665f062c 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 @@ public class NotificationMediaManager implements Dumpable { if (!isPlaybackActive(state.getState())) { clearCurrentMediaNotification(); } - dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */); + findAndUpdateMediaNotifications(); } } @@ -200,6 +200,16 @@ public class NotificationMediaManager implements Dumpable { 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 @@ public class NotificationMediaManager implements Dumpable { 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 @@ public class NotificationMediaManager implements Dumpable { // 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 bde097a89f6b..404087d9b973 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.FileDescriptor; 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 @@ public class NotificationEntryManager implements } /** + * @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 179375e31dd3..4e91e4c84e99 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 @@ public class KeyguardClockPositionAlgorithm { * @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 971843ec0187..afc147a75c56 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 @@ public class StatusBar extends SystemUI implements DemoMode, .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 f2d2faed6a42..2c996684f437 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 @@ public class UserSwitcherController implements Dumpable { } 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 2bff5488937d..ead14e501193 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.Context 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.junit.runner.RunWith 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 @@ class BroadcastDispatcherTest : SysuiTestCase() { 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 @@ class BroadcastDispatcherTest : SysuiTestCase() { 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 @@ class BroadcastDispatcherTest : SysuiTestCase() { 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 a1822c72fe18..f5d6f223b86c 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 @@ class PeopleHubViewControllerTest : SysuiTestCase() { 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 @@ class PeopleHubViewControllerTest : SysuiTestCase() { 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 @@ class PeopleHubViewControllerTest : SysuiTestCase() { 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 @@ class PeopleHubViewControllerTest : SysuiTestCase() { } } +/** 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 ca69c187089d..2bfe287c82b3 100644 --- a/packages/Tethering/Android.bp +++ b/packages/Tethering/Android.bp @@ -21,6 +21,7 @@ java_defaults { "src/**/*.java", ":framework-tethering-shared-srcs", ":services-tethering-shared-srcs", + ":servicescore-tethering-src", ], static_libs: [ "androidx.annotation_annotation", @@ -67,9 +68,17 @@ android_app { // 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 index f952bcef5606..6b0f1de7ce85 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 @@ public class EntitlementManager { 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 class EntitlementManager { 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 @@ public class EntitlementManager { /** * 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 @@ public class EntitlementManager { 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 da621076bb63..5564bd6ecbca 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 @@ android_test { 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 index 5217e26a40ef..5217e26a40ef 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java +++ b/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 3067bebcf19e..c86538438835 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -80,6 +80,7 @@ java_library_static { ":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 @@ prebuilt_etc { 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/NetworkScorerAppManager.java b/services/core/java/com/android/server/NetworkScorerAppManager.java index bfd4247abf40..3bcb36fd0e00 100644 --- a/services/core/java/com/android/server/NetworkScorerAppManager.java +++ b/services/core/java/com/android/server/NetworkScorerAppManager.java @@ -18,10 +18,10 @@ package com.android.server; 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 @@ public class NetworkScorerAppManager { } 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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 11cfe12f6e67..5df45437ae34 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 f7ac04041ed6..6010b1dc88c4 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 @@ import java.io.PrintWriter; 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 @@ import java.io.PrintWriter; //------------------------------------------------------------------- - /** - * 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 @@ import java.io.PrintWriter; // 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 @@ import java.io.PrintWriter; * @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 @@ import java.io.PrintWriter; * @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 @@ import java.io.PrintWriter; } /*package*/ boolean isSpeakerphoneOn() { - synchronized (mDeviceBrokerLock) { + synchronized (mDeviceStateLock) { return (mForcedUseForCommExt == AudioSystem.FORCE_SPEAKER); } } @@ -221,7 +223,9 @@ import java.io.PrintWriter; @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 @@ import java.io.PrintWriter; 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); - - sendLMsgNoDelay( - state == BluetoothProfile.STATE_CONNECTED - ? MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION - : MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION, - SENDMSG_QUEUE, info); - } + // 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); + } + + /** 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 @@ import java.io.PrintWriter; 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 @@ import java.io.PrintWriter; } /*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) { - mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource); + synchronized (mDeviceStateLock) { + 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 @@ import java.io.PrintWriter; //--------------------------------------------------------------------- // 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 @@ import java.io.PrintWriter; .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 @@ import java.io.PrintWriter; /*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 @@ import java.io.PrintWriter; 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 @@ import java.io.PrintWriter; + " 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 @@ import java.io.PrintWriter; + " 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 @@ import java.io.PrintWriter; /** 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 @@ import java.io.PrintWriter; 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 3933fb2d5f46..90973a888a9d 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 @@ public class AudioDeviceInventory { } // only public for mocking/spying + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") @VisibleForTesting public void onSetA2dpSinkConnectionState(@NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, @AudioService.BtProfileConnectionState int state) { @@ -283,6 +284,7 @@ public class AudioDeviceInventory { } } + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ void onBluetoothA2dpActiveDeviceChange( @NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int event) { final BluetoothDevice btDevice = btInfo.getBtDevice(); @@ -555,6 +557,7 @@ public class AudioDeviceInventory { } // 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 cc50e37d2b61..0d493b825133 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 @@ public class AudioService extends IAudioService.Stub // 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 @@ public class AudioService extends IAudioService.Stub * @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 class AudioService extends IAudioService.Stub 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 @@ public class AudioService extends IAudioService.Stub } 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 @@ public class AudioService extends IAudioService.Stub /** @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 @@ public class AudioService extends IAudioService.Stub int oldModeOwnerPid = 0; int newModeOwnerPid = 0; - synchronized (mSetModeDeathHandlers) { + synchronized (mDeviceBroker.mSetModeLock) { if (!mSetModeDeathHandlers.isEmpty()) { oldModeOwnerPid = mSetModeDeathHandlers.get(0).getPid(); } @@ -3280,21 +3280,17 @@ public class AudioService extends IAudioService.Stub 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 @@ public class AudioService extends IAudioService.Stub !mSystemReady) { return; } - mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource); + synchronized (mDeviceBroker.mSetModeLock) { + mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource); + } } /** @see AudioManager#stopBluetoothSco() */ @@ -3645,7 +3643,9 @@ public class AudioService extends IAudioService.Stub 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 @@ public class AudioService extends IAudioService.Stub // 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 625b6b690443..9f1a6bd15ac3 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 @@ public class BtHelper { //---------------------------------------------------------------------- // 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 @@ public class BtHelper { 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 @@ public class BtHelper { * * @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 @@ public class BtHelper { 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 @@ public class BtHelper { 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 @@ public class BtHelper { mDeviceBroker.postDisconnectHearingAid(); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void resetBluetoothSco() { clearAllScoClients(0, false); mScoAudioState = SCO_STATE_INACTIVE; @@ -421,6 +433,8 @@ public class BtHelper { mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco"); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void disconnectHeadset() { setBtScoActiveDevice(null); mBluetoothHeadset = null; @@ -466,6 +480,8 @@ public class BtHelper { /*eventSource*/ "mBluetoothProfileServiceListener"); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void onHeadsetProfileConnected(BluetoothHeadset headset) { // Discard timeout message mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService(); @@ -552,6 +568,8 @@ public class BtHelper { 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 @@ public class BtHelper { }; //---------------------------------------------------------------------- + // @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 @@ public class BtHelper { 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 @@ public class BtHelper { mStartcount++; } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + // @GuardedBy("AudioDeviceBroker.mDeviceStateLock") @GuardedBy("BtHelper.this") void decCount() { if (mStartcount == 0) { @@ -702,6 +726,8 @@ public class BtHelper { } } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + // @GuardedBy("AudioDeviceBroker.mDeviceStateLock") @GuardedBy("BtHelper.this") void clearCount(boolean stopSco) { if (mStartcount != 0) { @@ -738,6 +764,8 @@ public class BtHelper { return count; } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") @GuardedBy("BtHelper.this") private boolean requestScoState(int state, int scoAudioMode) { checkScoAudioState(); @@ -931,6 +959,8 @@ public class BtHelper { 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 d5706a59b987..3b0069cbf001 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 @@ public class DisplayTransformManager { @GuardedBy("mDaltonizerModeLock") private int mDaltonizerMode = -1; + private static final IBinder sFlinger = ServiceManager.getService(SURFACE_FLINGER); + /* package */ DisplayTransformManager() { } @@ -195,25 +197,22 @@ public class DisplayTransformManager { * 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); - } - 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(); + 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); + } + 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 @@ public class DisplayTransformManager { * 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 @@ public class DisplayTransformManager { * #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 @@ public class DisplayTransformManager { */ 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 @@ public class DisplayTransformManager { 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/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index c8179a767d23..dc0cd184c188 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 android.util.SparseArray; 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 @@ public class AppsFilter { 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 @@ public class AppsFilter { @Override public boolean packageIsEnabled(PackageParser.Package pkg) { - return mCompatibility.isChangeEnabled( + return mInjector.getCompatibility().isChangeEnabled( PackageManager.FILTER_APPLICATION_QUERY, pkg.applicationInfo); } } @@ -263,10 +262,10 @@ public class AppsFilter { * 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 74a85d58c016..b36958a69162 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.ServiceThread; 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 @@ public class PackageManagerService extends IPackageManager.Stub 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 @@ public class PackageManagerService extends IPackageManager.Stub 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 @@ public class PackageManagerService extends IPackageManager.Stub return mAppsFilterProducer.get(this, mPackageManager); } - public CompatConfig getCompatibility() { + public PlatformCompat getCompatibility() { return mPlatformCompatProducer.get(this, mPackageManager); } } @@ -2356,7 +2356,7 @@ public class PackageManagerService extends IPackageManager.Stub 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 ff0dc54f86b1..f87175de1d06 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 @@ class ActivityStarter { 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 a783ee9d4e44..6e238b33d8dd 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 @@ public class DisplayPolicy { 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 4c8ce9ebb72c..0f92bc83a666 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.View; 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 @@ class PolicyControl { 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 @@ class PolicyControl { : (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 @@ class PolicyControl { } } - 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 a8c76827c43c..64c7935efff9 100644 --- a/services/core/jni/com_android_server_VibratorService.cpp +++ b/services/core/jni/com_android_server_VibratorService.cpp @@ -190,7 +190,7 @@ static void vibratorSetExternalControl(JNIEnv*, jclass, jboolean enabled) { } } -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 2ab8189150de..e24dec562a46 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 4d653b99439c..d34f78353e71 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 1ad7b6e8d155..79af34d80686 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 @@ public class NetworkScoreServiceTest { @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 8965152257c6..129d2633ae92 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 @@ package com.android.server.am; 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 @@ import java.util.List; @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 @@ public class ActivityManagerTest { } } } + + @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 29a8dada7d89..5c2ad94720f0 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 @@ public class AudioDeviceBrokerTest { 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 000000000000..5cbd39c39434 --- /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 000000000000..8789992280d0 --- /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 000000000000..75f71d609a13 --- /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 80439cf66387..a1322b9a9015 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.os.UserHandle; 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 @@ public class PreferencesHelperTest extends UiServiceTestCase { 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 @@ public class PreferencesHelperTest extends UiServiceTestCase { // 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 @@ public class PreferencesHelperTest extends UiServiceTestCase { .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 @@ public class PreferencesHelperTest extends UiServiceTestCase { @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 @@ public class PreferencesHelperTest extends UiServiceTestCase { @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 2abd340e967a..8774b639b554 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 @@ public class RankingHelperTest extends UiServiceTestCase { 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 3b336eb7aec9..aceed86220c6 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.anyInt; 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 @@ public class PinnedSliceStateTest extends UiServiceTestCase { 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 @@ public class PinnedSliceStateTest extends UiServiceTestCase { // 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 @@ public class PinnedSliceStateTest extends UiServiceTestCase { 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 2a3731a2e483..67b7a66b4794 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_0; 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.DisplayCutout; 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 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { 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 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { 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 2933b4a460d8..d4558dc29fda 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 @@ public class DisplayPolicyTestsBase extends WindowTestsBase { 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 000000000000..7b53d09fdbef --- /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 bc29b595b0bb..dc95f1678b0b 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 @@ public final class Telephony { } /** - * 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 @@ public final class Telephony { /** * 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 @@ public final class Telephony { /** * 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 @@ public final class Telephony { /** * 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 @@ public final class Telephony { * "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 @@ public final class Telephony { * 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 58e15b91fa6c..7bab2230fe05 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -3224,6 +3224,14 @@ public class CarrierConfigManager { 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 @@ public class CarrierConfigManager { 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/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 2eb4809bcb27..8455e3d57e49 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -1873,7 +1873,12 @@ public class TelephonyManager { 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 @@ public class TelephonyManager { } /** - * 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 2161dcb9542f..a5d62669de05 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -1191,7 +1191,7 @@ public class ApnSetting implements Parcelable { && !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 @@ public class ApnSetting implements Parcelable { && 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 @@ public class ApnSetting implements Parcelable { 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 2fad8479178e..ed292be9cd05 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -25,14 +25,13 @@ import android.annotation.Nullable; 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.telephony.ims.stub.ImsRegistrationImplBase; 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.lang.annotation.RetentionPolicy; 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 @@ import java.util.concurrent.Executor; @SystemApi public class ImsMmTelManager { - private static final String TAG = "ImsMmTelManager"; - /** * @hide */ @@ -311,7 +310,7 @@ public class ImsMmTelManager { } } - private int mSubId; + private final int mSubId; /** * Create an instance of {@link ImsMmTelManager} for the subscription id specified. @@ -366,10 +365,6 @@ public class ImsMmTelManager { 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 @@ public class ImsMmTelManager { // 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 @@ public class ImsMmTelManager { 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 @@ public class ImsMmTelManager { // 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 @@ public class ImsMmTelManager { } /** + * 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 @@ public class ImsMmTelManager { * @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 @@ public class ImsMmTelManager { } } - 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 ceb470491dc5..8b27b6fc4bfd 100644 --- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java +++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java @@ -362,6 +362,25 @@ public class MmTelFeature extends ImsFeature { @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 @@ public class MmTelFeature extends ImsFeature { /** * 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 f79a5c654a63..b2f9addb9c5f 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -867,6 +867,11 @@ interface ITelephony { 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 @@ interface ITelephony { 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 4d8c7d930bde..9d3e12050193 100644 --- a/test-mock/src/android/test/mock/MockContentProvider.java +++ b/test-mock/src/android/test/mock/MockContentProvider.java @@ -56,21 +56,22 @@ public class MockContentProvider extends ContentProvider { */ 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 @@ public class MockContentProvider extends ContentProvider { } @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 @@ public class MockContentProvider extends ContentProvider { } @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 @@ public class MockContentProvider extends ContentProvider { } @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 b072d7440de4..e512b52643f3 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 @@ import java.util.ArrayList; */ 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 @@ public class MockIContentProvider implements IContentProvider { @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 @@ public class MockIContentProvider implements IContentProvider { } @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 @@ public class MockIContentProvider implements IContentProvider { } @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 @@ public class MockIContentProvider implements IContentProvider { } @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/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 19be13268d27..023df70ef55a 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.ITrafficStateCallback; 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 @@ interface IWifiManager 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 000000000000..1d06f458318f --- /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 000000000000..4cc86539926c --- /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 3bedddc8ec7d..30c24d37a494 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -34,6 +34,7 @@ import android.os.Parcel; 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 class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { /** * @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 @@ public class WifiConfiguration implements Parcelable { .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 @@ public class WifiConfiguration implements Parcelable { } 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 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { 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 f8c20111d79b..7b99a2b5502f 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -1263,4 +1263,23 @@ public class WifiEnterpriseConfig implements Parcelable { 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 8f71b0ba136f..380ebf104a14 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -71,6 +71,7 @@ import java.util.ArrayList; 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 @@ public class WifiManager { } } - 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 @@ public class WifiManager { * @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 @@ public class WifiManager { 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 class WifiManager { */ 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 @@ public class WifiManager { * * @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 @@ public class WifiManager { } final LocalOnlyHotspotReservation reservation = manager.new LocalOnlyHotspotReservation(config); + if (mCallback == null) return; mExecutor.execute(() -> mCallback.onStarted(reservation)); } @@ -3514,6 +3562,7 @@ public class WifiManager { if (manager == null) return; Log.w(TAG, "LocalOnlyHotspotCallbackProxy: hotspot stopped"); + if (mCallback == null) return; mExecutor.execute(() -> mCallback.onStopped()); } @@ -3524,6 +3573,7 @@ public class WifiManager { 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 9b529cee58b3..246e96f4ce3f 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java +++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java @@ -21,12 +21,15 @@ import static com.android.internal.util.Preconditions.checkNotNull; 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 @@ public final class WifiNetworkSuggestion implements Parcelable { */ 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 @@ public final class WifiNetworkSuggestion implements Parcelable { mIsUserInteractionRequired = false; mIsMetered = false; mPriority = UNASSIGNED_PRIORITY; + mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID; } /** @@ -258,6 +268,23 @@ public final class WifiNetworkSuggestion implements Parcelable { } /** + * 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 @@ public final class WifiNetworkSuggestion implements Parcelable { wifiConfiguration.meteredOverride = mIsMetered ? WifiConfiguration.METERED_OVERRIDE_METERED : WifiConfiguration.METERED_OVERRIDE_NONE; + wifiConfiguration.carrierId = mCarrierId; return wifiConfiguration; } @@ -405,6 +433,7 @@ public final class WifiNetworkSuggestion implements Parcelable { 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 e9aa076798e1..5befb54ce50a 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.net.wifi.hotspot2.pps.UpdateParameter; 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 @@ public final class PasspointConfiguration implements Parcelable { } /** + * 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 @@ public final class PasspointConfiguration implements Parcelable { mUsageLimitUsageTimePeriodInMinutes = source.mUsageLimitUsageTimePeriodInMinutes; mServiceFriendlyNames = source.mServiceFriendlyNames; mAaaServerTrustedNames = source.mAaaServerTrustedNames; + mCarrierId = source.mCarrierId; } @Override @@ -466,6 +492,7 @@ public final class PasspointConfiguration implements Parcelable { bundle.putSerializable("serviceFriendlyNames", (HashMap<String, String>) mServiceFriendlyNames); dest.writeBundle(bundle); + dest.writeInt(mCarrierId); } @Override @@ -495,6 +522,7 @@ public final class PasspointConfiguration implements Parcelable { && 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 @@ public final class PasspointConfiguration implements Parcelable { mUpdateIdentifier, mCredentialPriority, mSubscriptionCreationTimeInMillis, mSubscriptionExpirationTimeInMillis, mUsageLimitUsageTimePeriodInMinutes, mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes, - mServiceFriendlyNames); + mServiceFriendlyNames, mCarrierId); } @Override @@ -558,6 +586,7 @@ public final class PasspointConfiguration implements Parcelable { if (mServiceFriendlyNames != null) { builder.append("ServiceFriendlyNames: ").append(mServiceFriendlyNames); } + builder.append("CarrierId:" + mCarrierId); return builder.toString(); } @@ -662,6 +691,7 @@ public final class PasspointConfiguration implements Parcelable { 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 5e6c107dc187..4b7d205e7922 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.ITrafficStateCallback; 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 @@ public class BaseWifiService extends IWifiManager.Stub { 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 000000000000..949b4790d998 --- /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 8d0579bde92c..d2516a33fead 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -173,7 +173,7 @@ public class WifiManagerTest { 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 @@ public class WifiManagerTest { 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 @@ public class WifiManagerTest { mWifiManager.startLocalOnlyHotspot(callback, mHandler); verify(mWifiService) - .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString()); + .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), eq(null)); } /** @@ -362,7 +362,7 @@ public class WifiManagerTest { 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 class WifiManagerTest { 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 class WifiManagerTest { 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 @@ public class WifiManagerTest { 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 @@ public class WifiManagerTest { 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 @@ public class WifiManagerTest { 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 @@ public class WifiManagerTest { 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 @@ public class WifiManagerTest { 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 class WifiManagerTest { 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 class WifiManagerTest { 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 class WifiManagerTest { 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 class WifiManagerTest { 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 class WifiManagerTest { 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 class WifiManagerTest { 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 class WifiManagerTest { 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 @@ public class WifiManagerTest { 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. */ |