diff options
139 files changed, 3539 insertions, 1203 deletions
diff --git a/Android.bp b/Android.bp index 0848d2b7668e..8947cfae00bb 100644 --- a/Android.bp +++ b/Android.bp @@ -602,6 +602,7 @@ java_defaults { "telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl", "telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl", "telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl", + "telephony/java/com/android/internal/telephony/ISetOpportunisticDataCallback.aidl", "telephony/java/com/android/internal/telephony/ISms.aidl", "telephony/java/com/android/internal/telephony/ISub.aidl", "telephony/java/com/android/internal/telephony/IOns.aidl", @@ -741,10 +742,8 @@ java_defaults { static_libs: [ "apex_aidl_interface-java", - "networkstack-aidl-interfaces-java", "framework-protos", "game-driver-protos", - "mediaplayer2-protos", "android.hidl.base-V1.0-java", "android.hardware.cas-V1.1-java", "android.hardware.cas-V1.0-java", @@ -1188,52 +1187,6 @@ packages_to_document = [ "org/apache/http/params", ] -// The since flag (-since N.xml API_LEVEL) is used to add API Level information -// to the reference documentation. Must be in order of oldest to newest. -// -// Conscrypt (com.android.org.conscrypt) is an implementation detail and should -// not be referenced in the documentation. -framework_docs_args = "-android -manifest $(location core/res/AndroidManifest.xml) " + - "-hidePackage com.android.internal " + - "-hidePackage com.android.internal.util " + - "-hidePackage com.android.okhttp " + - "-hidePackage com.android.org.conscrypt " + - "-hidePackage com.android.server " + - "-since $(location 1/public/api/android.xml) 1 " + - "-since $(location 2/public/api/android.xml) 2 " + - "-since $(location 3/public/api/android.xml) 3 " + - "-since $(location 4/public/api/android.xml) 4 " + - "-since $(location 5/public/api/android.xml) 5 " + - "-since $(location 6/public/api/android.xml) 6 " + - "-since $(location 7/public/api/android.xml) 7 " + - "-since $(location 8/public/api/android.xml) 8 " + - "-since $(location 9/public/api/android.xml) 9 " + - "-since $(location 10/public/api/android.xml) 10 " + - "-since $(location 11/public/api/android.xml) 11 " + - "-since $(location 12/public/api/android.xml) 12 " + - "-since $(location 13/public/api/android.xml) 13 " + - "-since $(location 14/public/api/android.txt) 14 " + - "-since $(location 15/public/api/android.txt) 15 " + - "-since $(location 16/public/api/android.txt) 16 " + - "-since $(location 17/public/api/android.txt) 17 " + - "-since $(location 18/public/api/android.txt) 18 " + - "-since $(location 19/public/api/android.txt) 19 " + - "-since $(location 20/public/api/android.txt) 20 " + - "-since $(location 21/public/api/android.txt) 21 " + - "-since $(location 22/public/api/android.txt) 22 " + - "-since $(location 23/public/api/android.txt) 23 " + - "-since $(location 24/public/api/android.txt) 24 " + - "-since $(location 25/public/api/android.txt) 25 " + - "-since $(location 26/public/api/android.txt) 26 " + - "-since $(location 27/public/api/android.txt) 27 " + - "-since $(location 28/public/api/android.txt) 28 " + - "-since $(location api/current.txt) Q " + - "-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " + - "-overview $(location core/java/overview.html) " + - // Federate Support Library references against local API file. - "-federate SupportLib https://developer.android.com " + - "-federationapi SupportLib $(location current/support-api.txt) " - framework_docs_only_args = " -android -manifest $(location core/res/AndroidManifest.xml) " + "-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " + "-overview $(location core/java/overview.html) " + diff --git a/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java b/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java index ec46a75bd807..c506aec624d1 100644 --- a/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java +++ b/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java @@ -64,7 +64,7 @@ public class TextClassifierPerfTest { Context context = InstrumentationRegistry.getTargetContext(); TextClassificationManager textClassificationManager = context.getSystemService(TextClassificationManager.class); - mTextClassifier = textClassificationManager.getTextClassifier(); + mTextClassifier = textClassificationManager.getLocalTextClassifier(); } @Test diff --git a/api/current.txt b/api/current.txt index c5cd57378a9d..2b1036d6da36 100644 --- a/api/current.txt +++ b/api/current.txt @@ -80,7 +80,6 @@ package android { field public static final String FOREGROUND_SERVICE = "android.permission.FOREGROUND_SERVICE"; field public static final String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"; field public static final String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED"; - field public static final String GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY = "android.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY"; field public static final String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE"; field @Deprecated public static final String GET_TASKS = "android.permission.GET_TASKS"; field public static final String GLOBAL_SEARCH = "android.permission.GLOBAL_SEARCH"; @@ -131,6 +130,7 @@ package android { field public static final String REQUEST_DELETE_PACKAGES = "android.permission.REQUEST_DELETE_PACKAGES"; field public static final String REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"; field public static final String REQUEST_INSTALL_PACKAGES = "android.permission.REQUEST_INSTALL_PACKAGES"; + field public static final String REQUEST_SCREEN_LOCK_COMPLEXITY = "android.permission.REQUEST_SCREEN_LOCK_COMPLEXITY"; field @Deprecated public static final String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES"; field public static final String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE"; field public static final String SEND_SMS = "android.permission.SEND_SMS"; @@ -6646,7 +6646,7 @@ package android.app.admin { method @Nullable public CharSequence getOrganizationName(@NonNull android.content.ComponentName); method public java.util.List<android.telephony.data.ApnSetting> getOverrideApns(@NonNull android.content.ComponentName); method @NonNull public android.app.admin.DevicePolicyManager getParentProfileInstance(@NonNull android.content.ComponentName); - method @RequiresPermission(android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY) public int getPasswordComplexity(); + method @RequiresPermission(android.Manifest.permission.REQUEST_SCREEN_LOCK_COMPLEXITY) public int getPasswordComplexity(); method public long getPasswordExpiration(@Nullable android.content.ComponentName); method public long getPasswordExpirationTimeout(@Nullable android.content.ComponentName); method public int getPasswordHistoryLength(@Nullable android.content.ComponentName); @@ -6840,7 +6840,7 @@ package android.app.admin { field public static final String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION"; field public static final String EXTRA_DELEGATION_SCOPES = "android.app.extra.DELEGATION_SCOPES"; field public static final String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN"; - field @RequiresPermission(android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY) public static final String EXTRA_PASSWORD_COMPLEXITY = "android.app.extra.PASSWORD_COMPLEXITY"; + field @RequiresPermission(android.Manifest.permission.REQUEST_SCREEN_LOCK_COMPLEXITY) public static final String EXTRA_PASSWORD_COMPLEXITY = "android.app.extra.PASSWORD_COMPLEXITY"; field public static final String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE"; field public static final String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE = "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE"; field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME"; @@ -11482,7 +11482,6 @@ package android.content.pm { method public void setOriginatingUri(@Nullable android.net.Uri); method public void setReferrerUri(@Nullable android.net.Uri); method public void setSize(long); - method public void setStaged(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionParams> CREATOR; field public static final int MODE_FULL_INSTALL = 1; // 0x1 @@ -15409,28 +15408,38 @@ package android.graphics.drawable { method public float getGradientCenterY(); method public float getGradientRadius(); method public int getGradientType(); + method public int getInnerRadius(); + method public float getInnerRadiusRatio(); method public int getOpacity(); method public android.graphics.drawable.GradientDrawable.Orientation getOrientation(); method public int getShape(); + method public int getThickness(); + method public float getThicknessRatio(); method public boolean getUseLevel(); method public void setAlpha(int); method public void setColor(@ColorInt int); method public void setColor(@Nullable android.content.res.ColorStateList); method public void setColorFilter(@Nullable android.graphics.ColorFilter); method public void setColors(@ColorInt int[]); + method public void setColors(@ColorInt int[], @Nullable float[]); method public void setCornerRadii(@Nullable float[]); method public void setCornerRadius(float); method public void setDither(boolean); method public void setGradientCenter(float, float); method public void setGradientRadius(float); method public void setGradientType(int); + method public void setInnerRadius(int); + method public void setInnerRadiusRatio(float); method public void setOrientation(android.graphics.drawable.GradientDrawable.Orientation); + method public void setPadding(int, int, int, int); method public void setShape(int); method public void setSize(int, int); method public void setStroke(int, @ColorInt int); method public void setStroke(int, android.content.res.ColorStateList); method public void setStroke(int, @ColorInt int, float, float); method public void setStroke(int, android.content.res.ColorStateList, float, float); + method public void setThickness(int); + method public void setThicknessRatio(float); method public void setUseLevel(boolean); field public static final int LINE = 2; // 0x2 field public static final int LINEAR_GRADIENT = 0; // 0x0 @@ -22667,6 +22676,7 @@ package android.location { field public static final int MULTIPATH_INDICATOR_DETECTED = 1; // 0x1 field public static final int MULTIPATH_INDICATOR_NOT_DETECTED = 2; // 0x2 field public static final int MULTIPATH_INDICATOR_UNKNOWN = 0; // 0x0 + field public static final int STATE_2ND_CODE_LOCK = 65536; // 0x10000 field public static final int STATE_BDS_D2_BIT_SYNC = 256; // 0x100 field public static final int STATE_BDS_D2_SUBFRAME_SYNC = 512; // 0x200 field public static final int STATE_BIT_SYNC = 2; // 0x2 @@ -44373,6 +44383,7 @@ package android.telephony { method public int getChannelNumber(); method public String getMccString(); method public String getMncString(); + method public long getNci(); method public int getPci(); method public int getTac(); method public void writeToParcel(android.os.Parcel, int); @@ -45233,6 +45244,9 @@ package android.telephony { field public static final int PHONE_TYPE_GSM = 1; // 0x1 field public static final int PHONE_TYPE_NONE = 0; // 0x0 field public static final int PHONE_TYPE_SIP = 3; // 0x3 + field public static final int SET_OPPORTUNISTIC_SUB_INVALID_PARAMETER = 2; // 0x2 + field public static final int SET_OPPORTUNISTIC_SUB_SUCCESS = 0; // 0x0 + field public static final int SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED = 1; // 0x1 field public static final int SIM_STATE_ABSENT = 1; // 0x1 field public static final int SIM_STATE_CARD_IO_ERROR = 8; // 0x8 field public static final int SIM_STATE_CARD_RESTRICTED = 9; // 0x9 @@ -56691,6 +56705,8 @@ package android.widget { method public int getMaxValue(); method public int getMinValue(); method public int getSelectionDividerHeight(); + method @ColorInt public int getTextColor(); + method @FloatRange(from=0.0, fromInclusive=false) public float getTextSize(); method public int getValue(); method public boolean getWrapSelectorWheel(); method public void setDisplayedValues(String[]); @@ -56701,6 +56717,8 @@ package android.widget { method public void setOnScrollListener(android.widget.NumberPicker.OnScrollListener); method public void setOnValueChangedListener(android.widget.NumberPicker.OnValueChangeListener); method public void setSelectionDividerHeight(@IntRange(from=0) @Px int); + method public void setTextColor(@ColorInt int); + method public void setTextSize(@FloatRange(from=0.0, fromInclusive=false) float); method public void setValue(int); method public void setWrapSelectorWheel(boolean); } diff --git a/api/system-current.txt b/api/system-current.txt index f673827a8633..bca77b4ea397 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -641,7 +641,7 @@ package android.app.admin { method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String); method @Deprecated @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException; method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied(); - method public void setProfileOwnerCanAccessDeviceIdsForUser(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle); + method @RequiresPermission(value=android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, conditional=true) public void setProfileOwnerCanAccessDeviceIdsForUser(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle); field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED"; field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED"; field public static final String ACTION_PROVISION_FINALIZATION = "android.app.action.PROVISION_FINALIZATION"; @@ -1535,6 +1535,7 @@ package android.content.pm { method @RequiresPermission(android.Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS) public void setGrantedRuntimePermissions(String[]); method public void setInstallAsInstantApp(boolean); method public void setInstallAsVirtualPreload(); + method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setStaged(); } public class PackageItemInfo { @@ -5779,6 +5780,11 @@ package android.provider { field public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled"; } + public static interface DeviceConfig.Rollback { + field public static final String ENABLE_ROLLBACK_TIMEOUT = "enable_rollback_timeout"; + field public static final String NAMESPACE = "rollback"; + } + public static interface DeviceConfig.Runtime { field public static final String NAMESPACE = "runtime"; field public static final String USE_PRECOMPILED_LAYOUT = "view.precompiled_layout_enabled"; @@ -7759,6 +7765,7 @@ package android.telephony { method public void requestEmbeddedSubscriptionInfoListRefresh(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultDataSubId(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultSmsSubId(int); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPreferredDataSubscriptionId(int, boolean, @NonNull java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setSubscriptionEnabled(int, boolean); field public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI; field public static final int PROFILE_CLASS_DEFAULT = -1; // 0xffffffff diff --git a/cmds/bootanimation/bootanim.rc b/cmds/bootanimation/bootanim.rc index 1b3c32b20503..469c9646a4aa 100644 --- a/cmds/bootanimation/bootanim.rc +++ b/cmds/bootanimation/bootanim.rc @@ -2,7 +2,6 @@ service bootanim /system/bin/bootanimation class core animation user graphics group graphics audio - updatable disabled oneshot writepid /dev/stune/top-app/tasks diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp index 067b6eddf254..cca6d525ea16 100644 --- a/cmds/statsd/benchmark/metric_util.cpp +++ b/cmds/statsd/benchmark/metric_util.cpp @@ -367,7 +367,8 @@ sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const Stat sp<AlarmMonitor> periodicAlarmMonitor; sp<StatsLogProcessor> processor = new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseSec * NS_PER_SEC, [](const ConfigKey&) { return true; }); + timeBaseSec * NS_PER_SEC, [](const ConfigKey&) { return true; }, + [](const int&, const vector<int64_t>&) { return true; }); processor->OnConfigUpdated(timeBaseSec * NS_PER_SEC, key, config); return processor; } @@ -393,4 +394,4 @@ int64_t StringToId(const string& str) { } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index dd18bd4cc8ad..653ef2ec9869 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -88,12 +88,15 @@ StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap, const sp<AlarmMonitor>& anomalyAlarmMonitor, const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs, - const std::function<bool(const ConfigKey&)>& sendBroadcast) + const std::function<bool(const ConfigKey&)>& sendBroadcast, + const std::function<bool( + const int&, const vector<int64_t>&)>& activateBroadcast) : mUidMap(uidMap), mPullerManager(pullerManager), mAnomalyAlarmMonitor(anomalyAlarmMonitor), mPeriodicAlarmMonitor(periodicAlarmMonitor), mSendBroadcast(sendBroadcast), + mSendActivationBroadcast(activateBroadcast), mTimeBaseNs(timeBaseNs), mLargestTimestampSeen(0), mLastTimestampSeen(0) { @@ -223,11 +226,73 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { mapIsolatedUidToHostUidIfNecessaryLocked(event); } + std::unordered_set<int> uidsWithActiveConfigsChanged; + std::unordered_map<int, std::vector<int64_t>> activeConfigsPerUid; // pass the event to metrics managers. for (auto& pair : mMetricsManagers) { + int uid = pair.first.GetUid(); + int64_t configId = pair.first.GetId(); + bool isPrevActive = pair.second->isActive(); pair.second->onLogEvent(*event); + bool isCurActive = pair.second->isActive(); + // Map all active configs by uid. + if (isCurActive) { + auto activeConfigs = activeConfigsPerUid.find(uid); + if (activeConfigs != activeConfigsPerUid.end()) { + activeConfigs->second.push_back(configId); + } else { + vector<int64_t> newActiveConfigs; + newActiveConfigs.push_back(configId); + activeConfigsPerUid[uid] = newActiveConfigs; + } + } + // The activation state of this config changed. + if (isPrevActive != isCurActive) { + VLOG("Active status changed for uid %d", uid); + uidsWithActiveConfigsChanged.insert(uid); + StatsdStats::getInstance().noteActiveStatusChanged(pair.first, isCurActive); + } flushIfNecessaryLocked(event->GetElapsedTimestampNs(), pair.first, *(pair.second)); } + + 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) { + VLOG("StatsD would've sent an activation broadcast but the rate limit stopped us."); + return; + } + } + auto activeConfigs = activeConfigsPerUid.find(uid); + if (activeConfigs != activeConfigsPerUid.end()) { + if (mSendActivationBroadcast(uid, activeConfigs->second)) { + VLOG("StatsD sent activation notice for uid %d", uid); + mLastActivationBroadcastTimes[uid] = currentTimestampNs; + } + } else { + std::vector<int64_t> emptyActiveConfigs; + if (mSendActivationBroadcast(uid, emptyActiveConfigs)) { + VLOG("StatsD sent EMPTY activation notice for uid %d", uid); + mLastActivationBroadcastTimes[uid] = currentTimestampNs; + } + } + } +} + +void StatsLogProcessor::GetActiveConfigs(const int uid, vector<int64_t>& outActiveConfigs) { + std::lock_guard<std::mutex> lock(mMetricsMutex); + GetActiveConfigsLocked(uid, outActiveConfigs); +} + +void StatsLogProcessor::GetActiveConfigsLocked(const int uid, vector<int64_t>& outActiveConfigs) { + outActiveConfigs.clear(); + for (auto& pair : mMetricsManagers) { + if (pair.first.GetUid() == uid && pair.second->isActive()) { + outActiveConfigs.push_back(pair.first.GetId()); + } + } } void StatsLogProcessor::OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key, @@ -444,6 +509,18 @@ void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) { mLastBroadcastTimes.erase(key); + int uid = key.GetUid(); + bool lastConfigForUid = true; + for (auto it : mMetricsManagers) { + if (it.first.GetUid() == uid) { + lastConfigForUid = false; + break; + } + } + if (lastConfigForUid) { + mLastActivationBroadcastTimes.erase(uid); + } + if (mMetricsManagers.empty()) { mPullerManager->ForceClearPullerCache(); } diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index caf1a713986d..ea9c6e704017 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -49,7 +49,9 @@ public: const sp<AlarmMonitor>& anomalyAlarmMonitor, const sp<AlarmMonitor>& subscriberTriggerAlarmMonitor, const int64_t timeBaseNs, - const std::function<bool(const ConfigKey&)>& sendBroadcast); + const std::function<bool(const ConfigKey&)>& sendBroadcast, + const std::function<bool(const int&, + const vector<int64_t>&)>& sendActivationBroadcast); virtual ~StatsLogProcessor(); void OnLogEvent(LogEvent* event); @@ -60,6 +62,8 @@ public: size_t GetMetricsSize(const ConfigKey& key) const; + void GetActiveConfigs(const int uid, vector<int64_t>& outActiveConfigs); + void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs, const bool include_current_partial_bucket, const bool erase_data, const DumpReportReason dumpReportReason, vector<uint8_t>* outData); @@ -125,6 +129,9 @@ private: std::unordered_map<ConfigKey, long> mLastBroadcastTimes; + // Last time we sent a broadcast to this uid that the active configs had changed. + std::unordered_map<int, long> mLastActivationBroadcastTimes; + // Tracks when we last checked the bytes consumed for each config key. std::unordered_map<ConfigKey, long> mLastByteSizeTimes; @@ -144,6 +151,8 @@ private: void OnConfigUpdatedLocked( const int64_t currentTimestampNs, const ConfigKey& key, const StatsdConfig& config); + void GetActiveConfigsLocked(const int uid, vector<int64_t>& outActiveConfigs); + void WriteDataToDiskLocked(const DumpReportReason dumpReportReason); void WriteDataToDiskLocked(const ConfigKey& key, const int64_t timestampNs, const DumpReportReason dumpReportReason); @@ -174,6 +183,10 @@ private: // to retrieve the stored data. std::function<bool(const ConfigKey& key)> mSendBroadcast; + // Function used to send a broadcast so that receiver can be notified of which configs + // are currently active. + std::function<bool(const int& uid, const vector<int64_t>& configIds)> mSendActivationBroadcast; + const int64_t mTimeBaseNs; // Largest timestamp of the events that we have processed. diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index bd21a955729d..b478fed49a54 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -176,6 +176,21 @@ StatsService::StatsService(const sp<Looper>& handlerLooper) sc->sendDataBroadcast(receiver, mProcessor->getLastReportTimeNs(key)); return true; } + }, + [this](const int& uid, const vector<int64_t>& activeConfigs) { + auto receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid); + sp<IStatsCompanionService> sc = getStatsCompanionService(); + if (sc == nullptr) { + VLOG("Could not access statsCompanion"); + return false; + } else if (receiver == nullptr) { + VLOG("Could not find receiver for uid %d", uid); + return false; + } else { + sc->sendActiveConfigsChangedBroadcast(receiver, activeConfigs); + VLOG("StatsService::active configs broadcast succeeded for uid %d" , uid); + return true; + } }); mConfigManager->AddListener(mProcessor); @@ -357,6 +372,9 @@ status_t StatsService::command(int in, int out, int err, Vector<String8>& args, if (!args[0].compare(String8("print-logs"))) { return cmd_print_logs(out, args); } + if (!args[0].compare(String8("send-active-configs"))) { + return cmd_trigger_active_config_broadcast(out, args); + } if (!args[0].compare(String8("data-subscribe"))) { if (mShellSubscriber == nullptr) { mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager); @@ -449,6 +467,19 @@ void StatsService::print_cmd_help(int out) { dprintf(out, " NAME The name of the configuration\n"); dprintf(out, "\n"); dprintf(out, "\n"); + dprintf(out, + "usage: adb shell cmd stats send-active-configs [--uid=UID] [--configs] " + "[NAME1] [NAME2] [NAME3..]\n"); + dprintf(out, " Send a broadcast that informs the subscriber of the current active configs.\n"); + dprintf(out, " --uid=UID The uid of the configurations. It is only possible to pass\n"); + dprintf(out, " the UID parameter on eng builds. If UID is omitted the\n"); + dprintf(out, " calling uid is used.\n"); + dprintf(out, " --configs Send the list of configs in the name list instead of\n"); + dprintf(out, " the currently active configs\n"); + dprintf(out, " NAME LIST List of configuration names to be included in the broadcast.\n"); + + dprintf(out, "\n"); + dprintf(out, "\n"); dprintf(out, "usage: adb shell cmd stats print-stats\n"); dprintf(out, " Prints some basic stats.\n"); dprintf(out, " --proto Print proto binary instead of string format.\n"); @@ -499,6 +530,59 @@ status_t StatsService::cmd_trigger_broadcast(int out, Vector<String8>& args) { return NO_ERROR; } +status_t StatsService::cmd_trigger_active_config_broadcast(int out, Vector<String8>& args) { + const int argCount = args.size(); + int uid; + vector<int64_t> configIds; + if (argCount == 1) { + // Automatically pick the uid and send a broadcast that has no active configs. + uid = IPCThreadState::self()->getCallingUid(); + mProcessor->GetActiveConfigs(uid, configIds); + } else { + int curArg = 1; + if(args[curArg].find("--uid=") == 0) { + string uidArgStr(args[curArg].c_str()); + string uidStr = uidArgStr.substr(6); + if (!getUidFromString(uidStr.c_str(), uid)) { + dprintf(out, "Invalid UID. Note that the config can only be set for " + "other UIDs on eng or userdebug builds.\n"); + return UNKNOWN_ERROR; + } + curArg++; + } else { + uid = IPCThreadState::self()->getCallingUid(); + } + if (curArg == argCount || args[curArg] != "--configs") { + VLOG("Reached end of args, or specify configs not set. Sending actual active configs,"); + mProcessor->GetActiveConfigs(uid, configIds); + } else { + // Flag specified, use the given list of configs. + curArg++; + for (int i = curArg; i < argCount; i++) { + char* endp; + int64_t configID = strtoll(args[i].c_str(), &endp, 10); + if (endp == args[i].c_str() || *endp != '\0') { + dprintf(out, "Error parsing config ID.\n"); + return UNKNOWN_ERROR; + } + VLOG("Adding config id %ld", static_cast<long>(configID)); + configIds.push_back(configID); + } + } + } + auto receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid); + sp<IStatsCompanionService> sc = getStatsCompanionService(); + if (sc == nullptr) { + VLOG("Could not access statsCompanion"); + } else if (receiver == nullptr) { + VLOG("Could not find receiver for uid %d", uid); + } else { + sc->sendActiveConfigsChangedBroadcast(receiver, configIds); + VLOG("StatsService::trigger active configs changed broadcast succeeded for uid %d" , uid); + } + return NO_ERROR; +} + status_t StatsService::cmd_config(int in, int out, int err, Vector<String8>& args) { const int argCount = args.size(); if (argCount >= 2) { @@ -762,7 +846,10 @@ status_t StatsService::cmd_print_logs(int out, const Vector<String8>& args) { } bool StatsService::getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid) { - const char* s = args[uidArgIndex].c_str(); + return getUidFromString(args[uidArgIndex].c_str(), uid); +} + +bool StatsService::getUidFromString(const char* s, int32_t& uid) { if (*s == '\0') { return false; } @@ -998,8 +1085,13 @@ Status StatsService::setActiveConfigsChangedOperation(const sp<android::IBinder> ENFORCE_DUMP_AND_USAGE_STATS(packageName); IPCThreadState* ipc = IPCThreadState::self(); - mConfigManager->SetActiveConfigsChangedReceiver(ipc->getCallingUid(), intentSender); - //TODO: Return the list of configs that are already active + int uid = ipc->getCallingUid(); + mConfigManager->SetActiveConfigsChangedReceiver(uid, intentSender); + if (output != nullptr) { + mProcessor->GetActiveConfigs(uid, *output); + } else { + ALOGW("StatsService::setActiveConfigsChanged output was nullptr"); + } return Status::ok(); } diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 941ed462b303..7f10d74ec7d6 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -275,6 +275,12 @@ private: */ status_t cmd_trigger_broadcast(int outFd, Vector<String8>& args); + + /** + * Trigger an active configs changed broadcast. + */ + status_t cmd_trigger_active_config_broadcast(int outFd, Vector<String8>& args); + /** * Handle the config sub-command. */ @@ -341,6 +347,15 @@ private: bool getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid); /** + * Writes the value of uidSting into uid. + * Returns whether the uid is reasonable (type uid_t) and whether + * 1. it is equal to the calling uid, or + * 2. the device is mEngBuild, or + * 3. the caller is AID_ROOT and the uid is AID_SHELL (i.e. ROOT can impersonate SHELL). + */ + bool getUidFromString(const char* uidString, int32_t& uid); + + /** * Adds a configuration after checking permissions and obtaining UID from binder call. */ bool addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config); diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index da7e4daaf22b..29f67c7e6f9b 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -31,6 +31,7 @@ import "frameworks/base/core/proto/android/bluetooth/hfp/enums.proto"; import "frameworks/base/core/proto/android/bluetooth/smp/enums.proto"; import "frameworks/base/core/proto/android/debug/enums.proto"; import "frameworks/base/core/proto/android/hardware/biometrics/enums.proto"; +import "frameworks/base/core/proto/android/hardware/sensor/assist/enums.proto"; import "frameworks/base/core/proto/android/net/networkcapabilities.proto"; import "frameworks/base/core/proto/android/os/enums.proto"; import "frameworks/base/core/proto/android/server/connectivity/data_stall_event.proto"; @@ -241,6 +242,9 @@ message Atom { BluetoothSocketConnectionStateChanged bluetooth_socket_connection_state_changed = 171; DeviceIdentifierAccessDenied device_identifier_access_denied = 172; BubbleDeveloperErrorReported bubble_developer_error_reported = 173; + AssistGestureStageReported assist_gesture_stage_reported = 174; + AssistGestureFeedbackReported assist_gesture_feedback_reported = 175; + AssistGestureProgressReported assist_gesture_progress_reported = 176; } // Pulled events will start at field 10000. @@ -5522,3 +5526,35 @@ message TrainInfo { optional TrainExperimentIds train_experiment_id = 2; } + +/** + * Logs the gesture stage changed event. + * + * Logged from: + * frameworks/base/packages/SystemUI/ + */ +message AssistGestureStageReported { + optional android.hardware.sensor.assist.AssistGestureStageEnum gesture_stage = 1; +} + +/** + * Logs the feedback type. + * + * Logged from: + * frameworks/base/packages/SystemUI/ + */ +message AssistGestureFeedbackReported { + // Whether or not the gesture was used. + optional android.hardware.sensor.assist.AssistGestureFeedbackEnum feedback_type = 1; +} + +/** + * Logs the progress. + * + * Logged from: + * frameworks/base/packages/SystemUI/ + */ +message AssistGestureProgressReported { + // [0,100] progress for the assist gesture. + optional int32 progress = 1; +} diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp index aa22333ab26c..fc949b494194 100644 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -144,10 +144,20 @@ void ConfigManager::RemoveConfig(const ConfigKey& key) { { lock_guard <mutex> lock(mMutex); - auto uidIt = mConfigs.find(key.GetUid()); + auto uid = key.GetUid(); + auto uidIt = mConfigs.find(uid); if (uidIt != mConfigs.end() && uidIt->second.find(key) != uidIt->second.end()) { // Remove from map uidIt->second.erase(key); + + // No more configs for this uid, lets remove the active configs callback. + if (uidIt->second.empty()) { + auto itActiveConfigsChangedReceiver = mActiveConfigsChangedReceivers.find(uid); + if (itActiveConfigsChangedReceiver != mActiveConfigsChangedReceivers.end()) { + mActiveConfigsChangedReceivers.erase(itActiveConfigsChangedReceiver); + } + } + for (const sp<ConfigListener>& listener : mListeners) { broadcastList.push_back(listener); } diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index 40329b7c86ab..b433c41518cc 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -82,6 +82,8 @@ const int FIELD_ID_CONFIG_STATS_METRIC_STATS = 15; const int FIELD_ID_CONFIG_STATS_ALERT_STATS = 16; const int FIELD_ID_CONFIG_STATS_METRIC_DIMENSION_IN_CONDITION_STATS = 17; const int FIELD_ID_CONFIG_STATS_ANNOTATION = 18; +const int FIELD_ID_CONFIG_STATS_ACTIVATION = 22; +const int FIELD_ID_CONFIG_STATS_DEACTIVATION = 23; const int FIELD_ID_CONFIG_STATS_ANNOTATION_INT64 = 1; const int FIELD_ID_CONFIG_STATS_ANNOTATION_INT32 = 2; @@ -206,6 +208,25 @@ void StatsdStats::noteBroadcastSent(const ConfigKey& key, int32_t timeSec) { it->second->broadcast_sent_time_sec.push_back(timeSec); } +void StatsdStats::noteActiveStatusChanged(const ConfigKey& key, bool activated) { + noteActiveStatusChanged(key, activated, getWallClockSec()); +} + +void StatsdStats::noteActiveStatusChanged(const ConfigKey& key, bool activated, int32_t timeSec) { + lock_guard<std::mutex> lock(mLock); + auto it = mConfigStats.find(key); + if (it == mConfigStats.end()) { + ALOGE("Config key %s not found!", key.ToString().c_str()); + return; + } + auto& vec = activated ? it->second->activation_time_sec + : it->second->deactivation_time_sec; + if (vec.size() == kMaxTimestampCount) { + vec.pop_front(); + } + vec.push_back(timeSec); +} + void StatsdStats::noteDataDropped(const ConfigKey& key, const size_t totalBytes) { noteDataDropped(key, totalBytes, getWallClockSec()); } @@ -501,6 +522,8 @@ void StatsdStats::resetInternalLocked() { mLogLossStats.clear(); for (auto& config : mConfigStats) { config.second->broadcast_sent_time_sec.clear(); + config.second->activation_time_sec.clear(); + config.second->deactivation_time_sec.clear(); config.second->data_drop_time_sec.clear(); config.second->data_drop_bytes.clear(); config.second->dump_report_stats.clear(); @@ -558,6 +581,14 @@ void StatsdStats::dumpStats(int out) const { dprintf(out, "\tbroadcast time: %d\n", broadcastTime); } + for (const int& activationTime : configStats->activation_time_sec) { + dprintf(out, "\tactivation time: %d\n", activationTime); + } + + for (const int& deactivationTime : configStats->deactivation_time_sec) { + dprintf(out, "\tdeactivation time: %d\n", deactivationTime); + } + auto dropTimePtr = configStats->data_drop_time_sec.begin(); auto dropBytesPtr = configStats->data_drop_bytes.begin(); for (int i = 0; i < (int)configStats->data_drop_time_sec.size(); @@ -586,6 +617,14 @@ void StatsdStats::dumpStats(int out) const { (long long)broadcastTime); } + for (const int& activationTime : configStats->activation_time_sec) { + dprintf(out, "\tactivation time: %d\n", activationTime); + } + + for (const int& deactivationTime : configStats->deactivation_time_sec) { + dprintf(out, "\tdeactivation time: %d\n", deactivationTime); + } + auto dropTimePtr = configStats->data_drop_time_sec.begin(); auto dropBytesPtr = configStats->data_drop_bytes.begin(); for (int i = 0; i < (int)configStats->data_drop_time_sec.size(); @@ -696,6 +735,16 @@ void addConfigStatsToProto(const ConfigStats& configStats, ProtoOutputStream* pr broadcast); } + for (const auto& activation : configStats.activation_time_sec) { + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_ACTIVATION | FIELD_COUNT_REPEATED, + activation); + } + + for (const auto& deactivation : configStats.deactivation_time_sec) { + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DEACTIVATION | FIELD_COUNT_REPEATED, + deactivation); + } + for (const auto& drop_time : configStats.data_drop_time_sec) { proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DATA_DROP_TIME | FIELD_COUNT_REPEATED, drop_time); diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index 65e8a324d7d3..5275c8f34fd0 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -42,6 +42,13 @@ struct ConfigStats { bool is_valid; std::list<int32_t> broadcast_sent_time_sec; + + // Times at which this config is activated. + std::list<int32_t> activation_time_sec; + + // Times at which this config is deactivated. + std::list<int32_t> deactivation_time_sec; + std::list<int32_t> data_drop_time_sec; // Number of bytes dropped at corresponding time. std::list<int64_t> data_drop_bytes; @@ -132,6 +139,9 @@ public: /* Min period between two checks of byte size per config key in nanoseconds. */ static const int64_t kMinByteSizeCheckPeriodNs = 60 * NS_PER_SEC; + /* Minimum period between two activation broadcasts in nanoseconds. */ + static const int64_t kMinActivationBroadcastPeriodNs = 10 * NS_PER_SEC; + // Maximum age (30 days) that files on disk can exist in seconds. static const int kMaxAgeSecond = 60 * 60 * 24 * 30; @@ -175,6 +185,13 @@ public: void noteBroadcastSent(const ConfigKey& key); /** + * Report that a config has become activated or deactivated. + * This can be different from whether or not a broadcast is sent if the + * guardrail prevented the broadcast from being sent. + */ + void noteActiveStatusChanged(const ConfigKey& key, bool activate); + + /** * Report a config's metrics data has been dropped. */ void noteDataDropped(const ConfigKey& key, const size_t totalBytes); @@ -507,6 +524,8 @@ private: void noteBroadcastSent(const ConfigKey& key, int32_t timeSec); + void noteActiveStatusChanged(const ConfigKey& key, bool activate, int32_t timeSec); + void addToIceBoxLocked(std::shared_ptr<ConfigStats>& stats); /** diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index 495138ee9b77..11075685b7fa 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -94,7 +94,7 @@ void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) { void MetricProducer::addActivation(int activationTrackerIndex, int64_t ttl_seconds) { std::lock_guard<std::mutex> lock(mMutex); // When a metric producer does not depend on any activation, its mIsActive is true. - // Therefor, if this is the 1st activation, mIsActive will turn to false. Otherwise it does not + // Therefore, if this is the 1st activation, mIsActive will turn to false. Otherwise it does not // change. if (mEventActivationMap.empty()) { mIsActive = false; diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 6ed6ab500597..4851a8d40baa 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -118,6 +118,16 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, ALOGE("This config has too many alerts! Reject!"); mConfigValid = false; } + + mIsAlwaysActive = (mMetricIndexesWithActivation.size() != mAllMetricProducers.size()) || + (mAllMetricProducers.size() == 0); + bool isActive = mIsAlwaysActive; + for (int metric : mMetricIndexesWithActivation) { + isActive |= mAllMetricProducers[metric]->isActive(); + } + mIsActive = isActive; + VLOG("mIsActive is initialized to %d", mIsActive) + // no matter whether this config is valid, log it in the stats. StatsdStats::getInstance().noteConfigReceived( key, mAllMetricProducers.size(), mAllConditionTrackers.size(), mAllAtomMatchers.size(), @@ -332,12 +342,14 @@ void MetricsManager::onLogEvent(const LogEvent& event) { int tagId = event.GetTagId(); int64_t eventTimeNs = event.GetElapsedTimestampNs(); - bool isActive = false; + bool isActive = mIsAlwaysActive; for (int metric : mMetricIndexesWithActivation) { mAllMetricProducers[metric]->flushIfExpire(eventTimeNs); isActive |= mAllMetricProducers[metric]->isActive(); } + mIsActive = isActive; + if (mTagIds.find(tagId) == mTagIds.end()) { // not interesting... return; diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index cb1cefbf2063..eab1f762b390 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -128,6 +128,8 @@ public: // Does not change the state. virtual size_t byteSize(); + // Returns whether or not this config is active. + // The config is active if any metric in the config is active. inline bool isActive() const { return mIsActive; } @@ -241,9 +243,12 @@ private: // The metrics that don't need to be uploaded or even reported. std::set<int64_t> mNoReportMetricIds; - // Any metric active means the config is active. + // The config is active if any metric in the config is active. bool mIsActive; + // The config is always active if any metric in the config does not have an activation signal. + bool mIsAlwaysActive; + FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions); FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks); FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid); diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index 863261ae1626..f7428a50b3da 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -367,6 +367,8 @@ message StatsdStatsReport { optional int32 field_int32 = 2; } repeated Annotation annotation = 18; + repeated int32 activation_time_sec = 22; + repeated int32 deactivation_time_sec = 23; } repeated ConfigStats config_stats = 3; diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index 60df165f102c..5f3aae3ab93a 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -65,7 +65,8 @@ TEST(StatsLogProcessorTest, TestRateLimitByteSize) { sp<AlarmMonitor> periodicAlarmMonitor; // Construct the processor with a dummy sendBroadcast function that does nothing. StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, 0, - [](const ConfigKey& key) { return true; }); + [](const ConfigKey& key) { return true; }, + [](const int&, const vector<int64_t>&) {return true;}); MockMetricsManager mockMetricsManager; @@ -88,7 +89,8 @@ TEST(StatsLogProcessorTest, TestRateLimitBroadcast) { [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true; - }); + }, + [](const int&, const vector<int64_t>&) {return true;}); MockMetricsManager mockMetricsManager; @@ -118,7 +120,8 @@ TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) { [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true; - }); + }, + [](const int&, const vector<int64_t>&) {return true;}); MockMetricsManager mockMetricsManager; @@ -162,7 +165,8 @@ TEST(StatsLogProcessorTest, TestUidMapHasSnapshot) { [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true; - }); + }, + [](const int&, const vector<int64_t>&) {return true;}); ConfigKey key(3, 4); StatsdConfig config = MakeConfig(true); p.OnConfigUpdated(0, key, config); @@ -192,7 +196,8 @@ TEST(StatsLogProcessorTest, TestEmptyConfigHasNoUidMap) { [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true; - }); + }, + [](const int&, const vector<int64_t>&) {return true;}); ConfigKey key(3, 4); StatsdConfig config = MakeConfig(false); p.OnConfigUpdated(0, key, config); @@ -218,7 +223,8 @@ TEST(StatsLogProcessorTest, TestReportIncludesSubConfig) { [&broadcastCount](const ConfigKey& key) { broadcastCount++; return true; - }); + }, + [](const int&, const vector<int64_t>&) {return true;}); ConfigKey key(3, 4); StatsdConfig config; auto annotation = config.add_annotation(); diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp index f0d9cf188661..c04a40cfebd9 100644 --- a/cmds/statsd/tests/UidMap_test.cpp +++ b/cmds/statsd/tests/UidMap_test.cpp @@ -45,7 +45,8 @@ TEST(UidMapTest, TestIsolatedUID) { sp<AlarmMonitor> subscriberAlarmMonitor; // Construct the processor with a dummy sendBroadcast function that does nothing. StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, - [](const ConfigKey& key) { return true; }); + [](const ConfigKey& key) { return true; }, + [](const int&, const vector<int64_t>&) {return true;}); LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1); addEvent.write(100); // parent UID addEvent.write(101); // isolated UID diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp index 44a88f049443..1ff7982e1232 100644 --- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp +++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp @@ -133,6 +133,13 @@ TEST(StatsdStatsTest, TestSubStats) { stats.noteMetricsReportSent(key, 0); stats.noteMetricsReportSent(key, 0); + // activation_time_sec -> 2 + stats.noteActiveStatusChanged(key, true); + stats.noteActiveStatusChanged(key, true); + + // deactivation_time_sec -> 1 + stats.noteActiveStatusChanged(key, false); + vector<uint8_t> output; stats.dumpStats(&output, true); // Dump and reset stats StatsdStatsReport report; @@ -146,6 +153,8 @@ TEST(StatsdStatsTest, TestSubStats) { EXPECT_EQ(123, configReport.data_drop_bytes(0)); EXPECT_EQ(3, configReport.dump_report_time_sec_size()); EXPECT_EQ(3, configReport.dump_report_data_size_size()); + EXPECT_EQ(2, configReport.activation_time_sec_size()); + EXPECT_EQ(1, configReport.deactivation_time_sec_size()); EXPECT_EQ(1, configReport.annotation_size()); EXPECT_EQ(123, configReport.annotation(0).field_int64()); EXPECT_EQ(456, configReport.annotation(0).field_int32()); @@ -344,6 +353,8 @@ TEST(StatsdStatsTest, TestTimestampThreshold) { stats.noteDataDropped(key, timestamps[i]); stats.noteBroadcastSent(key, timestamps[i]); stats.noteMetricsReportSent(key, 0, timestamps[i]); + stats.noteActiveStatusChanged(key, true, timestamps[i]); + stats.noteActiveStatusChanged(key, false, timestamps[i]); } int32_t newTimestamp = 10000; @@ -352,6 +363,8 @@ TEST(StatsdStatsTest, TestTimestampThreshold) { stats.noteDataDropped(key, 123, 10000); stats.noteBroadcastSent(key, 10000); stats.noteMetricsReportSent(key, 0, 10000); + stats.noteActiveStatusChanged(key, true, 10000); + stats.noteActiveStatusChanged(key, false, 10000); EXPECT_TRUE(stats.mConfigStats.find(key) != stats.mConfigStats.end()); const auto& configStats = stats.mConfigStats[key]; @@ -360,17 +373,23 @@ TEST(StatsdStatsTest, TestTimestampThreshold) { EXPECT_EQ(maxCount, configStats->broadcast_sent_time_sec.size()); EXPECT_EQ(maxCount, configStats->data_drop_time_sec.size()); EXPECT_EQ(maxCount, configStats->dump_report_stats.size()); + EXPECT_EQ(maxCount, configStats->activation_time_sec.size()); + EXPECT_EQ(maxCount, configStats->deactivation_time_sec.size()); // the oldest timestamp is the second timestamp in history EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front()); - EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front()); - EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front()); + EXPECT_EQ(1, configStats->data_drop_bytes.front()); + EXPECT_EQ(1, configStats->dump_report_stats.front().first); + EXPECT_EQ(1, configStats->activation_time_sec.front()); + EXPECT_EQ(1, configStats->deactivation_time_sec.front()); // the last timestamp is the newest timestamp. EXPECT_EQ(newTimestamp, configStats->broadcast_sent_time_sec.back()); EXPECT_EQ(newTimestamp, configStats->data_drop_time_sec.back()); EXPECT_EQ(123, configStats->data_drop_bytes.back()); EXPECT_EQ(newTimestamp, configStats->dump_report_stats.back().first); + EXPECT_EQ(newTimestamp, configStats->activation_time_sec.back()); + EXPECT_EQ(newTimestamp, configStats->deactivation_time_sec.back()); } TEST(StatsdStatsTest, TestSystemServerCrash) { diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index b8b1a1db2c12..2c4f3c7692c9 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -461,7 +461,8 @@ sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const in [](const sp<IStatsCompanionService>&){}); sp<StatsLogProcessor> processor = new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, - timeBaseNs, [](const ConfigKey&) { return true; }); + timeBaseNs, [](const ConfigKey&) { return true; }, + [](const int&, const vector<int64_t>&) {return true;}); processor->OnConfigUpdated(currentTimeNs, key, config); return processor; } @@ -826,4 +827,4 @@ void backfillStartEndTimestamp(ConfigMetricsReportList *config_report_list) { } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt index 010e4478459d..b72ce8977128 100644 --- a/config/hiddenapi-greylist.txt +++ b/config/hiddenapi-greylist.txt @@ -2439,7 +2439,6 @@ Lcom/android/internal/telephony/cat/CatService;->isStkAppInstalled()Z Lcom/android/internal/telephony/cat/CatService;->mCmdIf:Lcom/android/internal/telephony/CommandsInterface; Lcom/android/internal/telephony/cat/CatService;->mContext:Landroid/content/Context; Lcom/android/internal/telephony/cat/CatService;->mCurrntCmd:Lcom/android/internal/telephony/cat/CatCmdMessage; -Lcom/android/internal/telephony/cat/CatService;->mHandlerThread:Landroid/os/HandlerThread; Lcom/android/internal/telephony/cat/CatService;->mMenuCmd:Lcom/android/internal/telephony/cat/CatCmdMessage; Lcom/android/internal/telephony/cat/CatService;->mMsgDecoder:Lcom/android/internal/telephony/cat/RilMessageDecoder; Lcom/android/internal/telephony/cat/CatService;->mSlotId:I diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 0eadd1dcd903..5f778da44a1c 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -65,6 +65,7 @@ import android.net.Uri; import android.os.BadParcelableException; import android.os.Build; import android.os.Bundle; +import android.os.GraphicsEnvironment; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -7708,6 +7709,8 @@ public class Activity extends ContextThemeWrapper } } + GraphicsEnvironment.getInstance().showAngleInUseDialogBox(this); + mActivityTransitionState.enterReady(this); dispatchActivityPostStarted(); } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index db3b720642aa..ca3c72627e3b 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -2019,8 +2019,9 @@ public class ActivityManager { return getTaskService().isActivityStartAllowedOnDisplay(displayId, intent, intent.resolveTypeIfNeeded(context.getContentResolver()), context.getUserId()); } catch (RemoteException e) { - throw new RuntimeException("Failure from system", e); + e.rethrowFromSystemServer(); } + return false; } /** diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index d46dbedfe8c2..5c4c0052cfbb 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -870,8 +870,9 @@ public final class LoadedApk { } } - // /vendor/lib, /odm/lib and /product/lib are added to the native lib search - // paths of the classloader. Note that this is done AFTER the classloader is + // /aepx/com.android.runtime/lib, /vendor/lib, /odm/lib and /product/lib + // are added to the native lib search paths of the classloader. + // Note that this is done AFTER the classloader is // created by ApplicationLoaders.getDefault().getClassLoader(...). The // reason is because if we have added the paths when creating the classloader // above, the paths are also added to the search path of the linker namespace @@ -888,8 +889,11 @@ public final class LoadedApk { // System.loadLibrary(). In order to prevent the problem, we explicitly // add the paths only to the classloader, and not to the native loader // (linker namespace). - List<String> extraLibPaths = new ArrayList<>(3); + List<String> extraLibPaths = new ArrayList<>(4); String abiSuffix = VMRuntime.getRuntime().is64Bit() ? "64" : ""; + if (!defaultSearchPaths.contains("/apex/com.android.runtime/lib")) { + extraLibPaths.add("/apex/com.android.runtime/lib" + abiSuffix); + } if (!defaultSearchPaths.contains("/vendor/lib")) { extraLibPaths.add("/vendor/lib" + abiSuffix); } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 886115103ccf..2f70c9d24936 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1376,7 +1376,7 @@ public class DevicePolicyManager { * complexity, and use this activity with extra {@link #EXTRA_PASSWORD_COMPLEXITY} to suggest * to users how complex the app wants the new screen lock to be. Note that both {@link * #getPasswordComplexity()} and the extra {@link #EXTRA_PASSWORD_COMPLEXITY} require the - * calling app to have the permission {@link permission#GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY}. + * calling app to have the permission {@link permission#REQUEST_SCREEN_LOCK_COMPLEXITY}. * * <p>If the intent is launched from within a managed profile with a profile * owner built against {@link android.os.Build.VERSION_CODES#M} or before, @@ -1404,7 +1404,7 @@ public class DevicePolicyManager { * * <p>If an invalid value is used, it will be treated as {@link #PASSWORD_COMPLEXITY_NONE}. */ - @RequiresPermission(android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY) + @RequiresPermission(android.Manifest.permission.REQUEST_SCREEN_LOCK_COMPLEXITY) public static final String EXTRA_PASSWORD_COMPLEXITY = "android.app.extra.PASSWORD_COMPLEXITY"; @@ -3346,10 +3346,10 @@ public class DevicePolicyManager { * * @throws IllegalStateException if the user is not unlocked. * @throws SecurityException if the calling application does not have the permission - * {@link permission#GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY} + * {@link permission#REQUEST_SCREEN_LOCK_COMPLEXITY} */ @PasswordComplexity - @RequiresPermission(android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY) + @RequiresPermission(android.Manifest.permission.REQUEST_SCREEN_LOCK_COMPLEXITY) public int getPasswordComplexity() { throwIfParentInstance("getPasswordComplexity"); if (mService == null) { @@ -10537,6 +10537,8 @@ public class DevicePolicyManager { * @hide */ @SystemApi + @RequiresPermission(value = android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, + conditional = true) public void setProfileOwnerCanAccessDeviceIdsForUser( @NonNull ComponentName who, @NonNull UserHandle userHandle) { if (mService == null) { diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 0ac4f6481fc4..80954731bffb 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -791,11 +791,6 @@ public class PackageInstaller { * individual session IDs can be added with {@link #addChildSessionId(int)} * and commit of the multi-package session will result in all child sessions * being committed atomically. - * <p> - * If a package requires to be installed only at reboot, the session should - * be marked as a staged session by calling {@link SessionParams#setStaged()} - * with {@code true}. This can also apply to a multi-package session, in - * which case all the packages in the session will be applied at reboot. */ public static class Session implements Closeable { /** {@hide} */ @@ -1539,7 +1534,11 @@ public class PackageInstaller { * Staged sessions are scheduled to be installed at next reboot. Staged sessions can also be * multi-package. In that case, if any of the children sessions fail to install at reboot, * all the other children sessions are aborted as well. + * + * {@hide} */ + @SystemApi + @RequiresPermission(Manifest.permission.INSTALL_PACKAGES) public void setStaged() { this.isStaged = true; } diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index fe68b8a048c2..b1553250d638 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -2273,7 +2273,8 @@ public final class ShortcutInfo implements Parcelable { CharSequence disabledMessage, int disabledMessageResId, String disabledMessageResName, Set<String> categories, Intent[] intentsWithExtras, int rank, PersistableBundle extras, long lastChangedTimestamp, - int flags, int iconResId, String iconResName, String bitmapPath, int disabledReason) { + int flags, int iconResId, String iconResName, String bitmapPath, int disabledReason, + Person[] persons) { mUserId = userId; mId = id; mPackageName = packageName; @@ -2299,5 +2300,6 @@ public final class ShortcutInfo implements Parcelable { mIconResName = iconResName; mBitmapPath = bitmapPath; mDisabledReason = disabledReason; + mPersons = persons; } } diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index f3ebd7f36fd6..ac44fe93ac31 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -16,6 +16,8 @@ package android.hardware.display; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.ParceledListSlice; @@ -229,7 +231,17 @@ public final class DisplayManagerGlobal { return getCompatibleDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS); } - public void registerDisplayListener(DisplayListener listener, Handler handler) { + /** + * Register a listener for display-related changes. + * + * @param listener The listener that will be called when display changes occur. + * @param handler Handler for the thread that will be receiving the callbacks. May be null. + * If null, listener will use the handler for the current thread, and if still null, + * the handler for the main thread. + * If that is still null, a runtime exception will be thrown. + */ + public void registerDisplayListener(@NonNull DisplayListener listener, + @Nullable Handler handler) { if (listener == null) { throw new IllegalArgumentException("listener must not be null"); } @@ -237,7 +249,8 @@ public final class DisplayManagerGlobal { synchronized (mLock) { int index = findDisplayListenerLocked(listener); if (index < 0) { - mDisplayListeners.add(new DisplayListenerDelegate(listener, handler)); + Looper looper = getLooperForHandler(handler); + mDisplayListeners.add(new DisplayListenerDelegate(listener, looper)); registerCallbackIfNeededLocked(); } } @@ -258,6 +271,17 @@ public final class DisplayManagerGlobal { } } + private static Looper getLooperForHandler(@Nullable Handler handler) { + Looper looper = handler != null ? handler.getLooper() : Looper.myLooper(); + if (looper == null) { + looper = Looper.getMainLooper(); + } + if (looper == null) { + throw new RuntimeException("Could not get Looper for the UI thread."); + } + return looper; + } + private int findDisplayListenerLocked(DisplayListener listener) { final int numListeners = mDisplayListeners.size(); for (int i = 0; i < numListeners; i++) { @@ -636,8 +660,8 @@ public final class DisplayManagerGlobal { private static final class DisplayListenerDelegate extends Handler { public final DisplayListener mListener; - public DisplayListenerDelegate(DisplayListener listener, Handler handler) { - super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/); + DisplayListenerDelegate(DisplayListener listener, @NonNull Looper looper) { + super(looper, null, true /*async*/); mListener = listener; } diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java index 7a4c9bc16ac7..ca49438390e9 100644 --- a/core/java/android/net/NetworkStack.java +++ b/core/java/android/net/NetworkStack.java @@ -15,6 +15,7 @@ */ package android.net; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; @@ -27,6 +28,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.PackageManager; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.IDhcpServerCallbacks; import android.net.ip.IIpClientCallbacks; @@ -201,7 +203,33 @@ public class NetworkStack { final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0); intent.setComponent(comp); - if (comp == null || !context.bindServiceAsUser(intent, new NetworkStackConnection(), + if (comp == null) { + Slog.wtf(TAG, "Could not resolve the network stack with " + intent); + // TODO: crash/reboot system server ? + return; + } + + final PackageManager pm = context.getPackageManager(); + int uid = -1; + try { + uid = pm.getPackageUid(comp.getPackageName(), UserHandle.USER_SYSTEM); + } catch (PackageManager.NameNotFoundException e) { + Slog.wtf("Network stack package not found", e); + // Fall through + } + + if (uid != Process.NETWORK_STACK_UID) { + throw new SecurityException("Invalid network stack UID: " + uid); + } + + final int hasPermission = + pm.checkPermission(PERMISSION_MAINLINE_NETWORK_STACK, comp.getPackageName()); + if (hasPermission != PERMISSION_GRANTED) { + throw new SecurityException( + "Network stack does not have permission " + PERMISSION_MAINLINE_NETWORK_STACK); + } + + if (!context.bindServiceAsUser(intent, new NetworkStackConnection(), Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) { Slog.wtf(TAG, "Could not bind to network stack in-process, or in app with " + intent); diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java index 1f3369376b10..a851e04e78ec 100644 --- a/core/java/android/os/AsyncTask.java +++ b/core/java/android/os/AsyncTask.java @@ -21,13 +21,14 @@ import android.annotation.Nullable; import android.annotation.WorkerThread; import java.util.ArrayDeque; -import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.FutureTask; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -190,13 +191,19 @@ import java.util.concurrent.atomic.AtomicInteger; public abstract class AsyncTask<Params, Progress, Result> { private static final String LOG_TAG = "AsyncTask"; - private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); - // We want at least 2 threads and at most 4 threads in the core pool, - // preferring to have 1 less than the CPU count to avoid saturating - // the CPU with background work - private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)); - private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; - private static final int KEEP_ALIVE_SECONDS = 30; + // We keep only a single pool thread around all the time. + // We let the pool grow to a fairly large number of threads if necessary, + // but let them time out quickly. In the unlikely case that we run out of threads, + // we fall back to a simple unbounded-queue executor. + // This combination ensures that: + // 1. We normally keep few threads (1) around. + // 2. We queue only after launching a significantly larger, but still bounded, set of threads. + // 3. We keep the total number of threads bounded, but still allow an unbounded set + // of tasks to be queued. + private static final int CORE_POOL_SIZE = 1; + private static final int MAXIMUM_POOL_SIZE = 20; + private static final int BACKUP_POOL_SIZE = 5; + private static final int KEEP_ALIVE_SECONDS = 3; private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); @@ -206,8 +213,29 @@ public abstract class AsyncTask<Params, Progress, Result> { } }; - private static final BlockingQueue<Runnable> sPoolWorkQueue = - new LinkedBlockingQueue<Runnable>(128); + // Used only for rejected executions. + // Initialization protected by sRunOnSerialPolicy lock. + private static ThreadPoolExecutor sBackupExecutor; + private static LinkedBlockingQueue<Runnable> sBackupExecutorQueue; + + private static final RejectedExecutionHandler sRunOnSerialPolicy = + new RejectedExecutionHandler() { + public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { + android.util.Log.w(LOG_TAG, "Exceeded ThreadPoolExecutor pool size"); + // As a last ditch fallback, run it on an executor with an unbounded queue. + // Create this executor lazily, hopefully almost never. + synchronized (this) { + if (sBackupExecutor == null) { + sBackupExecutorQueue = new LinkedBlockingQueue<Runnable>(); + sBackupExecutor = new ThreadPoolExecutor( + BACKUP_POOL_SIZE, BACKUP_POOL_SIZE, KEEP_ALIVE_SECONDS, + TimeUnit.SECONDS, sBackupExecutorQueue, sThreadFactory); + sBackupExecutor.allowCoreThreadTimeOut(true); + } + } + sBackupExecutor.execute(r); + } + }; /** * An {@link Executor} that can be used to execute tasks in parallel. @@ -217,8 +245,8 @@ public abstract class AsyncTask<Params, Progress, Result> { static { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, - sPoolWorkQueue, sThreadFactory); - threadPoolExecutor.allowCoreThreadTimeOut(true); + new SynchronousQueue<Runnable>(), sThreadFactory); + threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy); THREAD_POOL_EXECUTOR = threadPoolExecutor; } diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 269c781397ad..8813e400bee0 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -16,6 +16,7 @@ package android.os; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -30,6 +31,7 @@ import android.opengl.EGL14; import android.provider.Settings; import android.util.Base64; import android.util.Log; +import android.widget.Toast; import com.android.framework.protobuf.InvalidProtocolBufferException; @@ -222,9 +224,17 @@ public class GraphicsEnvironment { } - private static List<String> getGlobalSettingsString(Bundle bundle, String globalSetting) { - List<String> valueList = null; - final String settingsValue = bundle.getString(globalSetting); + private static List<String> getGlobalSettingsString(ContentResolver contentResolver, + Bundle bundle, + String globalSetting) { + final List<String> valueList; + final String settingsValue; + + if (bundle != null) { + settingsValue = bundle.getString(globalSetting); + } else { + settingsValue = Settings.Global.getString(contentResolver, globalSetting); + } if (settingsValue != null) { valueList = new ArrayList<>(Arrays.asList(settingsValue.split(","))); @@ -246,17 +256,27 @@ public class GraphicsEnvironment { return -1; } - private static String getDriverForPkg(Bundle bundle, String packageName) { - final String allUseAngle = - bundle.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE); + private static String getDriverForPkg(Context context, Bundle bundle, String packageName) { + final String allUseAngle; + if (bundle != null) { + allUseAngle = + bundle.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE); + } else { + ContentResolver contentResolver = context.getContentResolver(); + allUseAngle = Settings.Global.getString(contentResolver, + Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE); + } if ((allUseAngle != null) && allUseAngle.equals("1")) { return sDriverMap.get(OpenGlDriverChoice.ANGLE); } - final List<String> globalSettingsDriverPkgs = getGlobalSettingsString( - bundle, Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS); - final List<String> globalSettingsDriverValues = getGlobalSettingsString( - bundle, Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES); + final ContentResolver contentResolver = context.getContentResolver(); + final List<String> globalSettingsDriverPkgs = + getGlobalSettingsString(contentResolver, bundle, + Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS); + final List<String> globalSettingsDriverValues = + getGlobalSettingsString(contentResolver, bundle, + Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES); // Make sure we have a good package name if ((packageName == null) || (packageName.isEmpty())) { @@ -308,7 +328,7 @@ public class GraphicsEnvironment { * True: Temporary rules file was loaded. * False: Temporary rules file was *not* loaded. */ - private boolean setupAngleWithTempRulesFile(Context context, + private static boolean setupAngleWithTempRulesFile(Context context, String packageName, String paths, String devOptIn) { @@ -372,7 +392,7 @@ public class GraphicsEnvironment { * True: APK rules file was loaded. * False: APK rules file was *not* loaded. */ - private boolean setupAngleRulesApk(String anglePkgName, + private static boolean setupAngleRulesApk(String anglePkgName, ApplicationInfo angleInfo, PackageManager pm, String packageName, @@ -405,23 +425,32 @@ public class GraphicsEnvironment { /** * Pull ANGLE whitelist from GlobalSettings and compare against current package */ - private boolean checkAngleWhitelist(Bundle bundle, String packageName) { + private static boolean checkAngleWhitelist(Context context, Bundle bundle, String packageName) { + final ContentResolver contentResolver = context.getContentResolver(); final List<String> angleWhitelist = - getGlobalSettingsString(bundle, Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST); + getGlobalSettingsString(contentResolver, bundle, + Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST); return angleWhitelist.contains(packageName); } /** * Pass ANGLE details down to trigger enable logic + * + * @param context + * @param bundle + * @param packageName + * @return true: ANGLE setup successfully + * false: ANGLE not setup (not on whitelist, ANGLE not present, etc.) */ - public void setupAngle(Context context, Bundle bundle, PackageManager pm, String packageName) { + public boolean setupAngle(Context context, Bundle bundle, PackageManager pm, + String packageName) { if (packageName.isEmpty()) { Log.v(TAG, "No package name available yet, skipping ANGLE setup"); - return; + return false; } - final String devOptIn = getDriverForPkg(bundle, packageName); + final String devOptIn = getDriverForPkg(context, bundle, packageName); if (DEBUG) { Log.v(TAG, "ANGLE Developer option for '" + packageName + "' " + "set to: '" + devOptIn + "'"); @@ -439,11 +468,11 @@ public class GraphicsEnvironment { // load a driver, GraphicsEnv::shouldUseAngle() has seen the package name before // and can confidently answer yes/no based on the previously set developer // option value. - final boolean whitelisted = checkAngleWhitelist(bundle, packageName); + final boolean whitelisted = checkAngleWhitelist(context, bundle, packageName); final boolean defaulted = devOptIn.equals(sDriverMap.get(OpenGlDriverChoice.DEFAULT)); final boolean rulesCheck = (whitelisted || !defaulted); if (!rulesCheck) { - return; + return false; } if (whitelisted) { @@ -456,7 +485,7 @@ public class GraphicsEnvironment { final String anglePkgName = getAnglePackageName(pm); if (anglePkgName.isEmpty()) { Log.e(TAG, "Failed to find ANGLE package."); - return; + return false; } final ApplicationInfo angleInfo; @@ -464,7 +493,7 @@ public class GraphicsEnvironment { angleInfo = pm.getApplicationInfo(anglePkgName, PackageManager.MATCH_SYSTEM_ONLY); } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, "ANGLE package '" + anglePkgName + "' not installed"); - return; + return false; } final String abi = chooseAbi(angleInfo); @@ -480,12 +509,62 @@ public class GraphicsEnvironment { if (setupAngleWithTempRulesFile(context, packageName, paths, devOptIn)) { // We setup ANGLE with a temp rules file, so we're done here. - return; + return true; } if (setupAngleRulesApk(anglePkgName, angleInfo, pm, packageName, paths, devOptIn)) { // We setup ANGLE with rules from the APK, so we're done here. - return; + return true; + } + + return false; + } + + /** + * Determine if the "ANGLE In Use" dialog box should be shown. + */ + private boolean shouldShowAngleInUseDialogBox(Context context) { + try { + ContentResolver contentResolver = context.getContentResolver(); + final int showDialogBox = Settings.Global.getInt(contentResolver, + Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX); + + return (showDialogBox == 1); + } catch (Settings.SettingNotFoundException | SecurityException e) { + // Do nothing and move on + } + + // No setting, so assume false + return false; + } + + /** + * Determine if ANGLE should be used. + */ + private boolean shouldUseAngle(Context context, String packageName) { + // Need to make sure we are evaluating ANGLE usage for the correct circumstances + if (!setupAngle(context, null, context.getPackageManager(), packageName)) { + Log.v(TAG, "Package '" + packageName + "' should use not ANGLE"); + return false; + } + + final boolean useAngle = getShouldUseAngle(packageName); + Log.v(TAG, "Package '" + packageName + "' should use ANGLE = '" + useAngle + "'"); + + return useAngle; + } + + /** + * Show the ANGLE in Use Dialog Box + * @param context + */ + public void showAngleInUseDialogBox(Context context) { + final String packageName = context.getPackageName(); + + if (shouldShowAngleInUseDialogBox(context) && shouldUseAngle(context, packageName)) { + final String toastMsg = packageName + " is using ANGLE"; + final Toast toast = Toast.makeText(context, toastMsg, Toast.LENGTH_LONG); + toast.show(); } } @@ -541,19 +620,19 @@ public class GraphicsEnvironment { if (gameDriverAllApps != 1) { // GAME_DRIVER_OPT_OUT_APPS has higher priority than GAME_DRIVER_OPT_IN_APPS - if (getGlobalSettingsString(coreSettings, Settings.Global.GAME_DRIVER_OPT_OUT_APPS) - .contains(packageName)) { + if (getGlobalSettingsString(null, coreSettings, + Settings.Global.GAME_DRIVER_OPT_OUT_APPS).contains(packageName)) { if (DEBUG) { Log.w(TAG, packageName + " opts out from Game Driver."); } return false; } final boolean isOptIn = - getGlobalSettingsString(coreSettings, Settings.Global.GAME_DRIVER_OPT_IN_APPS) - .contains(packageName); + getGlobalSettingsString(null, coreSettings, + Settings.Global.GAME_DRIVER_OPT_IN_APPS).contains(packageName); if (!isOptIn - && !getGlobalSettingsString(coreSettings, Settings.Global.GAME_DRIVER_WHITELIST) - .contains(packageName)) { + && !getGlobalSettingsString(null, coreSettings, + Settings.Global.GAME_DRIVER_WHITELIST).contains(packageName)) { if (DEBUG) { Log.w(TAG, packageName + " is not on the whitelist."); } @@ -660,4 +739,5 @@ public class GraphicsEnvironment { long driverVersionCode, String appPackageName); private static native void setAngleInfo(String path, String appPackage, String devOptIn, FileDescriptor rulesFd, long rulesOffset, long rulesLength); + private static native boolean getShouldUseAngle(String packageName); } diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl index dde46cd93d5f..0751b964f85e 100644 --- a/core/java/android/os/IStatsCompanionService.aidl +++ b/core/java/android/os/IStatsCompanionService.aidl @@ -69,6 +69,12 @@ interface IStatsCompanionService { oneway void sendDataBroadcast(in IBinder intentSender, long lastReportTimeNs); /** + * Send a broadcast to the specified PendingIntent's as IBinder notifying it that the list + * of active configs has changed. + */ + oneway void sendActiveConfigsChangedBroadcast(in IBinder intentSender, in long[] configIds); + + /** * Requests StatsCompanionService to send a broadcast using the given intentSender * (which should cast to an IIntentSender), along with the other information specified. */ diff --git a/core/java/android/os/ParcelFileDescriptor.aidl b/core/java/android/os/ParcelFileDescriptor.aidl deleted file mode 100644 index c07b98055d5a..000000000000 --- a/core/java/android/os/ParcelFileDescriptor.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* //device/java/android/android/os/ParcelFileDescriptor.aidl -** -** Copyright 2007, 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.os; - -parcelable ParcelFileDescriptor cpp_header "binder/ParcelFileDescriptor.h"; diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 735f4f253594..43c906495cb6 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -1545,7 +1545,7 @@ public class StorageManager { public static boolean hasIsolatedStorage() { // Prefer to use snapshot for current boot when available return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE_SNAPSHOT, - SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false)); + SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, true)); } /** diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 18e1f5902a31..104b61dc410d 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -308,6 +308,21 @@ public final class DeviceConfig { } /** + * Namespace for Rollback. + * + * @hide + */ + @SystemApi + public interface Rollback { + String NAMESPACE = "rollback"; + + /** + * Timeout in milliseconds for enabling package rollback. + */ + String ENABLE_ROLLBACK_TIMEOUT = "enable_rollback_timeout"; + } + + /** * Namespace for storage-related features. * * @hide diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 22329baa5109..2b638f6e283f 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -12318,6 +12318,14 @@ public final class Settings { "angle_whitelist"; /** + * Show the "ANGLE In Use" dialog box to the user when ANGLE is the OpenGL driver. + * The value is a boolean (1 or 0). + * @hide + */ + public static final String GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX = + "show_angle_in_use_dialog_box"; + + /** * Game Driver global preference for all Apps. * 0 = Default * 1 = All Apps use Game Driver @@ -12347,6 +12355,12 @@ public final class Settings { public static final String GAME_DRIVER_BLACKLIST = "game_driver_blacklist"; /** + * List of blacklists, each blacklist is a blacklist for a specific version of Game Driver. + * @hide + */ + public static final String GAME_DRIVER_BLACKLISTS = "game_driver_blacklists"; + + /** * Apps on the whitelist that are allowed to use Game Driver. * The string is a list of application package names, seperated by comma. * i.e. <apk1>,<apk2>,...,<apkN> diff --git a/core/java/android/util/MathUtils.java b/core/java/android/util/MathUtils.java index 56558d04e50d..0eeef70a79a6 100644 --- a/core/java/android/util/MathUtils.java +++ b/core/java/android/util/MathUtils.java @@ -166,6 +166,26 @@ public final class MathUtils { } /** + * Returns the interpolation scalar (s) that satisfies the equation: {@code value = }{@link + * #lerp}{@code (a, b, s)} + * + * <p>If {@code a == b}, then this function will return 0. + */ + public static float lerpInv(float a, float b, float value) { + return a != b ? ((value - a) / (b - a)) : 0.0f; + } + + /** Returns the single argument constrained between [0.0, 1.0]. */ + public static float saturate(float value) { + return constrain(value, 0.0f, 1.0f); + } + + /** Returns the saturated (constrained between [0, 1]) result of {@link #lerpInv}. */ + public static float lerpInvSat(float a, float b, float value) { + return saturate(lerpInv(a, b, value)); + } + + /** * Returns an interpolated angle in degrees between a set of start and end * angles. * <p> @@ -195,6 +215,32 @@ public final class MathUtils { } /** + * Calculates a value in [rangeMin, rangeMax] that maps value in [valueMin, valueMax] to + * returnVal in [rangeMin, rangeMax]. + * <p> + * Always returns a constrained value in the range [rangeMin, rangeMax], even if value is + * outside [valueMin, valueMax]. + * <p> + * Eg: + * constrainedMap(0f, 100f, 0f, 1f, 0.5f) = 50f + * constrainedMap(20f, 200f, 10f, 20f, 20f) = 200f + * constrainedMap(20f, 200f, 10f, 20f, 50f) = 200f + * constrainedMap(10f, 50f, 10f, 20f, 5f) = 10f + * + * @param rangeMin minimum of the range that should be returned. + * @param rangeMax maximum of the range that should be returned. + * @param valueMin minimum of range to map {@code value} to. + * @param valueMax maximum of range to map {@code value} to. + * @param value to map to the range [{@code valueMin}, {@code valueMax}]. Note, can be outside + * this range, resulting in a clamped value. + * @return the mapped value, constrained to [{@code rangeMin}, {@code rangeMax}. + */ + public static float constrainedMap( + float rangeMin, float rangeMax, float valueMin, float valueMax, float value) { + return lerp(rangeMin, rangeMax, lerpInvSat(valueMin, valueMax, value)); + } + + /** * Perform Hermite interpolation between two values. * Eg: * smoothStep(0, 0.5f, 0.5f) = 1f diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index 06207a9290d7..384cdbb3831c 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -174,7 +174,7 @@ public final class AccessibilityManager { final Handler.Callback mCallback; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) boolean mIsEnabled; int mRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK; diff --git a/core/java/android/view/textclassifier/TEST_MAPPING b/core/java/android/view/textclassifier/TEST_MAPPING index 0d3c3465f2d6..01a6edecf21e 100644 --- a/core/java/android/view/textclassifier/TEST_MAPPING +++ b/core/java/android/view/textclassifier/TEST_MAPPING @@ -10,14 +10,6 @@ "exclude-annotation": "androidx.test.filters.FlakyTest" } ] - }, - { - "name": "CtsViewTestCases", - "options": [ - { - "include-filter": "android.view.textclassifier.cts" - } - ] } ] } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 9c21ba60a034..ded3be4e4ef5 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -6020,9 +6020,6 @@ public class Editor { } updateSelection(event); - if (mTextView.hasSelection() && mEndHandle != null) { - mEndHandle.updateMagnifier(event); - } break; case MotionEvent.ACTION_UP: @@ -6030,9 +6027,6 @@ public class Editor { break; } updateSelection(event); - if (mEndHandle != null) { - mEndHandle.dismissMagnifier(); - } // No longer dragging to select text, let the parent intercept events. mTextView.getParent().requestDisallowInterceptTouchEvent(false); diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index 89bb2738b899..dad2669d3de8 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -17,6 +17,8 @@ package android.widget; import android.annotation.CallSuper; +import android.annotation.ColorInt; +import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.Px; @@ -340,7 +342,7 @@ public class NumberPicker extends LinearLayout { /** * The {@link Paint} for drawing the selector. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private final Paint mSelectorWheelPaint; /** @@ -1718,6 +1720,44 @@ public class NumberPicker extends LinearLayout { } /** + * Sets the text color for all the states (normal, selected, focused) to be the given color. + * + * @param color A color value in the form 0xAARRGGBB. + */ + public void setTextColor(@ColorInt int color) { + mSelectorWheelPaint.setColor(color); + mInputText.setTextColor(color); + invalidate(); + } + + /** + * @return the text color. + */ + @ColorInt + public int getTextColor() { + return mSelectorWheelPaint.getColor(); + } + + /** + * Sets the text size to the given value. This value must be > 0 + * + * @param size The size in pixel units. + */ + public void setTextSize(@FloatRange(from = 0.0, fromInclusive = false) float size) { + mSelectorWheelPaint.setTextSize(size); + mInputText.setTextSize(TypedValue.COMPLEX_UNIT_PX, size); + invalidate(); + } + + /** + * @return the size (in pixels) of the text size in this NumberPicker. + */ + @FloatRange(from = 0.0, fromInclusive = false) + public float getTextSize() { + return mSelectorWheelPaint.getTextSize(); + } + + /** * Makes a measure spec that tries greedily to use the max value. * * @param measureSpec The measure spec. diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 119a015cd5ea..8ebcef5133b6 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -46,6 +46,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager; +import android.database.Cursor; import android.database.DataSetObserver; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -69,6 +70,8 @@ import android.os.ResultReceiver; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; +import android.provider.DocumentsContract; +import android.provider.OpenableColumns; import android.service.chooser.ChooserTarget; import android.service.chooser.ChooserTargetService; import android.service.chooser.IChooserTargetResult; @@ -87,7 +90,6 @@ import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.widget.AbsListView; import android.widget.BaseAdapter; -import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; @@ -373,50 +375,6 @@ public class ChooserActivity extends ResolverActivity { super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents, null, false); - Button copyButton = findViewById(R.id.copy_button); - copyButton.setOnClickListener(view -> { - Intent targetIntent = getTargetIntent(); - if (targetIntent == null) { - finish(); - } else { - final String action = targetIntent.getAction(); - - ClipData clipData = null; - if (Intent.ACTION_SEND.equals(action)) { - String extraText = targetIntent.getStringExtra(Intent.EXTRA_TEXT); - Uri extraStream = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); - - if (extraText != null) { - clipData = ClipData.newPlainText(null, extraText); - } else if (extraStream != null) { - clipData = ClipData.newUri(getContentResolver(), null, extraStream); - } else { - Log.w(TAG, "No data available to copy to clipboard"); - return; - } - } else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) { - final ArrayList<Uri> streams = targetIntent.getParcelableArrayListExtra( - Intent.EXTRA_STREAM); - clipData = ClipData.newUri(getContentResolver(), null, streams.get(0)); - for (int i = 1; i < streams.size(); i++) { - clipData.addItem(getContentResolver(), new ClipData.Item(streams.get(i))); - } - } else { - // expected to only be visible with ACTION_SEND or ACTION_SEND_MULTIPLE - // so warn about unexpected action - Log.w(TAG, "Action (" + action + ") not supported for copying to clipboard"); - return; - } - - ClipboardManager clipboardManager = (ClipboardManager) getSystemService( - Context.CLIPBOARD_SERVICE); - clipboardManager.setPrimaryClip(clipData); - Toast.makeText(getApplicationContext(), R.string.copied, Toast.LENGTH_SHORT).show(); - - finish(); - } - }); - mChooserShownTime = System.currentTimeMillis(); final long systemCost = mChooserShownTime - intentReceivedTime; @@ -474,6 +432,10 @@ public class ChooserActivity extends ResolverActivity { return; } + if (mChooserListAdapter == null || mChooserListAdapter.getCount() == 0) { + return; + } + int previewType = findPreferredContentPreview(targetIntent, getContentResolver()); getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_SHARE_WITH_PREVIEW) @@ -481,6 +443,49 @@ public class ChooserActivity extends ResolverActivity { displayContentPreview(previewType, targetIntent); } + private void onCopyButtonClicked(View v) { + Intent targetIntent = getTargetIntent(); + if (targetIntent == null) { + finish(); + } else { + final String action = targetIntent.getAction(); + + ClipData clipData = null; + if (Intent.ACTION_SEND.equals(action)) { + String extraText = targetIntent.getStringExtra(Intent.EXTRA_TEXT); + Uri extraStream = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); + + if (extraText != null) { + clipData = ClipData.newPlainText(null, extraText); + } else if (extraStream != null) { + clipData = ClipData.newUri(getContentResolver(), null, extraStream); + } else { + Log.w(TAG, "No data available to copy to clipboard"); + return; + } + } else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) { + final ArrayList<Uri> streams = targetIntent.getParcelableArrayListExtra( + Intent.EXTRA_STREAM); + clipData = ClipData.newUri(getContentResolver(), null, streams.get(0)); + for (int i = 1; i < streams.size(); i++) { + clipData.addItem(getContentResolver(), new ClipData.Item(streams.get(i))); + } + } else { + // expected to only be visible with ACTION_SEND or ACTION_SEND_MULTIPLE + // so warn about unexpected action + Log.w(TAG, "Action (" + action + ") not supported for copying to clipboard"); + return; + } + + ClipboardManager clipboardManager = (ClipboardManager) getSystemService( + Context.CLIPBOARD_SERVICE); + clipboardManager.setPrimaryClip(clipData); + Toast.makeText(getApplicationContext(), R.string.copied, Toast.LENGTH_SHORT).show(); + + finish(); + } + } + private void displayContentPreview(@ContentPreviewType int previewType, Intent targetIntent) { switch (previewType) { case CONTENT_PREVIEW_TEXT: @@ -501,6 +506,8 @@ public class ChooserActivity extends ResolverActivity { ViewGroup contentPreviewLayout = findViewById(R.id.content_preview_text_area); contentPreviewLayout.setVisibility(View.VISIBLE); + findViewById(R.id.copy_button).setOnClickListener(this::onCopyButtonClicked); + CharSequence sharingText = targetIntent.getCharSequenceExtra(Intent.EXTRA_TEXT); if (sharingText == null) { findViewById(R.id.content_preview_text_layout).setVisibility(View.GONE); @@ -510,7 +517,7 @@ public class ChooserActivity extends ResolverActivity { } String previewTitle = targetIntent.getStringExtra(Intent.EXTRA_TITLE); - if (previewTitle == null || previewTitle.trim().isEmpty()) { + if (TextUtils.isEmpty(previewTitle)) { findViewById(R.id.content_preview_title_layout).setVisibility(View.GONE); } else { TextView previewTitleView = findViewById(R.id.content_preview_title); @@ -561,6 +568,7 @@ public class ChooserActivity extends ResolverActivity { if (imageUris.size() == 0) { Log.i(TAG, "Attempted to display image preview area with zero" + " available images detected in EXTRA_STREAM list"); + contentPreviewLayout.setVisibility(View.GONE); return; } @@ -580,15 +588,95 @@ public class ChooserActivity extends ResolverActivity { } } + private static class FileInfo { + public final String name; + public final boolean hasThumbnail; + + FileInfo(String name, boolean hasThumbnail) { + this.name = name; + this.hasThumbnail = hasThumbnail; + } + } + + private FileInfo extractFileInfo(Uri uri, ContentResolver resolver) { + String fileName = null; + boolean hasThumbnail = false; + Cursor cursor = resolver.query(uri, null, null, null, null); + if (cursor != null && cursor.getCount() > 0) { + int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); + int flagsIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_FLAGS); + + cursor.moveToFirst(); + fileName = cursor.getString(nameIndex); + if (flagsIndex != -1) { + hasThumbnail = (cursor.getInt(flagsIndex) + & DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL) != 0; + } + } + + if (TextUtils.isEmpty(fileName)) { + fileName = uri.getPath(); + int index = fileName.lastIndexOf('/'); + if (index != -1) { + fileName = fileName.substring(index + 1); + } + } + + return new FileInfo(fileName, hasThumbnail); + } + private void displayFileContentPreview(Intent targetIntent) { - // support coming + ViewGroup contentPreviewLayout = findViewById(R.id.content_preview_file_area); + contentPreviewLayout.setVisibility(View.VISIBLE); + + // TODO(b/120417119): Disable file copy until after moving to sysui, + // due to permissions issues + findViewById(R.id.file_copy_button).setVisibility(View.GONE); + + ContentResolver resolver = getContentResolver(); + TextView fileNameView = findViewById(R.id.content_preview_filename); + String action = targetIntent.getAction(); + if (Intent.ACTION_SEND.equals(action)) { + Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); + + FileInfo fileInfo = extractFileInfo(uri, resolver); + fileNameView.setText(fileInfo.name); + + if (fileInfo.hasThumbnail) { + loadUriIntoView(R.id.content_preview_file_thumbnail, uri); + } else { + ImageView fileIconView = findViewById(R.id.content_preview_file_icon); + fileIconView.setVisibility(View.VISIBLE); + fileIconView.setImageResource(R.drawable.ic_doc_generic); + } + } else { + List<Uri> uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); + if (uris.size() == 0) { + contentPreviewLayout.setVisibility(View.GONE); + Log.i(TAG, + "Appears to be no uris available in EXTRA_STREAM, removing preview area"); + return; + } + + FileInfo fileInfo = extractFileInfo(uris.get(0), resolver); + int remFileCount = uris.size() - 1; + String fileName = getResources().getQuantityString(R.plurals.file_count, + remFileCount, fileInfo.name, remFileCount); + + fileNameView.setText(fileName); + ImageView fileIconView = findViewById(R.id.content_preview_file_icon); + fileIconView.setVisibility(View.VISIBLE); + fileIconView.setImageResource(R.drawable.ic_file_copy); + } } private RoundedRectImageView loadUriIntoView(int imageResourceId, Uri uri) { RoundedRectImageView imageView = findViewById(imageResourceId); - imageView.setVisibility(View.VISIBLE); Bitmap bmp = loadThumbnail(uri, new Size(200, 200)); - imageView.setImageBitmap(bmp); + if (bmp != null) { + imageView.setVisibility(View.VISIBLE); + imageView.setImageBitmap(bmp); + } return imageView; } @@ -1261,9 +1349,8 @@ public class ChooserActivity extends ResolverActivity { } try { - return ImageUtils.decodeSampledBitmapFromStream(getContentResolver(), - uri, size.getWidth(), size.getHeight()); - } catch (IOException | NullPointerException ex) { + return ImageUtils.loadThumbnail(getContentResolver(), uri, size); + } catch (IOException | NullPointerException | SecurityException ex) { Log.w(TAG, "Error loading preview thumbnail for uri: " + uri.toString(), ex); } return null; diff --git a/core/java/com/android/internal/util/ImageUtils.java b/core/java/com/android/internal/util/ImageUtils.java index 195ae52ce977..274a5136d8e9 100644 --- a/core/java/com/android/internal/util/ImageUtils.java +++ b/core/java/com/android/internal/util/ImageUtils.java @@ -16,20 +16,25 @@ package com.android.internal.util; +import android.content.ContentProviderClient; import android.content.ContentResolver; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; -import android.graphics.BitmapFactory; import android.graphics.Canvas; +import android.graphics.ImageDecoder; +import android.graphics.ImageDecoder.ImageInfo; +import android.graphics.ImageDecoder.Source; import android.graphics.Matrix; import android.graphics.Paint; +import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Bundle; +import android.util.Size; import java.io.IOException; -import java.io.InputStream; /** * Utility class for image analysis and processing. @@ -166,21 +171,18 @@ public class ImageUtils { /** * @see https://developer.android.com/topic/performance/graphics/load-bitmap */ - public static int calculateInSampleSize(BitmapFactory.Options options, - int reqWidth, int reqHeight) { - // Raw height and width of image - final int height = options.outHeight; - final int width = options.outWidth; + public static int calculateSampleSize(Size currentSize, Size requestedSize) { int inSampleSize = 1; - if (height > reqHeight || width > reqWidth) { - final int halfHeight = height / 2; - final int halfWidth = width / 2; + if (currentSize.getHeight() > requestedSize.getHeight() + || currentSize.getWidth() > requestedSize.getWidth()) { + final int halfHeight = currentSize.getHeight() / 2; + final int halfWidth = currentSize.getWidth() / 2; // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. - while ((halfHeight / inSampleSize) >= reqHeight - && (halfWidth / inSampleSize) >= reqWidth) { + while ((halfHeight / inSampleSize) >= requestedSize.getHeight() + && (halfWidth / inSampleSize) >= requestedSize.getWidth()) { inSampleSize *= 2; } } @@ -190,27 +192,27 @@ public class ImageUtils { /** * Load a bitmap, and attempt to downscale to the required size, to save - * on memory. + * on memory. Updated to use newer and more compatible ImageDecoder. * * @see https://developer.android.com/topic/performance/graphics/load-bitmap */ - public static Bitmap decodeSampledBitmapFromStream(ContentResolver resolver, - Uri uri, int reqWidth, int reqHeight) throws IOException { - - final BitmapFactory.Options options = new BitmapFactory.Options(); - try (InputStream is = resolver.openInputStream(uri)) { - // First decode with inJustDecodeBounds=true to check dimensions - options.inJustDecodeBounds = true; - BitmapFactory.decodeStream(is, null, options); - - options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); - } - - // need to do this twice as the InputStream is consumed in the first call, - // and not all InputStreams support marks - try (InputStream is = resolver.openInputStream(uri)) { - options.inJustDecodeBounds = false; - return BitmapFactory.decodeStream(is, null, options); + public static Bitmap loadThumbnail(ContentResolver resolver, Uri uri, Size size) + throws IOException { + + try (ContentProviderClient client = resolver.acquireContentProviderClient(uri)) { + final Bundle opts = new Bundle(); + opts.putParcelable(ContentResolver.EXTRA_SIZE, Point.convert(size)); + + return ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> { + return client.openTypedAssetFile(uri, "image/*", opts, null); + }), (ImageDecoder decoder, ImageInfo info, Source source) -> { + decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); + + final int sample = calculateSampleSize(info.getSize(), size); + if (sample > 1) { + decoder.setTargetSampleSize(sample); + } + }); } } } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 345058bd4f6f..af0b7c307ef6 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -287,8 +287,8 @@ cc_library_shared { "libsoundtrigger", "libminikin", "libprocessgroup", - "libnativebridge", - "libnativeloader", + "libnativebridge_lazy", + "libnativeloader_lazy", "libmemunreachable", "libhidlbase", "libhidltransport", diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp index e2e66ceb6fbe..95f99b760382 100644 --- a/core/jni/android_os_GraphicsEnvironment.cpp +++ b/core/jni/android_os_GraphicsEnvironment.cpp @@ -55,6 +55,11 @@ void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appNa devOptInChars.c_str(), rulesFd_native, rulesOffset, rulesLength); } +bool shouldUseAngle_native(JNIEnv* env, jobject clazz, jstring appName) { + ScopedUtfChars appNameChars(env, appName); + return android::GraphicsEnv::getInstance().shouldUseAngle(appNameChars.c_str()); +} + void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) { android::NativeLoaderNamespace* appNamespace = android::FindNativeLoaderNamespaceByClassLoader( env, classLoader); @@ -81,6 +86,7 @@ const JNINativeMethod g_methods[] = { { "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) }, { "setGpuStats", "(Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;)V", reinterpret_cast<void*>(setGpuStats_native) }, { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/io/FileDescriptor;JJ)V", reinterpret_cast<void*>(setAngleInfo_native) }, + { "getShouldUseAngle", "(Ljava/lang/String;)Z", reinterpret_cast<void*>(shouldUseAngle_native) }, { "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) }, { "setDebugLayers", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayers_native) }, { "setDebugLayersGLES", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayersGLES_native) }, diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index d04db92294d7..5cecf66a593c 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -742,7 +742,7 @@ static void MountEmulatedStorage(uid_t uid, jint mount_mode, return; } - if (GetBoolProperty(kIsolatedStorageSnapshot, GetBoolProperty(kIsolatedStorage, false))) { + if (GetBoolProperty(kIsolatedStorageSnapshot, GetBoolProperty(kIsolatedStorage, true))) { if (mount_mode == MOUNT_EXTERNAL_FULL || mount_mode == MOUNT_EXTERNAL_LEGACY) { storageSource = (mount_mode == MOUNT_EXTERNAL_FULL) ? "/mnt/runtime/full" : "/mnt/runtime/write"; diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index 30b9b7847fb6..5497b8665cf0 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -610,6 +610,11 @@ enum Action { // CATEGORY: SETTINGS // OS: Q ACTION_SET_NEW_PARENT_PROFILE_PASSWORD = 1646; + + // ACTION: An interaction with a Slice or other component in the Panel. + // CATEGORY: SETTINGS + // OS: Q + ACTION_PANEL_INTERACTION = 1658; } /** @@ -2214,4 +2219,16 @@ enum PageId { // CATEGORY: SETTINGS // OS: Q SET_NEW_PASSWORD_ACTIVITY = 1644; + + // Panel for Internet Connectivity + PANEL_INTERNET_CONNECTIVITY = 1654; + + // Panel for Volume + PANEL_VOLUME = 1655; + + // Panel for NFC + PANEL_NFC = 1656; + + // Panel for Media Output + PANEL_MEDIA_OUTPUT = 1657; } diff --git a/core/proto/android/hardware/sensor/assist/enums.proto b/core/proto/android/hardware/sensor/assist/enums.proto new file mode 100644 index 000000000000..8c5841a32d54 --- /dev/null +++ b/core/proto/android/hardware/sensor/assist/enums.proto @@ -0,0 +1,34 @@ +/* + * 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. + */ + +syntax = "proto2"; +package android.hardware.sensor.assist; + +option java_outer_classname = "AssistGestureProtoEnums"; +option java_multiple_files = true; + +enum AssistGestureStageEnum { + ASSIST_GESTURE_STAGE_UNKNOWN = 0; + ASSIST_GESTURE_STAGE_PROGRESS = 1; + ASSIST_GESTURE_STAGE_PRIMED = 2; + ASSIST_GESTURE_STAGE_DETECTED = 3; +} + +enum AssistGestureFeedbackEnum { + ASSIST_GESTURE_FEEDBACK_UNKNOWN = 0; + ASSIST_GESTURE_FEEDBACK_NOT_USED = 1; + ASSIST_GESTURE_FEEDBACK_USED = 2; +}
\ No newline at end of file diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index d5776534bb90..c9957f369473 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -448,6 +448,11 @@ message GlobalSettingsProto { optional SettingProto game_driver_whitelist = 12; // ANGLE - List of Apps that can check ANGLE rules optional SettingProto angle_whitelist = 13; + // Game Driver - List of blacklists, each blacklist is a blacklist for + // a specific Game Driver version + optional SettingProto game_driver_blacklists = 14; + // ANGLE - Show a dialog box when ANGLE is selected for the currently running PKG + optional SettingProto show_angle_in_use_dialog = 15; } optional Gpu gpu = 59; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 34ec92e68511..5b74d90608f7 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2151,13 +2151,13 @@ android:label="@string/permlab_disableKeyguard" android:protectionLevel="normal" /> - <!-- Allows an application to get the screen lock complexity and prompt users to update the + <!-- Allows an application to request the screen lock complexity and prompt users to update the screen lock to a certain complexity level. <p>Protection level: normal --> - <permission android:name="android.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY" - android:label="@string/permlab_getAndRequestScreenLockComplexity" - android:description="@string/permdesc_getAndRequestScreenLockComplexity" + <permission android:name="android.permission.REQUEST_SCREEN_LOCK_COMPLEXITY" + android:label="@string/permlab_requestScreenLockComplexity" + android:description="@string/permdesc_requestScreenLockComplexity" android:protectionLevel="normal" /> <!-- ================================== --> diff --git a/core/res/res/drawable/bottomsheet_background.xml b/core/res/res/drawable/bottomsheet_background.xml index bc32ba6e3896..3a8ad71187a0 100644 --- a/core/res/res/drawable/bottomsheet_background.xml +++ b/core/res/res/drawable/bottomsheet_background.xml @@ -16,7 +16,7 @@ <shape android:shape="rectangle" xmlns:android="http://schemas.android.com/apk/res/android"> <corners - android:topLeftRadius="?attr/dialogCornerRadius" - android:topRightRadius="?attr/dialogCornerRadius" /> + android:topLeftRadius="@dimen/config_bottomDialogCornerRadius" + android:topRightRadius="@dimen/config_bottomDialogCornerRadius"/> <solid android:color="?attr/colorBackgroundFloating" /> </shape> diff --git a/core/res/res/drawable/ic_file_copy.xml b/core/res/res/drawable/ic_file_copy.xml new file mode 100644 index 000000000000..b6d5e7328c40 --- /dev/null +++ b/core/res/res/drawable/ic_file_copy.xml @@ -0,0 +1,24 @@ +<!-- + 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM15,5l6,6v10c0,1.1 -0.9,2 -2,2L7.99,23C6.89,23 6,22.1 6,21l0.01,-14c0,-1.1 0.89,-2 1.99,-2h7zM14,12h5.5L14,6.5L14,12z" + android:fillColor="#FF737373"/> +</vector> diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml index 14a5310a4ff2..3683bfd02e87 100644 --- a/core/res/res/layout/chooser_grid.xml +++ b/core/res/res/layout/chooser_grid.xml @@ -213,6 +213,65 @@ </LinearLayout> </LinearLayout> + <!-- Layout Option 3: File preview, icon, filename, copy--> + <LinearLayout + android:id="@+id/content_preview_file_area" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingBottom="@dimen/chooser_view_spacing" + android:visibility="gone" + android:background="?attr/colorBackgroundFloating"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:paddingLeft="@dimen/chooser_edge_margin_normal" + android:paddingRight="@dimen/chooser_edge_margin_normal" + android:layout_marginBottom="@dimen/chooser_view_spacing" + android:id="@+id/content_preview_file_layout"> + + <view class="com.android.internal.app.ChooserActivity$RoundedRectImageView" + android:id="@+id/content_preview_file_thumbnail" + android:layout_width="75dp" + android:layout_height="75dp" + android:layout_marginRight="16dp" + android:adjustViewBounds="true" + android:layout_gravity="center_vertical" + android:gravity="center" + android:scaleType="centerCrop" + android:visibility="gone"/> + <ImageView + android:id="@+id/content_preview_file_icon" + android:layout_width="36dp" + android:layout_height="36dp" + android:layout_marginRight="16dp" + android:adjustViewBounds="true" + android:layout_gravity="center_vertical" + android:gravity="center" + android:scaleType="fitCenter" + android:visibility="gone"/> + <TextView + android:id="@+id/content_preview_filename" + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:ellipsize="middle" + android:gravity="start|top" + android:paddingRight="24dp" + android:singleLine="true"/> + <Button + android:id="@+id/file_copy_button" + android:layout_width="24dp" + android:layout_height="24dp" + android:gravity="center" + android:layout_gravity="center_vertical" + android:background="@drawable/ic_content_copy_gm2"/> + </LinearLayout> + </LinearLayout> + <ListView android:layout_width="match_parent" android:layout_height="match_parent" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 18eb7e96d748..d14164f69850 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3903,4 +3903,22 @@ The ambient color temperature (in cct) to which we fall back when the ambient brightness drops beneath a certain threshold. --> <item name="config_displayWhiteBalanceLowLightAmbientColorTemperature" format="float" type="dimen">6500.0</item> + + <!-- See DisplayWhiteBalanceController. + A float array containing a list of ambient color temperatures, in Kelvin. This array, + together with config_displayWhiteBalanceDisplayTemperatureValues, is used to generate a + lookup table used in DisplayWhiteBalanceController. This lookup table is used to map + ambient color temperature readings to a target color temperature for the display. + This table is optional. If used, this array must, + 1) Contain at least two entries + 2) Be the same length as config_displayWhiteBalanceDisplayTemperatureValues. --> + <array name="config_displayWhiteBalanceAmbientTemperatureValues"> + </array> + + <!-- See DisplayWhiteBalanceController. + An array containing a list of display color temperatures, in Kelvin. See + config_displayWhiteBalanceAmbientTemperatureValues for additional details. + The same restrictions apply to this array. --> + <array name="config_displayWhiteBalanceDisplayTemperatureValues"> + </array> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 6d5bd4ba1645..0cdf38849d8b 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1406,9 +1406,9 @@ re-enables the keylock when the call is finished.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] --> - <string name="permlab_getAndRequestScreenLockComplexity">get and request screen lock complexity</string> + <string name="permlab_requestScreenLockComplexity">request screen lock complexity</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] --> - <string name="permdesc_getAndRequestScreenLockComplexity">Allows the app to learn the screen + <string name="permdesc_requestScreenLockComplexity">Allows the app to learn the screen lock complexity level (high, medium, low or none), which indicates the possible range of length and type of the screen lock. The app can also suggest to users that they update the screen lock to a certain level but users can freely ignore and navigate away. Note that the @@ -5284,4 +5284,9 @@ <!-- Strings for car --> <!-- String displayed when loading a user in the car [CHAR LIMIT=30] --> <string name="car_loading_profile">Loading</string> + + <plurals name="file_count"> + <item quantity="one"><xliff:g id="file_name">%s</xliff:g> + <xliff:g id="count">%d</xliff:g> file</item> + <item quantity="other"><xliff:g id="file_name">%s</xliff:g> + <xliff:g id="count">%d</xliff:g> files</item> + </plurals> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 8e251fd4ea6f..a1bafbf8de69 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -50,6 +50,11 @@ <java-symbol type="id" name="characterPicker" /> <java-symbol type="id" name="clearDefaultHint" /> <java-symbol type="id" name="contentPanel" /> + <java-symbol type="id" name="content_preview_file_area" /> + <java-symbol type="id" name="content_preview_file_icon" /> + <java-symbol type="id" name="content_preview_file_layout" /> + <java-symbol type="id" name="content_preview_file_thumbnail" /> + <java-symbol type="id" name="content_preview_filename" /> <java-symbol type="id" name="content_preview_image_area" /> <java-symbol type="id" name="content_preview_image_1_large" /> <java-symbol type="id" name="content_preview_image_2_large" /> @@ -62,6 +67,7 @@ <java-symbol type="id" name="content_preview_title" /> <java-symbol type="id" name="content_preview_title_layout" /> <java-symbol type="id" name="copy_button" /> + <java-symbol type="id" name="file_copy_button" /> <java-symbol type="id" name="current_scene" /> <java-symbol type="id" name="scene_layoutid_cache" /> <java-symbol type="id" name="customPanel" /> @@ -1266,6 +1272,7 @@ <java-symbol type="string" name="tooltip_popup_title" /> <java-symbol type="plurals" name="bugreport_countdown" /> + <java-symbol type="plurals" name="file_count" /> <java-symbol type="plurals" name="last_num_days" /> <java-symbol type="plurals" name="matches_found" /> <java-symbol type="plurals" name="restr_pin_countdown" /> @@ -1307,6 +1314,7 @@ <java-symbol type="drawable" name="default_wallpaper" /> <java-symbol type="drawable" name="default_lock_wallpaper" /> <java-symbol type="drawable" name="indicator_input_error" /> + <java-symbol type="drawable" name="ic_file_copy" /> <java-symbol type="drawable" name="popup_bottom_dark" /> <java-symbol type="drawable" name="popup_bottom_bright" /> <java-symbol type="drawable" name="popup_bottom_medium" /> @@ -3643,6 +3651,7 @@ <java-symbol type="array" name="config_displayWhiteBalanceDecreaseThresholds" /> <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientBrightnessThreshold" /> <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientColorTemperature" /> - + <java-symbol type="array" name="config_displayWhiteBalanceAmbientTemperatureValues" /> + <java-symbol type="array" name="config_displayWhiteBalanceDisplayTemperatureValues" /> <java-symbol type="drawable" name="ic_action_open" /> </resources> diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 206682a1955b..4d2f005998c3 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -491,8 +491,10 @@ public class SettingsBackupTest { Settings.Global.GAME_DRIVER_ALL_APPS, Settings.Global.GAME_DRIVER_OPT_IN_APPS, Settings.Global.GAME_DRIVER_OPT_OUT_APPS, + Settings.Global.GAME_DRIVER_BLACKLISTS, Settings.Global.GAME_DRIVER_BLACKLIST, Settings.Global.GAME_DRIVER_WHITELIST, + Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX, Settings.Global.GPU_DEBUG_LAYER_APP, Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT, diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java index 3d59835a6719..7f104b1b0a14 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -409,8 +409,7 @@ public class ChooserActivityTest { @Test public void copyTextToClipboard() throws Exception { Intent sendIntent = createSendTextIntent(); - List<ResolvedComponentInfo> resolvedComponentInfos = - createResolvedComponentsForTestWithOtherProfile(1); + List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent( Mockito.anyBoolean(), @@ -421,6 +420,7 @@ public class ChooserActivityTest { .launchActivity(Intent.createChooser(sendIntent, null)); waitForIdle(); + onView(withId(R.id.copy_button)).check(matches(isDisplayed())); onView(withId(R.id.copy_button)).perform(click()); ClipboardManager clipboard = (ClipboardManager) activity.getSystemService( Context.CLIPBOARD_SERVICE); @@ -439,8 +439,9 @@ public class ChooserActivityTest { ArrayList<Uri> uris = new ArrayList<>(); uris.add(uri); - Intent sendIntent = createSendImageIntentWithPreview(uris); + Intent sendIntent = createSendUriIntentWithPreview(uris); sOverrides.previewThumbnail = createBitmap(); + sOverrides.isImageType = true; List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); @@ -464,8 +465,9 @@ public class ChooserActivityTest { uris.add(uri); uris.add(uri); - Intent sendIntent = createSendImageIntentWithPreview(uris); + Intent sendIntent = createSendUriIntentWithPreview(uris); sOverrides.previewThumbnail = createBitmap(); + sOverrides.isImageType = true; List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); @@ -492,8 +494,9 @@ public class ChooserActivityTest { uris.add(uri); uris.add(uri); - Intent sendIntent = createSendImageIntentWithPreview(uris); + Intent sendIntent = createSendUriIntentWithPreview(uris); sOverrides.previewThumbnail = createBitmap(); + sOverrides.isImageType = true; List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); @@ -538,12 +541,11 @@ public class ChooserActivityTest { ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class); mActivityRule.launchActivity(Intent.createChooser(sendIntent, "empty preview logger test")); waitForIdle(); - verify(mockLogger, Mockito.times(2)).write(logMakerCaptor.capture()); + + verify(mockLogger, Mockito.times(1)).write(logMakerCaptor.capture()); // First invocation is from onCreate - assertThat(logMakerCaptor.getAllValues().get(1).getCategory(), - is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW)); - assertThat(logMakerCaptor.getAllValues().get(1).getSubtype(), - is(CONTENT_PREVIEW_TEXT)); + assertThat(logMakerCaptor.getAllValues().get(0).getCategory(), + is(MetricsProto.MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN)); } @Test @@ -552,9 +554,17 @@ public class ChooserActivityTest { MetricsLogger mockLogger = sOverrides.metricsLogger; ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class); + + List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); + + when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent( + Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); + mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); waitForIdle(); - verify(mockLogger, Mockito.times(2)).write(logMakerCaptor.capture()); + verify(mockLogger, Mockito.times(3)).write(logMakerCaptor.capture()); // First invocation is from onCreate assertThat(logMakerCaptor.getAllValues().get(1).getCategory(), is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW)); @@ -570,8 +580,9 @@ public class ChooserActivityTest { ArrayList<Uri> uris = new ArrayList<>(); uris.add(uri); - Intent sendIntent = createSendImageIntentWithPreview(uris); + Intent sendIntent = createSendUriIntentWithPreview(uris); sOverrides.previewThumbnail = createBitmap(); + sOverrides.isImageType = true; List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); @@ -595,6 +606,51 @@ public class ChooserActivityTest { is(CONTENT_PREVIEW_IMAGE)); } + @Test + public void oneVisibleFilePreview() throws InterruptedException { + Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf"); + + ArrayList<Uri> uris = new ArrayList<>(); + uris.add(uri); + + Intent sendIntent = createSendUriIntentWithPreview(uris); + + List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); + + when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); + mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); + waitForIdle(); + onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed())); + onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf"))); + onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed())); + } + + + @Test + public void moreThanOneVisibleFilePreview() throws InterruptedException { + Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf"); + + ArrayList<Uri> uris = new ArrayList<>(); + uris.add(uri); + uris.add(uri); + uris.add(uri); + + Intent sendIntent = createSendUriIntentWithPreview(uris); + + List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); + + when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); + mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); + waitForIdle(); + onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed())); + onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf + 2 files"))); + onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed())); + } + private Intent createSendTextIntent() { Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); @@ -615,7 +671,7 @@ public class ChooserActivityTest { return sendIntent; } - private Intent createSendImageIntentWithPreview(ArrayList<Uri> uris) { + private Intent createSendUriIntentWithPreview(ArrayList<Uri> uris) { Intent sendIntent = new Intent(); if (uris.size() > 1) { diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java index f60467bd3df2..096b78b95fbd 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java @@ -89,11 +89,7 @@ public class ChooserWrapperActivity extends ChooserActivity { @Override protected boolean isImageType(String mimeType) { - if (sOverrides.previewThumbnail != null) { - return true; - } - - return super.isImageType(mimeType); + return sOverrides.isImageType; } @Override @@ -112,6 +108,7 @@ public class ChooserWrapperActivity extends ChooserActivity { public Function<TargetInfo, Boolean> onSafelyStartCallback; public ResolverListController resolverListController; public Boolean isVoiceInteraction; + public boolean isImageType; public Bitmap previewThumbnail; public MetricsLogger metricsLogger; @@ -120,6 +117,7 @@ public class ChooserWrapperActivity extends ChooserActivity { isVoiceInteraction = null; createPackageManager = null; previewThumbnail = null; + isImageType = false; resolverListController = mock(ResolverListController.class); metricsLogger = mock(MetricsLogger.class); } diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 56be05b51d1a..ef9255f66695 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -127,7 +127,10 @@ public class Typeface { static Typeface sDefaultTypeface; // Following two fields are not used but left for hiddenapi private list - @UnsupportedAppUsage + /** + * Use {@link SystemFonts#getAvailableFonts()} instead. + */ + @UnsupportedAppUsage(trackingBug = 123769347) static final Map<String, Typeface> sSystemFontMap; // We cannot support sSystemFallbackMap since we will migrate to public FontFamily API. diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java index 991847ad27fa..6ecb62140532 100644 --- a/graphics/java/android/graphics/drawable/GradientDrawable.java +++ b/graphics/java/android/graphics/drawable/GradientDrawable.java @@ -44,6 +44,7 @@ import android.graphics.RectF; import android.graphics.Shader; import android.graphics.SweepGradient; import android.graphics.Xfermode; +import android.os.Build; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; @@ -156,12 +157,12 @@ public class GradientDrawable extends Drawable { private static final float DEFAULT_INNER_RADIUS_RATIO = 3.0f; private static final float DEFAULT_THICKNESS_RATIO = 9.0f; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) private GradientState mGradientState; @UnsupportedAppUsage private final Paint mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124051827) private Rect mPadding; @UnsupportedAppUsage private Paint mStrokePaint; // optional, set by the caller @@ -670,7 +671,28 @@ public class GradientDrawable extends Drawable { * @see #setColor(int) */ public void setColors(@ColorInt int[] colors) { + setColors(colors, null); + } + + /** + * Sets the colors and offsets used to draw the gradient. + * <p> + * Each color is specified as an ARGB integer and the array must contain at + * least 2 colors. + * <p> + * <strong>Note</strong>: changing colors will affect all instances of a + * drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing the colors. + * + * @param colors an array containing 2 or more ARGB colors + * @param offsets optional array of floating point parameters representing the positions + * of the colors. Null evenly disperses the colors + * @see #mutate() + * @see #setColors(int[]) + */ + public void setColors(@ColorInt int[] colors, @Nullable float[] offsets) { mGradientState.setGradientColors(colors); + mGradientState.mPositions = offsets; mGradientIsDirty = true; invalidateSelf(); } @@ -849,6 +871,115 @@ public class GradientDrawable extends Drawable { } } + /** + * Inner radius of the ring expressed as a ratio of the ring's width. + * + * @see #getInnerRadiusRatio() + * @attr ref android.R.styleable#GradientDrawable_innerRadiusRatio + */ + public void setInnerRadiusRatio(float innerRadiusRatio) { + mGradientState.mInnerRadiusRatio = innerRadiusRatio; + mPathIsDirty = true; + invalidateSelf(); + } + + /** + * Return the inner radius of the ring expressed as a ratio of the ring's width. + * + * @see #setInnerRadiusRatio(float) + * @attr ref android.R.styleable#GradientDrawable_innerRadiusRatio + */ + public float getInnerRadiusRatio() { + return mGradientState.mInnerRadiusRatio; + } + + /** + * Configure the inner radius of the ring. + * + * @see #getInnerRadius() + * @attr ref android.R.styleable#GradientDrawable_innerRadius + */ + public void setInnerRadius(int innerRadius) { + mGradientState.mInnerRadius = innerRadius; + mPathIsDirty = true; + invalidateSelf(); + } + + /** + * Retrn the inner radius of the ring + * + * @see #setInnerRadius(int) + * @attr ref android.R.styleable#GradientDrawable_innerRadius + */ + public int getInnerRadius() { + return mGradientState.mInnerRadius; + } + + /** + * Configure the thickness of the ring expressed as a ratio of the ring's width. + * + * @see #getThicknessRatio() + * @attr ref android.R.styleable#GradientDrawable_thicknessRatio + */ + public void setThicknessRatio(float thicknessRatio) { + mGradientState.mThicknessRatio = thicknessRatio; + mPathIsDirty = true; + invalidateSelf(); + } + + /** + * Return the thickness ratio of the ring expressed as a ratio of the ring's width. + * + * @see #setThicknessRatio(float) + * @attr ref android.R.styleable#GradientDrawable_thicknessRatio + */ + public float getThicknessRatio() { + return mGradientState.mThicknessRatio; + } + + /** + * Configure the thickness of the ring. + * + * @attr ref android.R.styleable#GradientDrawable_thickness + */ + public void setThickness(int thickness) { + mGradientState.mThickness = thickness; + mPathIsDirty = true; + invalidateSelf(); + } + + /** + * Return the thickness of the ring + * + * @see #setThickness(int) + * @attr ref android.R.styleable#GradientDrawable_thickness + */ + public int getThickness() { + return mGradientState.mThickness; + } + + /** + * Configure the padding of the gradient shape + * @param left Left padding of the gradient shape + * @param top Top padding of the gradient shape + * @param right Right padding of the gradient shape + * @param bottom Bottom padding of the gradient shape + * + * @attr ref android.R.styleable#GradientDrawablePadding_left + * @attr ref android.R.styleable#GradientDrawablePadding_top + * @attr ref android.R.styleable#GradientDrawablePadding_right + * @attr ref android.R.styleable#GradientDrawablePadding_bottom + */ + public void setPadding(int left, int top, int right, int bottom) { + if (mGradientState.mPadding == null) { + mGradientState.mPadding = new Rect(); + } + + mGradientState.mPadding.set(left, top, right, bottom); + mPadding = mGradientState.mPadding; + invalidateSelf(); + } + private Path buildRing(GradientState st) { if (mRingPath != null && (!st.mUseLevelForShape || !mPathIsDirty)) return mRingPath; mPathIsDirty = false; @@ -1814,46 +1945,46 @@ public class GradientDrawable extends Drawable { final static class GradientState extends ConstantState { public @Config int mChangingConfigurations; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public @Shape int mShape = RECTANGLE; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public @GradientType int mGradient = LINEAR_GRADIENT; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public int mAngle = 0; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public Orientation mOrientation; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public ColorStateList mSolidColors; public ColorStateList mStrokeColors; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public @ColorInt int[] mGradientColors; public @ColorInt int[] mTempColors; // no need to copy public float[] mTempPositions; // no need to copy @UnsupportedAppUsage public float[] mPositions; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public int mStrokeWidth = -1; // if >= 0 use stroking. - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public float mStrokeDashWidth = 0.0f; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public float mStrokeDashGap = 0.0f; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public float mRadius = 0.0f; // use this if mRadiusArray is null - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public float[] mRadiusArray = null; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public Rect mPadding = null; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public int mWidth = -1; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public int mHeight = -1; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public float mInnerRadiusRatio = DEFAULT_INNER_RADIUS_RATIO; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050218) public float mThicknessRatio = DEFAULT_THICKNESS_RATIO; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917) public int mInnerRadius = -1; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050218) public int mThickness = -1; public boolean mDither = false; public Insets mOpticalInsets = Insets.NONE; diff --git a/libs/hwui/FrameMetricsObserver.h b/libs/hwui/FrameMetricsObserver.h index 237fc622dd2e..b93f07853242 100644 --- a/libs/hwui/FrameMetricsObserver.h +++ b/libs/hwui/FrameMetricsObserver.h @@ -23,7 +23,7 @@ namespace uirenderer { class FrameMetricsObserver : public VirtualLightRefBase { public: - virtual void notify(const int64_t* buffer); + virtual void notify(const int64_t* buffer) = 0; }; } // namespace uirenderer diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java index 59eff6401deb..a545f2edd1bf 100644 --- a/location/java/android/location/GnssMeasurement.java +++ b/location/java/android/location/GnssMeasurement.java @@ -96,7 +96,7 @@ public final class GnssMeasurement implements Parcelable { STATE_TOW_DECODED, STATE_MSEC_AMBIGUOUS, STATE_SYMBOL_SYNC, STATE_GLO_STRING_SYNC, STATE_GLO_TOD_DECODED, STATE_BDS_D2_BIT_SYNC, STATE_BDS_D2_SUBFRAME_SYNC, STATE_GAL_E1BC_CODE_LOCK, STATE_GAL_E1C_2ND_CODE_LOCK, STATE_GAL_E1B_PAGE_SYNC, - STATE_SBAS_SYNC, STATE_TOW_KNOWN, STATE_GLO_TOD_KNOWN + STATE_SBAS_SYNC, STATE_TOW_KNOWN, STATE_GLO_TOD_KNOWN, STATE_2ND_CODE_LOCK }) @Retention(RetentionPolicy.SOURCE) public @interface State {} @@ -144,6 +144,9 @@ public final class GnssMeasurement implements Parcelable { */ public static final int STATE_GLO_TOD_KNOWN = (1<<15); + /** This GNSS measurement's tracking state has secondary code lock. */ + public static final int STATE_2ND_CODE_LOCK = (1 << 16); + /** * All the GNSS receiver state flags, for bit masking purposes (not a sensible state for any * individual measurement.) @@ -517,6 +520,9 @@ public final class GnssMeasurement implements Parcelable { if ((mState & STATE_SBAS_SYNC) != 0) { builder.append("SbasSync|"); } + if ((mState & STATE_2ND_CODE_LOCK) != 0) { + builder.append("2ndCodeLock|"); + } int remainingStates = mState & ~STATE_ALL; if (remainingStates > 0) { @@ -531,96 +537,315 @@ public final class GnssMeasurement implements Parcelable { /** * Gets the received GNSS satellite time, at the measurement time, in nanoseconds. * - * <p>For GPS & QZSS, this is: - * <ul> - * <li>Received GPS Time-of-Week at the measurement time, in nanoseconds.</li> - * <li>The value is relative to the beginning of the current GPS week.</li> - * </ul> - * - * <p>Given the highest sync state that can be achieved, per each satellite, valid range - * for this field can be: - * <pre> - * Searching : [ 0 ] : STATE_UNKNOWN - * C/A code lock : [ 0 1ms ] : STATE_CODE_LOCK is set - * Bit sync : [ 0 20ms ] : STATE_BIT_SYNC is set - * Subframe sync : [ 0 6s ] : STATE_SUBFRAME_SYNC is set - * TOW decoded : [ 0 1week ] : STATE_TOW_DECODED is set - * TOW Known : [ 0 1week ] : STATE_TOW_KNOWN set</pre> - * - * Note: TOW Known refers to the case where TOW is possibly not decoded over the air but has + * <p>The received satellite time is relative to the beginning of the system week for all + * constellations except for Glonass where it is relative to the beginning of the Glonass + * system day. + * + * <p>The table below indicates the valid range of the received GNSS satellite time. These + * ranges depend on the constellation and code being tracked and the state of the tracking + * algorithms given by the {@link #getState} method. The minimum value of this field is zero. + * The maximum value of this field is determined by looking across all of the state flags + * that are set, for the given constellation and code type, and finding the the maximum value + * in this table. + * + * <p>For example, for GPS L1 C/A, if STATE_TOW_KNOWN is set, this field can be any value from 0 + * to 1 week (in nanoseconds), and for GAL E1B code, if only STATE_GAL_E1BC_CODE_LOCK is set, + * then this field can be any value from 0 to 4 milliseconds (in nanoseconds.) + * + * <table border="1"> + * <thead> + * <tr> + * <td /> + * <td colspan="3"><strong>GPS/QZSS</strong></td> + * <td><strong>GLNS</strong></td> + * <td colspan="2"><strong>BDS</strong></td> + * <td colspan="3"><strong>GAL</strong></td> + * <td><strong>SBAS</strong></td> + * </tr> + * <tr> + * <td><strong>State Flag</strong></td> + * <td><strong>L1 C/A</strong></td> + * <td><strong>L5I</strong></td> + * <td><strong>L5Q</strong></td> + * <td><strong>L1OF</strong></td> + * <td><strong>B1I (D1)</strong></td> + * <td><strong>B1I (D2)</strong></td> + * <td><strong>E1B</strong></td> + * <td><strong>E1C</strong></td> + * <td><strong>E5AQ</strong></td> + * <td><strong>L1 C/A</strong></td> + * </tr> + * </thead> + * <tbody> + * <tr> + * <td> + * <strong>STATE_UNKNOWN</strong> + * </td> + * <td>0</td> + * <td>0</td> + * <td>0</td> + * <td>0</td> + * <td>0</td> + * <td>0</td> + * <td>0</td> + * <td>0</td> + * <td>0</td> + * <td>0</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_CODE_LOCK</strong> + * </td> + * <td>1 ms</td> + * <td>1 ms</td> + * <td>1 ms</td> + * <td>1 ms</td> + * <td>1 ms</td> + * <td>1 ms</td> + * <td>-</td> + * <td>-</td> + * <td>1 ms</td> + * <td>1 ms</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_SYMBOL_SYNC</strong> + * </td> + * <td>20 ms (optional)</td> + * <td>10 ms</td> + * <td>1 ms (optional)</td> + * <td>10 ms</td> + * <td>20 ms (optional)</td> + * <td>2 ms</td> + * <td>4 ms (optional)</td> + * <td>4 ms (optional)</td> + * <td>1 ms (optional)</td> + * <td>2 ms</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_BIT_SYNC</strong> + * </td> + * <td>20 ms</td> + * <td>20 ms</td> + * <td>1 ms (optional)</td> + * <td>20 ms</td> + * <td>20 ms</td> + * <td>-</td> + * <td>8 ms</td> + * <td>-</td> + * <td>1 ms (optional)</td> + * <td>4 ms</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_SUBFRAME_SYNC</strong> + * </td> + * <td>6s</td> + * <td>6s</td> + * <td>-</td> + * <td>2 s</td> + * <td>6 s</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>100 ms</td> + * <td>-</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_TOW_DECODED</strong> + * </td> + * <td colspan="2">1 week</td> + * <td>-</td> + * <td>1 day</td> + * <td colspan="2">1 week</td> + * <td colspan="2">1 week</td> + * <td>-</td> + * <td>1 week</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_TOW_KNOWN</strong> + * </td> + * <td colspan="3">1 week</td> + * <td>1 day</td> + * <td colspan="2">1 week</td> + * <td colspan="3">1 week</td> + * <td>1 week</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_GLO_STRING_SYNC</strong> + * </td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>2 s</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_GLO_TOD_DECODED</strong> + * </td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>1 day</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_GLO_TOD_KNOWN</strong> + * </td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>1 day</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_BDS_D2_BIT_SYNC</strong> + * </td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>2 ms</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_BDS_D2_SUBFRAME_SYNC</strong> + * </td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>600 ms</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_GAL_E1BC_CODE_LOCK</strong> + * </td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>4 ms</td> + * <td>4 ms</td> + * <td>-</td> + * <td>-</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_GAL_E1C_2ND_CODE_LOCK</strong> + * </td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>100 ms</td> + * <td>-</td> + * <td>-</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_2ND_CODE_LOCK</strong> + * </td> + * <td>-</td> + * <td>10 ms (optional)</td> + * <td>20 ms</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>100 ms (optional)</td> + * <td>100 ms</td> + * <td>-</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_GAL_E1B_PAGE_SYNC</strong> + * </td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>2 s</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * </tr> + * <tr> + * <td> + * <strong>STATE_SBAS_SYNC</strong> + * </td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>-</td> + * <td>1 s</td> + * </tr> + * </tbody> + * </table> + * + * <p>Note: TOW Known refers to the case where TOW is possibly not decoded over the air but has * been determined from other sources. If TOW decoded is set then TOW Known must also be set. * - * <p>Note well: if there is any ambiguity in integer millisecond, {@code STATE_MSEC_AMBIGUOUS} - * must be set accordingly, in the 'state' field. - * - * <p>This value must be populated if 'state' != {@code STATE_UNKNOWN}. + * <p>Note well: if there is any ambiguity in integer millisecond, STATE_MSEC_AMBIGUOUS must be + * set accordingly, in the 'state' field. This value must be populated, unless the 'state' == + * STATE_UNKNOWN. * - * <p>For Glonass, this is: + * <p>Note on optional flags: * <ul> - * <li>Received Glonass time of day, at the measurement time in nanoseconds.</li> + * <li> For L1 C/A and B1I, STATE_SYMBOL_SYNC is optional since the symbol length is the + * same as the bit length. + * <li> For L5Q and E5aQ, STATE_BIT_SYNC and STATE_SYMBOL_SYNC are optional since they are + * implied by STATE_CODE_LOCK. + * <li> STATE_2ND_CODE_LOCK for L5I is optional since it is implied by STATE_SYMBOL_SYNC. + * <li> STATE_2ND_CODE_LOCK for E1C is optional since it is implied by + * STATE_GAL_E1C_2ND_CODE_LOCK. + * <li> For E1B and E1C, STATE_SYMBOL_SYNC is optional, because it is implied by + * STATE_GAL_E1BC_CODE_LOCK. * </ul> - * - * <p>Given the highest sync state that can be achieved, per each satellite, valid range for - * this field can be: - * <pre> - * Searching : [ 0 ] : STATE_UNKNOWN - * C/A code lock : [ 0 1ms ] : STATE_CODE_LOCK is set - * Symbol sync : [ 0 10ms ] : STATE_SYMBOL_SYNC is set - * Bit sync : [ 0 20ms ] : STATE_BIT_SYNC is set - * String sync : [ 0 2s ] : STATE_GLO_STRING_SYNC is set - * Time of day decoded : [ 0 1day ] : STATE_GLO_TOD_DECODED is set - * Time of day known : [ 0 1day ] : STATE_GLO_TOD_KNOWN set</pre> - * - * Note: Time of day known refers to the case where it is possibly not decoded over the air but - * has been determined from other sources. If Time of day decoded is set then Time of day known - * must also be set. - * - * <p>For Beidou, this is: - * <ul> - * <li>Received Beidou time of week, at the measurement time in nanoseconds.</li> - * </ul> - * - * <p>Given the highest sync state that can be achieved, per each satellite, valid range for - * this field can be: - * <pre> - * Searching : [ 0 ] : STATE_UNKNOWN - * C/A code lock : [ 0 1ms ] : STATE_CODE_LOCK is set - * Bit sync (D2) : [ 0 2ms ] : STATE_BDS_D2_BIT_SYNC is set - * Bit sync (D1) : [ 0 20ms ] : STATE_BIT_SYNC is set - * Subframe (D2) : [ 0 0.6s ] : STATE_BDS_D2_SUBFRAME_SYNC is set - * Subframe (D1) : [ 0 6s ] : STATE_SUBFRAME_SYNC is set - * Time of week decoded : [ 0 1week ] : STATE_TOW_DECODED is set - * Time of week known : [ 0 1week ] : STATE_TOW_KNOWN set</pre> - * - * Note: TOW Known refers to the case where TOW is possibly not decoded over the air but has - * been determined from other sources. If TOW decoded is set then TOW Known must also be set. - * - * <p>For Galileo, this is: - * <ul> - * <li>Received Galileo time of week, at the measurement time in nanoseconds.</li> - * </ul> - * <pre> - * E1BC code lock : [ 0 4ms ] : STATE_GAL_E1BC_CODE_LOCK is set - * E1C 2nd code lock : [ 0 100ms ] : STATE_GAL_E1C_2ND_CODE_LOCK is set - * E1B page : [ 0 2s ] : STATE_GAL_E1B_PAGE_SYNC is set - * Time of week decoded : [ 0 1week ] : STATE_TOW_DECODED is set - * Time of week known : [ 0 1week ] : STATE_TOW_KNOWN set</pre> - * - * Note: TOW Known refers to the case where TOW is possibly not decoded over the air but has - * been determined from other sources. If TOW decoded is set then TOW Known must also be set. - * - * <p>For SBAS, this is: - * <ul> - * <li>Received SBAS time, at the measurement time in nanoseconds.</li> - * </ul> - * - * <p>Given the highest sync state that can be achieved, per each satellite, valid range for - * this field can be: - * <pre> - * Searching : [ 0 ] : STATE_UNKNOWN - * C/A code lock : [ 0 1ms ] : STATE_CODE_LOCK is set - * Symbol sync : [ 0 2ms ] : STATE_SYMBOL_SYNC is set - * Message : [ 0 1s ] : STATE_SBAS_SYNC is set</pre> */ public long getReceivedSvTimeNanos() { return mReceivedSvTimeNanos; diff --git a/media/Android.bp b/media/Android.bp index 141d415cbaf3..86dc509501a4 100644 --- a/media/Android.bp +++ b/media/Android.bp @@ -3,7 +3,6 @@ java_library { srcs: [ ":updatable-media-srcs", - ":framework-media-annotation-srcs", ], aidl: { @@ -28,7 +27,12 @@ java_library { installable: true, // Make sure that the implementaion only relies on SDK or system APIs. - sdk_version: "system_current", + no_framework_libs: true, + libs: [ + // The order matters. android_system_* library should come later. + "framework_media_annotation", + "android_system_stubs_current", + ], } filegroup { @@ -125,3 +129,8 @@ java_library { sdk_version: "28", } +java_library { + name: "framework_media_annotation", + srcs: [":framework-media-annotation-srcs"], + installable: false, +} diff --git a/media/apex/java/android/media/MediaPlayer2.java b/media/apex/java/android/media/MediaPlayer2.java index c38a83131a6c..89a954061958 100644 --- a/media/apex/java/android/media/MediaPlayer2.java +++ b/media/apex/java/android/media/MediaPlayer2.java @@ -45,8 +45,8 @@ import android.util.Size; import android.view.Surface; import android.view.SurfaceHolder; -import com.android.framework.protobuf.InvalidProtocolBufferException; import com.android.internal.annotations.GuardedBy; +import com.android.media.protobuf.InvalidProtocolBufferException; import java.io.ByteArrayOutputStream; import java.io.File; diff --git a/media/proto/jarjar-rules.txt b/media/proto/jarjar-rules.txt index 7be6e732d3fd..bfb0b2782486 100644 --- a/media/proto/jarjar-rules.txt +++ b/media/proto/jarjar-rules.txt @@ -1,2 +1,2 @@ -rule com.google.protobuf.** com.android.framework.protobuf.@1 +rule com.google.protobuf.** com.android.media.protobuf.@1 diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index 730c409a91fb..a3db2d6a5055 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -146,6 +146,7 @@ LIBANDROID { AHardwareBuffer_getNativeHandle; # introduced=26 AHardwareBuffer_isSupported; # introduced=29 AHardwareBuffer_lock; # introduced=26 + AHardwareBuffer_lockPlanes; # introduced=29 AHardwareBuffer_recvHandleFromUnixSocket; # introduced=26 AHardwareBuffer_release; # introduced=26 AHardwareBuffer_sendHandleToUnixSocket; # introduced=26 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 2dba1d5bc257..c5a951ca5249 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -221,30 +221,6 @@ public class CarStatusBar extends StatusBar implements } } - @Override - public void destroy() { - mCarBatteryController.stopListening(); - mConnectedDeviceSignalController.stopListening(); - mActivityManagerWrapper.unregisterTaskStackListener(mTaskStackListener); - mDrivingStateHelper.disconnectFromCarService(); - - if (mNavigationBarWindow != null) { - mWindowManager.removeViewImmediate(mNavigationBarWindow); - mNavigationBarView = null; - } - - if (mLeftNavigationBarWindow != null) { - mWindowManager.removeViewImmediate(mLeftNavigationBarWindow); - mLeftNavigationBarView = null; - } - - if (mRightNavigationBarWindow != null) { - mWindowManager.removeViewImmediate(mRightNavigationBarWindow); - mRightNavigationBarView = null; - } - super.destroy(); - } - @Override protected void makeStatusBarView() { diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java index f372fe55dfb0..24fa87a7f39b 100644 --- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java +++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java @@ -15,11 +15,10 @@ */ package android.ext.services.notification; -import android.annotation.NonNull; -import android.annotation.Nullable; import android.app.Notification; import android.app.Person; import android.app.RemoteAction; +import android.app.RemoteInput; import android.content.Context; import android.graphics.drawable.Icon; import android.os.Bundle; @@ -27,7 +26,9 @@ import android.os.Parcelable; import android.os.Process; import android.service.notification.NotificationAssistantService; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.LruCache; +import android.util.Pair; import android.view.textclassifier.ConversationAction; import android.view.textclassifier.ConversationActions; import android.view.textclassifier.TextClassificationContext; @@ -35,6 +36,8 @@ import android.view.textclassifier.TextClassificationManager; import android.view.textclassifier.TextClassifier; import android.view.textclassifier.TextClassifierEvent; +import com.android.internal.util.ArrayUtils; + import java.time.Instant; import java.time.ZoneOffset; import java.time.ZonedDateTime; @@ -43,11 +46,13 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Deque; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; public class SmartActionsHelper { private static final String KEY_ACTION_TYPE = "action_type"; + private static final String KEY_ACTION_SCORE = "action_score"; // If a notification has any of these flags set, it's inelgibile for actions being added. private static final int FLAG_MASK_INELGIBILE_FOR_ACTIONS = Notification.FLAG_ONGOING_EVENT @@ -58,75 +63,136 @@ public class SmartActionsHelper { private static final List<String> HINTS = Collections.singletonList(ConversationActions.Request.HINT_FOR_NOTIFICATION); + private static final ConversationActions EMPTY_CONVERSATION_ACTIONS = + new ConversationActions(Collections.emptyList(), null); private Context mContext; - @Nullable private TextClassifier mTextClassifier; - @NonNull private AssistantSettings mSettings; - private LruCache<String, String> mNotificationKeyToResultIdCache = - new LruCache<>(MAX_RESULT_ID_TO_CACHE); + private LruCache<String, Session> mSessionCache = new LruCache<>(MAX_RESULT_ID_TO_CACHE); SmartActionsHelper(Context context, AssistantSettings settings) { mContext = context; TextClassificationManager textClassificationManager = mContext.getSystemService(TextClassificationManager.class); - if (textClassificationManager != null) { - mTextClassifier = textClassificationManager.getTextClassifier(); - } + mTextClassifier = textClassificationManager.getTextClassifier(); mSettings = settings; } - SmartSuggestions suggest(@NonNull NotificationEntry entry) { + SmartSuggestions suggest(NotificationEntry entry) { // Whenever suggest() is called on a notification, its previous session is ended. - mNotificationKeyToResultIdCache.remove(entry.getSbn().getKey()); + mSessionCache.remove(entry.getSbn().getKey()); boolean eligibleForReplyAdjustment = mSettings.mGenerateReplies && isEligibleForReplyAdjustment(entry); boolean eligibleForActionAdjustment = mSettings.mGenerateActions && isEligibleForActionAdjustment(entry); - List<ConversationAction> conversationActions = + ConversationActions conversationActionsResult = suggestConversationActions( entry, eligibleForReplyAdjustment, eligibleForActionAdjustment); - ArrayList<CharSequence> replies = conversationActions.stream() - .map(ConversationAction::getTextReply) - .filter(textReply -> !TextUtils.isEmpty(textReply)) - .collect(Collectors.toCollection(ArrayList::new)); + String resultId = conversationActionsResult.getId(); + List<ConversationAction> conversationActions = + conversationActionsResult.getConversationActions(); + + ArrayList<CharSequence> replies = new ArrayList<>(); + Map<CharSequence, Float> repliesScore = new ArrayMap<>(); + for (ConversationAction conversationAction : conversationActions) { + CharSequence textReply = conversationAction.getTextReply(); + if (TextUtils.isEmpty(textReply)) { + continue; + } + replies.add(textReply); + repliesScore.put(textReply, conversationAction.getConfidenceScore()); + } ArrayList<Notification.Action> actions = conversationActions.stream() .filter(conversationAction -> conversationAction.getAction() != null) - .map(action -> createNotificationAction(action.getAction(), action.getType())) + .map(action -> createNotificationAction( + action.getAction(), action.getType(), action.getConfidenceScore())) .collect(Collectors.toCollection(ArrayList::new)); + + // Start a new session for logging if necessary. + if (!TextUtils.isEmpty(resultId) + && !conversationActions.isEmpty() + && suggestionsMightBeUsedInNotification( + entry, !actions.isEmpty(), !replies.isEmpty())) { + mSessionCache.put(entry.getSbn().getKey(), new Session(resultId, repliesScore)); + } + return new SmartSuggestions(replies, actions); } /** + * Returns whether the suggestion might be used in the notifications in SysUI. + * <p> + * Currently, NAS has no idea if suggestions will actually be used in the notification, and thus + * this function tries to make a heuristic. This function tries to optimize the precision, + * that means when it is unsure, it will return false. The objective is to avoid false positive, + * which could pollute the log and CTR as we are logging click rate of suggestions that could + * be never visible to users. On the other hand, it is fine to have false negative because + * it would be just like sampling. + */ + private boolean suggestionsMightBeUsedInNotification( + NotificationEntry notificationEntry, boolean hasSmartAction, boolean hasSmartReply) { + Notification notification = notificationEntry.getNotification(); + boolean hasAppGeneratedContextualActions = !notification.getContextualActions().isEmpty(); + + Pair<RemoteInput, Notification.Action> freeformRemoteInputAndAction = + notification.findRemoteInputActionPair(/* requiresFreeform */ true); + boolean hasAppGeneratedReplies = false; + boolean allowGeneratedReplies = false; + if (freeformRemoteInputAndAction != null) { + RemoteInput freeformRemoteInput = freeformRemoteInputAndAction.first; + Notification.Action actionWithFreeformRemoteInput = freeformRemoteInputAndAction.second; + hasAppGeneratedReplies = !ArrayUtils.isEmpty(freeformRemoteInput.getChoices()); + allowGeneratedReplies = actionWithFreeformRemoteInput.getAllowGeneratedReplies(); + } + + if (hasAppGeneratedReplies || hasAppGeneratedContextualActions) { + return false; + } + return hasSmartAction && notification.getAllowSystemGeneratedContextualActions() + || hasSmartReply && allowGeneratedReplies; + } + + private void reportActionsGenerated( + String resultId, List<ConversationAction> conversationActions) { + if (TextUtils.isEmpty(resultId)) { + return; + } + TextClassifierEvent textClassifierEvent = + createTextClassifierEventBuilder( + TextClassifierEvent.TYPE_ACTIONS_GENERATED, resultId) + .setEntityTypes(conversationActions.stream() + .map(ConversationAction::getType) + .toArray(String[]::new)) + .build(); + mTextClassifier.onTextClassifierEvent(textClassifierEvent); + } + + /** * Adds action adjustments based on the notification contents. */ - @NonNull - private List<ConversationAction> suggestConversationActions( - @NonNull NotificationEntry entry, + private ConversationActions suggestConversationActions( + NotificationEntry entry, boolean includeReplies, boolean includeActions) { if (!includeReplies && !includeActions) { - return Collections.emptyList(); - } - if (mTextClassifier == null) { - return Collections.emptyList(); + return EMPTY_CONVERSATION_ACTIONS; } List<ConversationActions.Message> messages = extractMessages(entry.getNotification()); if (messages.isEmpty()) { - return Collections.emptyList(); + return EMPTY_CONVERSATION_ACTIONS; } // Do not generate smart actions if the last message is from the local user. ConversationActions.Message lastMessage = messages.get(messages.size() - 1); if (arePersonsEqual( ConversationActions.Message.PERSON_USER_SELF, lastMessage.getAuthor())) { - return Collections.emptyList(); + return EMPTY_CONVERSATION_ACTIONS; } TextClassifier.EntityConfig.Builder typeConfigBuilder = @@ -146,25 +212,20 @@ public class SmartActionsHelper { .setHints(HINTS) .setTypeConfig(typeConfigBuilder.build()) .build(); - - ConversationActions conversationActionsResult = + ConversationActions conversationActions = mTextClassifier.suggestConversationActions(request); - - String resultId = conversationActionsResult.getId(); - if (!TextUtils.isEmpty(resultId) - && !conversationActionsResult.getConversationActions().isEmpty()) { - mNotificationKeyToResultIdCache.put(entry.getSbn().getKey(), resultId); - } - return conversationActionsResult.getConversationActions(); + reportActionsGenerated( + conversationActions.getId(), conversationActions.getConversationActions()); + return conversationActions; } - void onNotificationExpansionChanged(@NonNull NotificationEntry entry, boolean isUserAction, + void onNotificationExpansionChanged(NotificationEntry entry, boolean isUserAction, boolean isExpanded) { if (!isExpanded) { return; } - String resultId = mNotificationKeyToResultIdCache.get(entry.getSbn().getKey()); - if (resultId == null) { + Session session = mSessionCache.get(entry.getSbn().getKey()); + if (session == null) { return; } // Only report if this is the first time the user sees these suggestions. @@ -173,56 +234,50 @@ public class SmartActionsHelper { } entry.setShowActionEventLogged(); TextClassifierEvent textClassifierEvent = - createTextClassifierEventBuilder(TextClassifierEvent.TYPE_ACTIONS_SHOWN, - resultId) + createTextClassifierEventBuilder( + TextClassifierEvent.TYPE_ACTIONS_SHOWN, session.resultId) .build(); // TODO: If possible, report which replies / actions are actually seen by user. mTextClassifier.onTextClassifierEvent(textClassifierEvent); } - void onNotificationDirectReplied(@NonNull String key) { - if (mTextClassifier == null) { - return; - } - String resultId = mNotificationKeyToResultIdCache.get(key); - if (resultId == null) { + void onNotificationDirectReplied(String key) { + Session session = mSessionCache.get(key); + if (session == null) { return; } TextClassifierEvent textClassifierEvent = - createTextClassifierEventBuilder(TextClassifierEvent.TYPE_MANUAL_REPLY, resultId) + createTextClassifierEventBuilder( + TextClassifierEvent.TYPE_MANUAL_REPLY, session.resultId) .build(); mTextClassifier.onTextClassifierEvent(textClassifierEvent); } - void onSuggestedReplySent(@NonNull String key, @NonNull CharSequence reply, + void onSuggestedReplySent(String key, CharSequence reply, @NotificationAssistantService.Source int source) { - if (mTextClassifier == null) { - return; - } if (source != NotificationAssistantService.SOURCE_FROM_ASSISTANT) { return; } - String resultId = mNotificationKeyToResultIdCache.get(key); - if (resultId == null) { + Session session = mSessionCache.get(key); + if (session == null) { return; } TextClassifierEvent textClassifierEvent = - createTextClassifierEventBuilder(TextClassifierEvent.TYPE_SMART_ACTION, resultId) + createTextClassifierEventBuilder( + TextClassifierEvent.TYPE_SMART_ACTION, session.resultId) .setEntityTypes(ConversationAction.TYPE_TEXT_REPLY) + .setScore(session.repliesScores.getOrDefault(reply, 0f)) .build(); mTextClassifier.onTextClassifierEvent(textClassifierEvent); } - void onActionClicked(@NonNull String key, @NonNull Notification.Action action, + void onActionClicked(String key, Notification.Action action, @NotificationAssistantService.Source int source) { - if (mTextClassifier == null) { - return; - } if (source != NotificationAssistantService.SOURCE_FROM_ASSISTANT) { return; } - String resultId = mNotificationKeyToResultIdCache.get(key); - if (resultId == null) { + Session session = mSessionCache.get(key); + if (session == null) { return; } String actionType = action.getExtras().getString(KEY_ACTION_TYPE); @@ -230,28 +285,32 @@ public class SmartActionsHelper { return; } TextClassifierEvent textClassifierEvent = - createTextClassifierEventBuilder(TextClassifierEvent.TYPE_SMART_ACTION, resultId) + createTextClassifierEventBuilder( + TextClassifierEvent.TYPE_SMART_ACTION, session.resultId) .setEntityTypes(actionType) .build(); mTextClassifier.onTextClassifierEvent(textClassifierEvent); } private Notification.Action createNotificationAction( - RemoteAction remoteAction, String actionType) { + RemoteAction remoteAction, String actionType, float score) { Icon icon = remoteAction.shouldShowIcon() ? remoteAction.getIcon() : Icon.createWithResource(mContext, com.android.internal.R.drawable.ic_action_open); + Bundle extras = new Bundle(); + extras.putString(KEY_ACTION_TYPE, actionType); + extras.putFloat(KEY_ACTION_SCORE, score); return new Notification.Action.Builder( icon, remoteAction.getTitle(), remoteAction.getActionIntent()) .setContextual(true) - .addExtras(Bundle.forPair(KEY_ACTION_TYPE, actionType)) + .addExtras(extras) .build(); } private TextClassifierEvent.Builder createTextClassifierEventBuilder( - int eventType, @NonNull String resultId) { + int eventType, String resultId) { return new TextClassifierEvent.Builder( TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS, eventType) .setEventTime(System.currentTimeMillis()) @@ -269,7 +328,7 @@ public class SmartActionsHelper { * to fundamental phone functionality where any error would result in a very negative user * experience. */ - private boolean isEligibleForActionAdjustment(@NonNull NotificationEntry entry) { + private boolean isEligibleForActionAdjustment(NotificationEntry entry) { Notification notification = entry.getNotification(); String pkg = entry.getSbn().getPackageName(); if (!Process.myUserHandle().equals(entry.getSbn().getUser())) { @@ -285,7 +344,7 @@ public class SmartActionsHelper { return entry.isMessaging(); } - private boolean isEligibleForReplyAdjustment(@NonNull NotificationEntry entry) { + private boolean isEligibleForReplyAdjustment(NotificationEntry entry) { if (!Process.myUserHandle().equals(entry.getSbn().getUser())) { return false; } @@ -306,8 +365,7 @@ public class SmartActionsHelper { } /** Returns the text most salient for action extraction in a notification. */ - @Nullable - private List<ConversationActions.Message> extractMessages(@NonNull Notification notification) { + private List<ConversationActions.Message> extractMessages(Notification notification) { Parcelable[] messages = notification.extras.getParcelableArray(Notification.EXTRA_MESSAGES); if (messages == null || messages.length == 0) { return Collections.singletonList(new ConversationActions.Message.Builder( @@ -343,7 +401,7 @@ public class SmartActionsHelper { return new ArrayList<>(extractMessages); } - private static boolean arePersonsEqual(@NonNull Person left, @NonNull Person right) { + private static boolean arePersonsEqual(Person left, Person right) { return Objects.equals(left.getKey(), right.getKey()) && Objects.equals(left.getName(), right.getName()) && Objects.equals(left.getUri(), right.getUri()); @@ -359,4 +417,14 @@ public class SmartActionsHelper { this.actions = actions; } } + + private static class Session { + public final String resultId; + public final Map<CharSequence, Float> repliesScores; + + Session(String resultId, Map<CharSequence, Float> repliesScores) { + this.resultId = resultId; + this.repliesScores = repliesScores; + } + } } diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java index 74c20fc09df2..d0b6d0061166 100644 --- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java +++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java @@ -19,7 +19,9 @@ import static com.google.common.truth.Truth.assertAbout; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -53,8 +55,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.time.Instant; @@ -71,9 +73,12 @@ import javax.annotation.Nullable; public class SmartActionsHelperTest { private static final String NOTIFICATION_KEY = "key"; private static final String RESULT_ID = "id"; + private static final float SCORE = 0.7f; + private static final CharSequence SMART_REPLY = "Home"; private static final ConversationAction REPLY_ACTION = new ConversationAction.Builder(ConversationAction.TYPE_TEXT_REPLY) - .setTextReply("Home") + .setTextReply(SMART_REPLY) + .setConfidenceScore(SCORE) .build(); private static final String MESSAGE = "Where are you?"; @@ -197,8 +202,16 @@ public class SmartActionsHelperTest { List<ConversationActions.Message> messages = runSuggestAndCaptureRequest().getConversation(); + assertThat(messages).hasSize(1); MessageSubject.assertThat(messages.get(0)).hasText(MESSAGE); + ArgumentCaptor<TextClassifierEvent> argumentCaptor = + ArgumentCaptor.forClass(TextClassifierEvent.class); + verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture()); + TextClassifierEvent textClassifierEvent = argumentCaptor.getValue(); + assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_GENERATED); + assertThat(textClassifierEvent.getEntityTypes()).asList() + .containsExactly(ConversationAction.TYPE_TEXT_REPLY); } @Test @@ -249,6 +262,14 @@ public class SmartActionsHelperTest { MessageSubject.assertThat(fourthMessage).hasPerson(userB); MessageSubject.assertThat(fourthMessage) .hasReferenceTime(createZonedDateTimeFromMsUtc(4000)); + + ArgumentCaptor<TextClassifierEvent> argumentCaptor = + ArgumentCaptor.forClass(TextClassifierEvent.class); + verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture()); + TextClassifierEvent textClassifierEvent = argumentCaptor.getValue(); + assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_GENERATED); + assertThat(textClassifierEvent.getEntityTypes()).asList() + .containsExactly(ConversationAction.TYPE_TEXT_REPLY); } @Test @@ -299,13 +320,15 @@ public class SmartActionsHelperTest { mSmartActionsHelper.suggest(createNotificationEntry()); mSmartActionsHelper.onSuggestedReplySent( - NOTIFICATION_KEY, MESSAGE, NotificationAssistantService.SOURCE_FROM_ASSISTANT); + NOTIFICATION_KEY, SMART_REPLY, NotificationAssistantService.SOURCE_FROM_ASSISTANT); ArgumentCaptor<TextClassifierEvent> argumentCaptor = ArgumentCaptor.forClass(TextClassifierEvent.class); - verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture()); - TextClassifierEvent textClassifierEvent = argumentCaptor.getValue(); - assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_SMART_ACTION); + verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture()); + List<TextClassifierEvent> events = argumentCaptor.getAllValues(); + assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED); + assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_SMART_ACTION); + assertThat(events.get(1).getScore()).isEqualTo(SCORE); } @Test @@ -317,24 +340,22 @@ public class SmartActionsHelperTest { mSmartActionsHelper.onSuggestedReplySent( "something_else", MESSAGE, NotificationAssistantService.SOURCE_FROM_ASSISTANT); - verify(mTextClassifier, never()) - .onTextClassifierEvent(Mockito.any(TextClassifierEvent.class)); + verify(mTextClassifier, never()).onTextClassifierEvent( + argThat(new TextClassifierEventMatcher(TextClassifierEvent.TYPE_SMART_ACTION))); } @Test public void testOnSuggestedReplySent_missingResultId() { when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class))) - .thenReturn(new ConversationActions(Collections.emptyList(), null)); - + .thenReturn(new ConversationActions(Collections.singletonList(REPLY_ACTION), null)); Notification notification = createMessageNotification(); when(mStatusBarNotification.getNotification()).thenReturn(notification); mSmartActionsHelper.suggest(createNotificationEntry()); mSmartActionsHelper.onSuggestedReplySent( - "something_else", MESSAGE, NotificationAssistantService.SOURCE_FROM_ASSISTANT); + NOTIFICATION_KEY, SMART_REPLY, NotificationAssistantService.SOURCE_FROM_ASSISTANT); - verify(mTextClassifier, never()) - .onTextClassifierEvent(Mockito.any(TextClassifierEvent.class)); + verify(mTextClassifier, never()).onTextClassifierEvent(any(TextClassifierEvent.class)); } @Test @@ -347,9 +368,10 @@ public class SmartActionsHelperTest { ArgumentCaptor<TextClassifierEvent> argumentCaptor = ArgumentCaptor.forClass(TextClassifierEvent.class); - verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture()); - TextClassifierEvent textClassifierEvent = argumentCaptor.getValue(); - assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_MANUAL_REPLY); + verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture()); + List<TextClassifierEvent> events = argumentCaptor.getAllValues(); + assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED); + assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_MANUAL_REPLY); } @Test @@ -362,9 +384,10 @@ public class SmartActionsHelperTest { ArgumentCaptor<TextClassifierEvent> argumentCaptor = ArgumentCaptor.forClass(TextClassifierEvent.class); - verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture()); - TextClassifierEvent textClassifierEvent = argumentCaptor.getValue(); - assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_SHOWN); + verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture()); + List<TextClassifierEvent> events = argumentCaptor.getAllValues(); + assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED); + assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_ACTIONS_SHOWN); } @Test @@ -376,7 +399,7 @@ public class SmartActionsHelperTest { mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), false, false); verify(mTextClassifier, never()).onTextClassifierEvent( - Mockito.any(TextClassifierEvent.class)); + argThat(new TextClassifierEventMatcher(TextClassifierEvent.TYPE_ACTIONS_SHOWN))); } @Test @@ -389,9 +412,10 @@ public class SmartActionsHelperTest { ArgumentCaptor<TextClassifierEvent> argumentCaptor = ArgumentCaptor.forClass(TextClassifierEvent.class); - verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture()); - TextClassifierEvent textClassifierEvent = argumentCaptor.getValue(); - assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_SHOWN); + verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture()); + List<TextClassifierEvent> events = argumentCaptor.getAllValues(); + assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED); + assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_ACTIONS_SHOWN); } private ZonedDateTime createZonedDateTimeFromMsUtc(long msUtc) { @@ -490,4 +514,21 @@ public class SmartActionsHelperTest { return assertAbout(FACTORY).that(message); } } + + private final class TextClassifierEventMatcher implements ArgumentMatcher<TextClassifierEvent> { + + private int mType; + + private TextClassifierEventMatcher(int type) { + mType = type; + } + + @Override + public boolean matches(TextClassifierEvent textClassifierEvent) { + if (textClassifierEvent == null) { + return false; + } + return mType == textClassifierEvent.getEventType(); + } + } } diff --git a/packages/SettingsLib/common.mk b/packages/SettingsLib/common.mk index 8c309ff97370..8d24eaba5cc4 100644 --- a/packages/SettingsLib/common.mk +++ b/packages/SettingsLib/common.mk @@ -31,3 +31,4 @@ LOCAL_STATIC_ANDROID_LIBRARIES += \ androidx.legacy_legacy-preference-v14 \ SettingsLib +LOCAL_RESOURCE_DIR += $(call my-dir)/res diff --git a/packages/SettingsLib/res/drawable/ic_system_update.xml b/packages/SettingsLib/res/drawable/ic_system_update.xml new file mode 100644 index 000000000000..3325fdd5c01a --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_system_update.xml @@ -0,0 +1,25 @@ +<!-- + 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?android:attr/colorControlNormal"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M17,1.01L7,1C5.9,1 5,1.9 5,3v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V3C19,1.9 18.1,1.01 17,1.01zM17,21H7l0,-1h10V21zM17,18H7V6h10V18zM7,4V3h10v1H7zM16,12.5l-4,4l-4,-4l1.41,-1.41L11,12.67V8.5V8h2v0.5v4.17l1.59,-1.59L16,12.5z"/> +</vector> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index d6448336e402..f5d58d8a2004 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -337,6 +337,9 @@ <string name="data_usage_uninstalled_apps">Removed apps</string> <!-- Title of data usage item that represents all uninstalled applications or removed users. [CHAR LIMIT=48] --> <string name="data_usage_uninstalled_apps_users">Removed apps and users</string> + <!-- Title of data usage item that represents system updates (OTAs). [CHAR LIMIT=48] --> + <string name="data_usage_ota">System updates</string> + <!-- Tethering controls, item title to go into the tethering settings --> <!-- Tethering controls, item title to go into the tethering settings when only USB tethering is available [CHAR LIMIT=25]--> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java index a1bf93654774..c1933fd87633 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java @@ -73,9 +73,8 @@ public class HeadsetProfile implements LocalBluetoothProfile { BluetoothProfile.STATE_CONNECTED); device.refresh(); } - - mProfileManager.callServiceConnectedListeners(); mIsProfileReady=true; + mProfileManager.callServiceConnectedListeners(); } public void onServiceDisconnected(int profile) { diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java index 305a1ff97e71..dd6d563b3197 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java @@ -40,6 +40,7 @@ import androidx.loader.content.AsyncTaskLoader; import com.android.settingslib.NetworkPolicyEditor; import java.time.ZonedDateTime; +import java.util.ArrayList; import java.util.Iterator; /** @@ -52,6 +53,7 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { protected final int mNetworkType; private final NetworkPolicy mPolicy; private final NetworkTemplate mNetworkTemplate; + private final ArrayList<Long> mCycles; @VisibleForTesting final INetworkStatsService mNetworkStatsService; @@ -60,6 +62,7 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { mSubId = builder.mSubId; mNetworkType = builder.mNetworkType; mNetworkTemplate = builder.mNetworkTemplate; + mCycles = builder.mCycles; mNetworkStatsManager = (NetworkStatsManager) builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE); mNetworkStatsService = INetworkStatsService.Stub.asInterface( @@ -77,7 +80,9 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { } public D loadInBackground() { - if (mPolicy == null) { + if (mCycles != null && mCycles.size() > 1) { + loadDataForSpecificCycles(); + } else if (mPolicy == null) { loadFourWeeksData(); } else { loadPolicyData(); @@ -132,6 +137,17 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { } @VisibleForTesting + void loadDataForSpecificCycles() { + long cycleEnd = mCycles.get(0); + final int lastCycleIndex = mCycles.size() - 1; + for (int i = 1; i <= lastCycleIndex; i++) { + final long cycleStart = mCycles.get(i); + recordUsage(cycleStart, cycleEnd); + cycleEnd = cycleStart; + } + } + + @VisibleForTesting abstract void recordUsage(long start, long end); abstract D getCycleUsage(); @@ -157,11 +173,17 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { return bytes; } + @VisibleForTesting(otherwise = VisibleForTesting.NONE) + public ArrayList<Long> getCycles() { + return mCycles; + } + public static abstract class Builder<T extends NetworkCycleDataLoader> { private final Context mContext; private String mSubId; private int mNetworkType; private NetworkTemplate mNetworkTemplate; + private ArrayList<Long> mCycles; public Builder (Context context) { mContext = context; @@ -178,6 +200,16 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { return this; } + /** + * Sets the network cycles to be used to query the usage data. + * @param cycles the time slots for the network cycle to be used to query the network usage. + * @return the builder + */ + public Builder<T> setCycles(ArrayList<Long> cycles) { + mCycles = cycles; + return this; + } + public abstract T build(); } diff --git a/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java b/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java index c14f5588cddf..e3516158daac 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java @@ -28,6 +28,7 @@ import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.net.ConnectivityManager; import android.net.TrafficStats; +import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; @@ -111,7 +112,7 @@ public class UidDetailProvider { // handle special case labels switch (uid) { - case android.os.Process.SYSTEM_UID: + case Process.SYSTEM_UID: detail.label = res.getString(R.string.process_kernel_label); detail.icon = pm.getDefaultActivityIcon(); return detail; @@ -127,6 +128,10 @@ public class UidDetailProvider { detail.label = res.getString(Utils.getTetheringLabel(cm)); detail.icon = pm.getDefaultActivityIcon(); return detail; + case Process.OTA_UPDATE_UID: + detail.label = res.getString(R.string.data_usage_ota); + detail.icon = mContext.getDrawable(R.drawable.ic_system_update); + return detail; } final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index b9a5f2347f16..8ea0f4b6ba61 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java @@ -53,6 +53,7 @@ import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; +import com.android.internal.util.CollectionUtils; import com.android.settingslib.R; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; @@ -566,9 +567,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro AccessPoint accessPoint = getCachedOrCreate(entry.getValue(), cachedAccessPoints); - if (mLastInfo != null && mLastNetworkInfo != null) { - accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo); - } // Update the matching config if there is one, to populate saved network info accessPoint.update(configsByKey.get(entry.getKey())); @@ -578,68 +576,20 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro List<ScanResult> cachedScanResults = new ArrayList<>(mScanResultCache.values()); - // Add a unique Passpoint R1 AccessPoint for each Passpoint profile's FQDN. - List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> passpointConfigsAndScans = - mWifiManager.getAllMatchingWifiConfigs(cachedScanResults); - Set<String> seenFQDNs = new ArraySet<>(); - for (Pair<WifiConfiguration, - Map<Integer, List<ScanResult>>> pairing : passpointConfigsAndScans) { - WifiConfiguration config = pairing.first; - - List<ScanResult> scanResults = new ArrayList<>(); + // Add a unique Passpoint AccessPoint for each Passpoint profile's FQDN. + accessPoints.addAll(updatePasspointAccessPoints( + mWifiManager.getAllMatchingWifiConfigs(cachedScanResults), cachedAccessPoints)); - List<ScanResult> homeScans = - pairing.second.get(WifiManager.PASSPOINT_HOME_NETWORK); - List<ScanResult> roamingScans = - pairing.second.get(WifiManager.PASSPOINT_ROAMING_NETWORK); + // Add OSU Provider AccessPoints + accessPoints.addAll(updateOsuAccessPoints( + mWifiManager.getMatchingOsuProviders(cachedScanResults), cachedAccessPoints)); - if (homeScans == null) { - homeScans = new ArrayList<>(); - } - if (roamingScans == null) { - roamingScans = new ArrayList<>(); - } - - // TODO(b/118705403): Differentiate home network vs roaming network for summary info - if (!homeScans.isEmpty()) { - scanResults.addAll(homeScans); - } else { - scanResults.addAll(roamingScans); - } - - if (seenFQDNs.add(config.FQDN)) { - int bestRssi = Integer.MIN_VALUE; - for (ScanResult result : scanResults) { - if (result.level >= bestRssi) { - bestRssi = result.level; - config.SSID = AccessPoint.convertToQuotedString(result.SSID); - } - } - - AccessPoint accessPoint = - getCachedOrCreatePasspoint(scanResults, cachedAccessPoints, config); - accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo); - accessPoints.add(accessPoint); - } - } - - // Add Passpoint OSU Provider AccessPoints - Map<OsuProvider, List<ScanResult>> providersAndScans = - mWifiManager.getMatchingOsuProviders(cachedScanResults); - Set<OsuProvider> alreadyProvisioned = mWifiManager - .getMatchingPasspointConfigsForOsuProviders( - providersAndScans.keySet()).keySet(); - for (OsuProvider provider : providersAndScans.keySet()) { - if (!alreadyProvisioned.contains(provider)) { - AccessPoint accessPointOsu = - getCachedOrCreateOsu(providersAndScans.get(provider), - cachedAccessPoints, provider); - accessPointOsu.update(connectionConfig, mLastInfo, mLastNetworkInfo); - accessPoints.add(accessPointOsu); + if (mLastInfo != null && mLastNetworkInfo != null) { + for (AccessPoint ap : accessPoints) { + ap.update(connectionConfig, mLastInfo, mLastNetworkInfo); } } - // If there were no scan results, create an AP for the currently connected network (if // it exists). if (accessPoints.isEmpty() && connectionConfig != null) { @@ -686,7 +636,67 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } @VisibleForTesting - AccessPoint getCachedOrCreate( + List<AccessPoint> updatePasspointAccessPoints( + List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> passpointConfigsAndScans, + List<AccessPoint> accessPointCache) { + List<AccessPoint> accessPoints = new ArrayList<>(); + + Set<String> seenFQDNs = new ArraySet<>(); + for (Pair<WifiConfiguration, + Map<Integer, List<ScanResult>>> pairing : passpointConfigsAndScans) { + WifiConfiguration config = pairing.first; + if (seenFQDNs.add(config.FQDN)) { + List<ScanResult> apScanResults = new ArrayList<>(); + + List<ScanResult> homeScans = + pairing.second.get(WifiManager.PASSPOINT_HOME_NETWORK); + List<ScanResult> roamingScans = + pairing.second.get(WifiManager.PASSPOINT_ROAMING_NETWORK); + + // TODO(b/118705403): Differentiate home network vs roaming network for summary info + if (!CollectionUtils.isEmpty(homeScans)) { + apScanResults.addAll(homeScans); + } else if (!CollectionUtils.isEmpty(roamingScans)) { + apScanResults.addAll(roamingScans); + } + + int bestRssi = Integer.MIN_VALUE; + for (ScanResult result : apScanResults) { + if (result.level >= bestRssi) { + bestRssi = result.level; + config.SSID = AccessPoint.convertToQuotedString(result.SSID); + } + } + + AccessPoint accessPoint = + getCachedOrCreatePasspoint(apScanResults, accessPointCache, config); + accessPoints.add(accessPoint); + } + } + return accessPoints; + } + + @VisibleForTesting + List<AccessPoint> updateOsuAccessPoints( + Map<OsuProvider, List<ScanResult>> providersAndScans, + List<AccessPoint> accessPointCache) { + List<AccessPoint> accessPoints = new ArrayList<>(); + + Set<OsuProvider> alreadyProvisioned = mWifiManager + .getMatchingPasspointConfigsForOsuProviders( + providersAndScans.keySet()).keySet(); + for (OsuProvider provider : providersAndScans.keySet()) { + if (!alreadyProvisioned.contains(provider)) { + AccessPoint accessPointOsu = + getCachedOrCreateOsu(providersAndScans.get(provider), + accessPointCache, provider); + accessPoints.add(accessPointOsu); + } + } + return accessPoints; + } + + private AccessPoint getCachedOrCreate( List<ScanResult> scanResults, List<AccessPoint> cache) { AccessPoint accessPoint = getCachedByKey(cache, AccessPoint.getKey(scanResults.get(0))); diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java index 42eb0b940938..7d227883b1fe 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -48,11 +49,15 @@ import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiNetworkScoreCache; import android.net.wifi.WifiSsid; +import android.net.wifi.hotspot2.OsuProvider; +import android.net.wifi.hotspot2.PasspointConfiguration; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.SystemClock; import android.provider.Settings; +import android.util.ArraySet; +import android.util.Pair; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -74,7 +79,10 @@ import org.mockito.stubbing.Answer; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -94,6 +102,8 @@ public class WifiTrackerTest { private static final int RSSI_1 = -30; private static final byte SCORE_1 = 10; private static final int BADGE_1 = AccessPoint.Speed.MODERATE; + private static final String FQDN_1 = "fqdn1"; + private static final String PROVIDER_FRIENDLY_NAME_1 = "providerFriendlyName1"; private static final String SSID_2 = "ssid2"; private static final String BSSID_2 = "AA:AA:AA:AA:AA:AA"; @@ -102,6 +112,8 @@ public class WifiTrackerTest { private static final int RSSI_2 = -30; private static final byte SCORE_2 = 15; private static final int BADGE_2 = AccessPoint.Speed.FAST; + private static final String FQDN_2 = "fqdn2"; + private static final String PROVIDER_FRIENDLY_NAME_2 = "providerFriendlyName2"; private static final String SSID_3 = "ssid3"; private static final String BSSID_3 = "CC:00:00:00:00:00"; @@ -271,6 +283,61 @@ public class WifiTrackerTest { 0 /* microsecond timestamp */); } + private static WifiConfiguration buildPasspointConfiguration(String fqdn, String friendlyName) { + WifiConfiguration config = spy(new WifiConfiguration()); + config.FQDN = fqdn; + config.providerFriendlyName = friendlyName; + when(config.isPasspoint()).thenReturn(true); + return config; + } + + private List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> + createPasspointMatchingWifiConfigsWithDuplicates() { + List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> matchingList = + new ArrayList<>(); + Map<Integer, List<ScanResult>> mapping = new HashMap<>(); + + mapping.put(WifiManager.PASSPOINT_HOME_NETWORK, Arrays.asList(buildScanResult1())); + + WifiConfiguration passpointConfig1 = + buildPasspointConfiguration(FQDN_1, PROVIDER_FRIENDLY_NAME_1); + WifiConfiguration passpointConfig2 = + buildPasspointConfiguration(FQDN_2, PROVIDER_FRIENDLY_NAME_2); + + matchingList.add(new Pair(passpointConfig1, mapping)); + matchingList.add(new Pair(passpointConfig1, mapping)); + matchingList.add(new Pair(passpointConfig2, mapping)); + matchingList.add(new Pair(passpointConfig2, mapping)); + + return matchingList; + } + + private List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> + createPasspointMatchingWifiConfigWithScanResults( + List<ScanResult> homeList, List<ScanResult> roamingList) { + List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> matchingList = + new ArrayList<>(); + Map<Integer, List<ScanResult>> mapping = new HashMap<>(); + + if (homeList != null) { + mapping.put(WifiManager.PASSPOINT_HOME_NETWORK, homeList); + } + if (roamingList != null) { + mapping.put(WifiManager.PASSPOINT_ROAMING_NETWORK, roamingList); + } + + matchingList.add(new Pair(buildPasspointConfiguration(FQDN_1, PROVIDER_FRIENDLY_NAME_1), + mapping)); + + return matchingList; + } + + private static OsuProvider buildOsuProvider(String friendlyName) { + Map<String, String> friendlyNames = new HashMap<>(); + friendlyNames.put("en", friendlyName); + return new OsuProvider(null, friendlyNames, null, null, null, null, null); + } + private WifiTracker createTrackerWithImmediateBroadcastsAndInjectInitialScanResults( Intent ... intents) throws InterruptedException { @@ -926,4 +993,172 @@ public class WifiTrackerTest { assertThat(tracker.getAccessPoints().get(0).getBssid()).isEqualTo(BSSID_1); assertThat(tracker.getAccessPoints().get(1).getBssid()).isEqualTo(BSSID_2); } + + /** + * Verifies that updatePasspointAccessPoints will only return AccessPoints whose + * isPasspoint() evaluates as true. + */ + @Test + public void updatePasspointAccessPoints_returnedAccessPointsArePasspoint() { + WifiTracker tracker = createMockedWifiTracker(); + + List<AccessPoint> passpointAccessPoints = tracker.updatePasspointAccessPoints( + createPasspointMatchingWifiConfigsWithDuplicates(), new ArrayList<>()); + + assertTrue(passpointAccessPoints.size() != 0); + for (AccessPoint ap : passpointAccessPoints) { + assertTrue(ap.isPasspoint()); + } + } + + /** + * Verifies that updatePasspointAccessPoints will return the same amount of AccessPoints as + * unique WifiConfigurations, even if duplicate FQDNs exist. + */ + @Test + public void updatePasspointAccessPoints_ignoresDuplicateFQDNs() { + WifiTracker tracker = createMockedWifiTracker(); + + // Process matching list of four configs with two duplicate FQDNs. + List<AccessPoint> passpointAccessPoints = tracker.updatePasspointAccessPoints( + createPasspointMatchingWifiConfigsWithDuplicates(), new ArrayList<>()); + + // Should have 2 APs with unique FQDNs, ignoring the 2 duplicate FQDNs. + assertThat(passpointAccessPoints).hasSize(2); + + Set<String> fqdns = new ArraySet<>(Arrays.asList(FQDN_1, FQDN_2)); + + assertTrue(fqdns.remove(passpointAccessPoints.get(0).getConfig().FQDN)); + assertTrue(fqdns.remove(passpointAccessPoints.get(1).getConfig().FQDN)); + } + + /** + * Verifies that updatePasspointAccessPoints will return matching cached APs and update their + * scan results instead of creating new APs. + */ + @Test + public void updatePasspointAccessPoints_usesCachedAccessPoints() { + WifiTracker tracker = createMockedWifiTracker(); + + ScanResult result = buildScanResult1(); + + List<AccessPoint> passpointAccessPointsFirstUpdate = tracker.updatePasspointAccessPoints( + createPasspointMatchingWifiConfigWithScanResults(Arrays.asList(result), + null), new ArrayList<>()); + List<AccessPoint> cachedAccessPoints = new ArrayList<>(passpointAccessPointsFirstUpdate); + + int prevRssi = result.level; + int newRssi = prevRssi + 10; + result.level = newRssi; + + List<AccessPoint> passpointAccessPointsSecondUpdate = tracker.updatePasspointAccessPoints( + createPasspointMatchingWifiConfigWithScanResults(Arrays.asList(result), + null), cachedAccessPoints); + + // Verify second update AP is the same object as the first update AP + assertThat(passpointAccessPointsFirstUpdate.get(0)) + .isSameAs(passpointAccessPointsSecondUpdate.get(0)); + // Verify second update AP has the average of the first and second update RSSIs + assertThat(passpointAccessPointsSecondUpdate.get(0).getRssi()) + .isEqualTo((prevRssi + newRssi) / 2); + } + + /** + * Verifies that updateOsuAccessPoints will only return AccessPoints whose + * isOsuProvider() evaluates as true. + */ + @Test + public void updateOsuAccessPoints_returnedAccessPointsAreOsuProviders() { + WifiTracker tracker = createMockedWifiTracker(); + + Map<OsuProvider, List<ScanResult>> providersAndScans = new HashMap<>(); + providersAndScans.put( + buildOsuProvider(PROVIDER_FRIENDLY_NAME_1), Arrays.asList(buildScanResult1())); + providersAndScans.put( + buildOsuProvider(PROVIDER_FRIENDLY_NAME_2), Arrays.asList(buildScanResult2())); + + List<AccessPoint> osuAccessPoints = tracker.updateOsuAccessPoints( + providersAndScans, new ArrayList<>()); + + assertThat(osuAccessPoints).hasSize(2); + for (AccessPoint ap: osuAccessPoints) { + assertThat(ap.isOsuProvider()).isTrue(); + } + } + + /** + * Verifies that updateOsuAccessPoints will not return Osu AccessPoints for already provisioned + * networks + */ + @Test + public void updateOsuAccessPoints_doesNotReturnAlreadyProvisionedOsuAccessPoints() { + WifiTracker tracker = createMockedWifiTracker(); + + // Start with two Osu Providers + Map<OsuProvider, List<ScanResult>> providersAndScans = new HashMap<>(); + providersAndScans.put( + buildOsuProvider(PROVIDER_FRIENDLY_NAME_1), Arrays.asList(buildScanResult1())); + providersAndScans.put( + buildOsuProvider(PROVIDER_FRIENDLY_NAME_2), Arrays.asList(buildScanResult2())); + + // First update + List<AccessPoint> osuAccessPoints = tracker.updateOsuAccessPoints( + providersAndScans, new ArrayList<>()); + + // Make sure both Osu Providers' APs are returned + assertThat(osuAccessPoints).hasSize(2); + List<String> friendlyNames = Arrays.asList( + osuAccessPoints.get(0).getTitle(), osuAccessPoints.get(1).getTitle()); + assertThat(friendlyNames) + .containsExactly(PROVIDER_FRIENDLY_NAME_1, PROVIDER_FRIENDLY_NAME_2); + + // Simulate Osu Provider 1 being provisioned + Map<OsuProvider, PasspointConfiguration> matchingPasspointConfigForOsuProvider = + new HashMap<>(); + matchingPasspointConfigForOsuProvider.put(buildOsuProvider(PROVIDER_FRIENDLY_NAME_1), null); + when(mockWifiManager.getMatchingPasspointConfigsForOsuProviders(any())).thenReturn( + matchingPasspointConfigForOsuProvider); + + // Second update + osuAccessPoints = tracker.updateOsuAccessPoints( + providersAndScans, new ArrayList<>()); + + // Returned AP should only be for Osu Provider 2 + assertThat(osuAccessPoints).hasSize(1); + assertThat(osuAccessPoints.get(0).getTitle()).isEqualTo(PROVIDER_FRIENDLY_NAME_2); + } + + /** + * Verifies that updateOsuAccessPoints will return matching cached APs and update their + * scan results instead of creating new APs. + */ + @Test + public void updateOsuAccessPoints_usesCachedAccessPoints() { + WifiTracker tracker = createMockedWifiTracker(); + + ScanResult result = buildScanResult1(); + + Map<OsuProvider, List<ScanResult>> providersAndScans = new HashMap<>(); + providersAndScans.put( + buildOsuProvider(PROVIDER_FRIENDLY_NAME_1), Arrays.asList(result)); + + List<AccessPoint> osuAccessPointsFirstUpdate = tracker.updateOsuAccessPoints( + providersAndScans, new ArrayList<>()); + List<AccessPoint> cachedAccessPoints = new ArrayList<>(osuAccessPointsFirstUpdate); + + // New RSSI for second update + int prevRssi = result.level; + int newRssi = prevRssi + 10; + result.level = newRssi; + + List<AccessPoint> osuAccessPointsSecondUpdate = tracker.updateOsuAccessPoints( + providersAndScans, cachedAccessPoints); + + // Verify second update AP is the same object as the first update AP + assertTrue(osuAccessPointsFirstUpdate.get(0) + == osuAccessPointsSecondUpdate.get(0)); + // Verify second update AP has the average of the first and second update RSSIs + assertThat(osuAccessPointsSecondUpdate.get(0).getRssi()) + .isEqualTo((prevRssi + newRssi) / 2); + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java index b8a143a376fd..c5f54bb0f0d9 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java @@ -46,6 +46,7 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.util.ReflectionHelpers; import java.time.ZonedDateTime; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -99,6 +100,20 @@ public class NetworkCycleDataLoaderTest { } @Test + public void loadInBackground_hasCyclePeriod_shouldLoadDataForSpecificCycles() { + mLoader = spy(new NetworkCycleDataTestLoader(mContext)); + doNothing().when(mLoader).loadDataForSpecificCycles(); + final ArrayList<Long> cycles = new ArrayList<>(); + cycles.add(67890L); + cycles.add(12345L); + ReflectionHelpers.setField(mLoader, "mCycles", cycles); + + mLoader.loadInBackground(); + + verify(mLoader).loadDataForSpecificCycles(); + } + + @Test public void loadPolicyData_shouldRecordUsageFromPolicyCycle() { final int networkType = ConnectivityManager.TYPE_MOBILE; final String subId = "TestSubscriber"; @@ -139,6 +154,27 @@ public class NetworkCycleDataLoaderTest { verify(mLoader).recordUsage(fourWeeksAgo, now); } + @Test + public void loadDataForSpecificCycles_shouldRecordUsageForSpecifiedTime() { + mLoader = spy(new NetworkCycleDataTestLoader(mContext)); + final long now = System.currentTimeMillis(); + final long tenDaysAgo = now - (DateUtils.DAY_IN_MILLIS * 10); + final long twentyDaysAgo = now - (DateUtils.DAY_IN_MILLIS * 20); + final long thirtyDaysAgo = now - (DateUtils.DAY_IN_MILLIS * 30); + final ArrayList<Long> cycles = new ArrayList<>(); + cycles.add(now); + cycles.add(tenDaysAgo); + cycles.add(twentyDaysAgo); + cycles.add(thirtyDaysAgo); + ReflectionHelpers.setField(mLoader, "mCycles", cycles); + + mLoader.loadDataForSpecificCycles(); + + verify(mLoader).recordUsage(tenDaysAgo, now); + verify(mLoader).recordUsage(twentyDaysAgo, tenDaysAgo); + verify(mLoader).recordUsage(thirtyDaysAgo, twentyDaysAgo); + } + public class NetworkCycleDataTestLoader extends NetworkCycleDataLoader<List<NetworkCycleData>> { private NetworkCycleDataTestLoader(Context context) { diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 6152b8cbd562..db4b131a0f6f 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -698,6 +698,9 @@ class SettingsProtoDumpUtil { Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST, GlobalSettingsProto.Gpu.ANGLE_WHITELIST); dumpSetting(s, p, + Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX, + GlobalSettingsProto.Gpu.SHOW_ANGLE_IN_USE_DIALOG); + dumpSetting(s, p, Settings.Global.GPU_DEBUG_LAYER_APP, GlobalSettingsProto.Gpu.DEBUG_LAYER_APP); dumpSetting(s, p, @@ -718,6 +721,9 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Global.GAME_DRIVER_WHITELIST, GlobalSettingsProto.Gpu.GAME_DRIVER_WHITELIST); + dumpSetting(s, p, + Settings.Global.GAME_DRIVER_BLACKLISTS, + GlobalSettingsProto.Gpu.GAME_DRIVER_BLACKLISTS); p.end(gpuToken); final long hdmiToken = p.start(GlobalSettingsProto.HDMI); diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml index 94481e7fe456..5714556ae8a3 100644 --- a/packages/SystemUI/res-keyguard/values/strings.xml +++ b/packages/SystemUI/res-keyguard/values/strings.xml @@ -64,6 +64,9 @@ charged, say that it is charged. --> <string name="keyguard_charged">Fully charged</string> + <!-- When the lock screen is showing and the phone plugged in, and the battery is not fully charged, say that it's wirelessly charging. [CHAR LIMIT=50] --> + <string name="keyguard_plugged_in_wireless"><xliff:g id="percentage" example="20%">%s</xliff:g> • Wirelessly Charging</string> + <!-- When the lock screen is showing and the phone plugged in, and the battery is not fully charged, say that it's charging. --> <string name="keyguard_plugged_in"><xliff:g id="percentage">%s</xliff:g> • Charging</string> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index db92ed258064..7d009b5a9f18 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -955,6 +955,9 @@ <!-- Interruption level: Alarms only. Optimized for narrow two-line display. [CHAR LIMIT=40] --> <string name="interruption_level_alarms_twoline">Alarms\nonly</string> + <!-- Indication on the keyguard that is shown when the device is wirelessly charging. [CHAR LIMIT=80]--> + <string name="keyguard_indication_charging_time_wireless"><xliff:g id="percentage" example="20%">%2$s</xliff:g> • Wirelessly Charging (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%1$s</xliff:g> until full)</string> + <!-- Indication on the keyguard that is shown when the device is charging. [CHAR LIMIT=50]--> <string name="keyguard_indication_charging_time"><xliff:g id="percentage">%2$s</xliff:g> • Charging (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%1$s</xliff:g> until full)</string> diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index 4d708908975e..038f4912e40f 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -290,10 +290,6 @@ public class AssistManager implements ConfigurationChangedReceiver { return mAssistUtils.isSessionRunning(); } - public void destroy() { - mWindowManager.removeViewImmediate(mView); - } - private void maybeSwapSearchIcon(@NonNull ComponentName assistComponent, boolean isService) { replaceDrawable(mView.getOrb().getLogo(), assistComponent, ASSIST_ICON_METADATA_NAME, isService); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index be749aef7f25..164f5826061e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -424,22 +424,28 @@ public class KeyguardIndicationController implements StateListener { final boolean hasChargingTime = chargingTimeRemaining > 0; int chargingId; - switch (mChargingSpeed) { - case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST: - chargingId = hasChargingTime - ? R.string.keyguard_indication_charging_time_fast - : R.string.keyguard_plugged_in_charging_fast; - break; - case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY: - chargingId = hasChargingTime - ? R.string.keyguard_indication_charging_time_slowly - : R.string.keyguard_plugged_in_charging_slowly; - break; - default: - chargingId = hasChargingTime - ? R.string.keyguard_indication_charging_time - : R.string.keyguard_plugged_in; - break; + if (mPowerPluggedInWired) { + switch (mChargingSpeed) { + case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST: + chargingId = hasChargingTime + ? R.string.keyguard_indication_charging_time_fast + : R.string.keyguard_plugged_in_charging_fast; + break; + case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY: + chargingId = hasChargingTime + ? R.string.keyguard_indication_charging_time_slowly + : R.string.keyguard_plugged_in_charging_slowly; + break; + default: + chargingId = hasChargingTime + ? R.string.keyguard_indication_charging_time + : R.string.keyguard_plugged_in; + break; + } + } else { + chargingId = hasChargingTime + ? R.string.keyguard_indication_charging_time_wireless + : R.string.keyguard_plugged_in_wireless; } String percentage = NumberFormat.getPercentInstance() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java index e11ec2d24e29..2edea789d820 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java @@ -158,18 +158,6 @@ public class NavigationBarController implements DisplayListener, Callbacks { }); } - /** Removes navigation bars. */ - public void destroy() { - mDisplayManager.unregisterDisplayListener(this); - if (mNavigationBars.size() > 0) { - for (int i = 0; i < mNavigationBars.size(); i++) { - int displayId = mNavigationBars.keyAt(i); - removeNavigationBar(displayId); - } - mNavigationBars.clear(); - } - } - private void removeNavigationBar(int displayId) { NavigationBarFragment navBar = mNavigationBars.get(displayId); if (navBar != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java index c25b7cfd804c..1cc6dc391c1d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java @@ -42,8 +42,6 @@ public interface NotificationLockscreenUserManager { /** Adds a listener to be notified when the current user changes. */ void addUserChangedListener(UserChangedListener listener); - void destroy(); - SparseArray<UserInfo> getCurrentProfiles(); void setLockscreenPublicMode(boolean isProfilePublic, int userId); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index 6a49b804f5ba..9cb6f11fb558 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -550,12 +550,6 @@ public class NotificationLockscreenUserManagerImpl implements return mKeyguardMonitor.isSecure() || mLockPatternUtils.isSecure(userId); } - public void destroy() { - mContext.unregisterReceiver(mBaseBroadcastReceiver); - mContext.unregisterReceiver(mAllUsersReceiver); - mListeners.clear(); - } - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("NotificationLockscreenUserManager state:"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java index 88f4ca239af4..769cbb7b984c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java @@ -61,11 +61,6 @@ public class NotificationListController { mDeviceProvisionedController.addCallback(mDeviceProvisionedListener); } - /** Should be called when the list controller is being destroyed. */ - public void destroy() { - mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener); - } - @SuppressWarnings("FieldCanBeLocal") private final NotificationEntryListener mEntryListener = new NotificationEntryListener() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 7105876907bf..fbf1e310abf2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -578,6 +578,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @Override @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void onDensityOrFontScaleChanged() { + reinflateViews(); + } + + private void reinflateViews() { inflateFooterView(); inflateEmptyShadeView(); updateFooter(); @@ -608,6 +612,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mCornerRadius = newRadius; invalidate(); } + reinflateViews(); } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index 4c1c0a4b4e58..de0e194ef90c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -141,10 +141,6 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, mAnimationStateHandler = handler; } - public void destroy() { - Dependency.get(StatusBarStateController.class).removeCallback(this); - } - private void initResources() { Resources resources = mContext.getResources(); mStatusBarHeight = resources.getDimensionPixelSize( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 18711c0d1ae3..2799191a886f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -284,25 +284,6 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, }); } - public void destroy() { - mRotationLockController.removeCallback(this); - mBluetooth.removeCallback(this); - mProvisionedController.removeCallback(this); - mZenController.removeCallback(this); - mCast.removeCallback(mCastCallback); - mHotspot.removeCallback(mHotspotCallback); - mNextAlarmController.removeCallback(mNextAlarmCallback); - mDataSaver.removeCallback(this); - mKeyguardMonitor.removeCallback(this); - mPrivacyItemController.removeCallback(this); - SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallback(this); - mContext.unregisterReceiver(mIntentReceiver); - - NotificationManager noMan = mContext.getSystemService(NotificationManager.class); - mCurrentNotifs.forEach(v -> noMan.cancelAsUser(v.first, SystemMessage.NOTE_INSTANT_APPS, - new UserHandle(v.second))); - } - @Override public void onZenChanged(int zen) { updateVolumeZen(); 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 9f3bec60cb6d..849570937ea4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -2843,38 +2843,6 @@ public class StatusBar extends SystemUI implements DemoMode, startActivityDismissingKeyguard(intent, onlyProvisioned, true /* dismissShade */); } - public void destroy() { - // Begin old BaseStatusBar.destroy(). - mContext.unregisterReceiver(mBannerActionBroadcastReceiver); - mLockscreenUserManager.destroy(); - try { - mNotificationListener.unregisterAsSystemService(); - } catch (RemoteException e) { - // Ignore. - } - mNotificationListController.destroy(); - // End old BaseStatusBar.destroy(). - if (mStatusBarWindow != null) { - mWindowManager.removeViewImmediate(mStatusBarWindow); - mStatusBarWindow = null; - } - mNavigationBarController.destroy(); - mContext.unregisterReceiver(mBroadcastReceiver); - mContext.unregisterReceiver(mDemoReceiver); - mAssistManager.destroy(); - mHeadsUpManager.destroy(); - mStatusBarStateController.removeCallback(this); - - if (mQSPanel != null && mQSPanel.getHost() != null) { - mQSPanel.getHost().destroy(); - } - Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(null); - mDeviceProvisionedController.removeCallback(mUserSetupObserver); - Dependency.get(ConfigurationController.class).removeCallback(this); - mZenController.removeCallback(this); - mAppOpsController.removeCallback(APP_OPS, this); - } - private boolean mDemoModeAllowed; private boolean mDemoMode; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index e9705ff35a4d..3ce66c5de372 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -258,6 +258,11 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, } } + @Override + public void onOverlayChanged() { + onDensityOrFontScaleChanged(); + } + private void updateNotificationOnUiModeChanged() { ArrayList<NotificationEntry> userNotifications = mEntryManager.getNotificationData().getNotificationsForCurrentUser(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index cad1a96b8075..79bc0a39d59c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -521,6 +521,16 @@ public class NotificationEntryManagerTest extends SysuiTestCase { verify(extender2).setShouldManageLifetime(mEntry, false); } + /** + * Ensure that calling NotificationEntryManager.performRemoveNotification() doesn't crash when + * given a notification that has already been removed from NotificationData. + */ + @Test + public void testPerformRemoveNotification_removedEntry() { + mEntryManager.getNotificationData().remove(mSbn.getKey(), null /* ranking */); + mEntryManager.performRemoveNotification(mSbn); + } + private Notification.Action createAction() { return new Notification.Action.Builder( Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon), diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index 3c910696ba60..d9adec85d40e 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -6975,6 +6975,23 @@ message MetricsEvent { // formerly: histogram system_cost_for_smart_sharing FIELD_TIME_TO_APP_TARGETS = 1653; + // Open: Settings > Panel for Internet Connectivity + PANEL_INTERNET_CONNECTIVITY = 1654; + + // Open: Settings > Panel for Volume + PANEL_VOLUME = 1655; + + // Open: Settings > Panel for NFC + PANEL_NFC = 1656; + + // Open: Settings > Panel for Media Output + PANEL_MEDIA_OUTPUT = 1657; + + // ACTION: An interaction with a Slice or other component in the Panel. + // CATEGORY: SETTINGS + // OS: Q + ACTION_PANEL_INTERACTION = 1658; + // ---- End Q Constants, all Q constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS diff --git a/services/core/java/com/android/server/ExtconUEventObserver.java b/services/core/java/com/android/server/ExtconUEventObserver.java index b3084f50071f..ebd4c5584fe1 100644 --- a/services/core/java/com/android/server/ExtconUEventObserver.java +++ b/services/core/java/com/android/server/ExtconUEventObserver.java @@ -22,8 +22,11 @@ import android.util.Slog; import java.io.File; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.regex.Pattern; /** * A specialized UEventObserver that receives UEvents from the kernel for devices in the {@code @@ -40,13 +43,14 @@ import java.util.Map; * time in that process. Once started the UEvent thread will not stop (although it can stop * notifying UEventObserver's via stopObserving()). * - * <p> - * * @hide */ public abstract class ExtconUEventObserver extends UEventObserver { private static final String TAG = "ExtconUEventObserver"; private static final boolean LOG = false; + private static final String SELINUX_POLICIES_NEED_TO_BE_CHANGED = + "This probably mean the selinux policies need to be changed."; + private final Map<String, ExtconInfo> mExtconInfos = new ArrayMap<>(); @Override @@ -70,15 +74,47 @@ public abstract class ExtconUEventObserver extends UEventObserver { /** Starts observing {@link ExtconInfo#getDevicePath()}. */ public void startObserving(ExtconInfo extconInfo) { - mExtconInfos.put(extconInfo.getDevicePath(), extconInfo); - if (LOG) Slog.v(TAG, "Observing " + extconInfo.getDevicePath()); - startObserving("DEVPATH=" + extconInfo.getDevicePath()); + String devicePath = extconInfo.getDevicePath(); + if (devicePath == null) { + Slog.wtf(TAG, "Unable to start observing " + extconInfo.getName() + + " because the device path is null. " + SELINUX_POLICIES_NEED_TO_BE_CHANGED); + } else { + mExtconInfos.put(devicePath, extconInfo); + if (LOG) Slog.v(TAG, "Observing " + devicePath); + startObserving("DEVPATH=" + devicePath); + } } /** An External Connection to watch. */ public static final class ExtconInfo { private static final String TAG = "ExtconInfo"; + /** Returns a new list of all external connections whose name matches {@code regex}. */ + public static List<ExtconInfo> getExtconInfos(@Nullable String regex) { + Pattern p = regex == null ? null : Pattern.compile(regex); + File file = new File("/sys/class/extcon"); + File[] files = file.listFiles(); + if (files == null) { + Slog.wtf(TAG, file + " exists " + file.exists() + " isDir " + file.isDirectory() + + " but listFiles returns null. " + + SELINUX_POLICIES_NEED_TO_BE_CHANGED); + return new ArrayList<>(0); // Always return a new list. + } else { + ArrayList list = new ArrayList(files.length); + for (File f : files) { + String name = f.getName(); + if (p == null || p.matcher(name).matches()) { + ExtconInfo uei = new ExtconInfo(name); + list.add(uei); + if (LOG) Slog.d(TAG, name + " matches " + regex); + } else { + if (LOG) Slog.d(TAG, name + " does not match " + regex); + } + } + return list; + } + } + private final String mName; public ExtconInfo(String name) { diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 4834ce0da9b3..3479e18b97c5 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -67,6 +67,7 @@ import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.IInterface; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; @@ -109,6 +110,7 @@ import com.android.server.location.LocationRequestStatistics.PackageProviderKey; import com.android.server.location.LocationRequestStatistics.PackageStatistics; import com.android.server.location.MockProvider; import com.android.server.location.PassiveProvider; +import com.android.server.location.RemoteListenerHelper; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -121,6 +123,8 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; +import java.util.function.Consumer; +import java.util.function.Function; /** * The service class that manages LocationProviders and issues location @@ -225,11 +229,14 @@ public class LocationManagerService extends ILocationManager.Stub { private final ArraySet<String> mIgnoreSettingsPackageWhitelist = new ArraySet<>(); @GuardedBy("mLock") - private final ArrayMap<IBinder, CallerIdentity> mGnssMeasurementsListeners = new ArrayMap<>(); - + private final ArrayMap<IBinder, LinkedListener<IGnssMeasurementsListener>> + mGnssMeasurementsListeners = new ArrayMap<>(); @GuardedBy("mLock") - private final ArrayMap<IBinder, CallerIdentity> + private final ArrayMap<IBinder, LinkedListener<IGnssNavigationMessageListener>> mGnssNavigationMessageListeners = new ArrayMap<>(); + @GuardedBy("mLock") + private final ArrayMap<IBinder, LinkedListener<IGnssStatusListener>> + mGnssStatusListeners = new ArrayMap<>(); // current active user on the device - other users are denied location data private int mCurrentUserId = UserHandle.USER_SYSTEM; @@ -243,7 +250,7 @@ public class LocationManagerService extends ILocationManager.Stub { @GuardedBy("mLock") private IBatchedLocationCallback mGnssBatchingCallback; @GuardedBy("mLock") - private LinkedCallback mGnssBatchingDeathCallback; + private LinkedListener<IBatchedLocationCallback> mGnssBatchingDeathCallback; @GuardedBy("mLock") private boolean mGnssBatchingInProgress = false; @@ -485,7 +492,7 @@ public class LocationManagerService extends ILocationManager.Stub { && record.mIsForegroundUid != foreground) { if (D) { Log.d(TAG, "request from uid " + uid + " is now " - + (foreground ? "foreground" : "background)")); + + foregroundAsString(foreground)); } record.updateForeground(foreground); @@ -499,44 +506,48 @@ public class LocationManagerService extends ILocationManager.Stub { applyRequirementsLocked(provider); } - for (Entry<IBinder, CallerIdentity> entry : mGnssMeasurementsListeners.entrySet()) { - CallerIdentity callerIdentity = entry.getValue(); - if (callerIdentity.mUid == uid) { - if (D) { - Log.d(TAG, "gnss measurements listener from uid " + uid - + " is now " + (foreground ? "foreground" : "background)")); - } - if (foreground || isThrottlingExemptLocked(entry.getValue())) { - mGnssMeasurementsProvider.addListener( - IGnssMeasurementsListener.Stub.asInterface(entry.getKey()), - callerIdentity); - } else { - mGnssMeasurementsProvider.removeListener( - IGnssMeasurementsListener.Stub.asInterface(entry.getKey())); - } + updateGnssDataProviderOnUidImportanceChangedLocked(mGnssMeasurementsListeners, + mGnssMeasurementsProvider, IGnssMeasurementsListener.Stub::asInterface, + uid, foreground); + + updateGnssDataProviderOnUidImportanceChangedLocked(mGnssNavigationMessageListeners, + mGnssNavigationMessageProvider, IGnssNavigationMessageListener.Stub::asInterface, + uid, foreground); + + updateGnssDataProviderOnUidImportanceChangedLocked(mGnssStatusListeners, + mGnssStatusProvider, IGnssStatusListener.Stub::asInterface, uid, foreground); + } + + @GuardedBy("mLock") + private <TListener extends IInterface> void updateGnssDataProviderOnUidImportanceChangedLocked( + ArrayMap<IBinder, ? extends LinkedListenerBase> gnssDataListeners, + RemoteListenerHelper<TListener> gnssDataProvider, + Function<IBinder, TListener> mapBinderToListener, int uid, boolean foreground) { + for (Entry<IBinder, ? extends LinkedListenerBase> entry : gnssDataListeners.entrySet()) { + LinkedListenerBase linkedListener = entry.getValue(); + CallerIdentity callerIdentity = linkedListener.mCallerIdentity; + if (callerIdentity.mUid != uid) { + continue; } - } - for (Entry<IBinder, CallerIdentity> entry : mGnssNavigationMessageListeners.entrySet()) { - CallerIdentity callerIdentity = entry.getValue(); - if (callerIdentity.mUid == uid) { - if (D) { - Log.d(TAG, "gnss navigation message listener from uid " - + uid + " is now " - + (foreground ? "foreground" : "background)")); - } - if (foreground || isThrottlingExemptLocked(entry.getValue())) { - mGnssNavigationMessageProvider.addListener( - IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()), - callerIdentity); - } else { - mGnssNavigationMessageProvider.removeListener( - IGnssNavigationMessageListener.Stub.asInterface(entry.getKey())); - } + if (D) { + Log.d(TAG, linkedListener.mListenerName + " from uid " + + uid + " is now " + foregroundAsString(foreground)); + } + + TListener listener = mapBinderToListener.apply(entry.getKey()); + if (foreground || isThrottlingExemptLocked(callerIdentity)) { + gnssDataProvider.addListener(listener, callerIdentity); + } else { + gnssDataProvider.removeListener(listener); } } } + private static String foregroundAsString(boolean foreground) { + return foreground ? "foreground" : "background"; + } + private static boolean isImportanceForeground(int importance) { return importance <= FOREGROUND_IMPORTANCE_CUTOFF; } @@ -1218,9 +1229,8 @@ public class LocationManagerService extends ILocationManager.Stub { * A wrapper class holding either an ILocationListener or a PendingIntent to receive * location updates. */ - private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished { + private final class Receiver extends LinkedListenerBase implements PendingIntent.OnFinished { private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000; - final CallerIdentity mCallerIdentity; private final int mAllowedResolutionLevel; // resolution level allowed to receiver private final ILocationListener mListener; @@ -1240,6 +1250,7 @@ public class LocationManagerService extends ILocationManager.Stub { private Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid, String packageName, WorkSource workSource, boolean hideFromAppOps) { + super(new CallerIdentity(uid, pid, packageName), "LocationListener"); mListener = listener; mPendingIntent = intent; if (listener != null) { @@ -1248,7 +1259,6 @@ public class LocationManagerService extends ILocationManager.Stub { mKey = intent; } mAllowedResolutionLevel = getAllowedResolutionLevel(pid, uid); - mCallerIdentity = new CallerIdentity(uid, pid, packageName); if (workSource != null && workSource.isEmpty()) { workSource = null; } @@ -1486,7 +1496,7 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void binderDied() { - if (D) Log.d(TAG, "Location listener died"); + if (D) Log.d(TAG, "Remote " + mListenerName + " died."); synchronized (mLock) { removeUpdatesLocked(this); @@ -1617,53 +1627,59 @@ public class LocationManagerService extends ILocationManager.Stub { return false; } + CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), + Binder.getCallingPid(), packageName); synchronized (mLock) { mGnssBatchingCallback = callback; - mGnssBatchingDeathCallback = new LinkedCallback(callback); - try { - callback.asBinder().linkToDeath(mGnssBatchingDeathCallback, 0 /* flags */); - } catch (RemoteException e) { - // if the remote process registering the listener is already dead, just swallow the - // exception and return - Log.e(TAG, "Remote listener already died.", e); + mGnssBatchingDeathCallback = new LinkedListener<>(callback, + "BatchedLocationCallback", callerIdentity, + (IBatchedLocationCallback listener) -> { + stopGnssBatch(); + removeGnssBatchingCallback(); + }); + if (!linkToListenerDeathNotificationLocked(callback.asBinder(), + mGnssBatchingDeathCallback)) { return false; } - return true; } } - private class LinkedCallback implements IBinder.DeathRecipient { - private final IBatchedLocationCallback mCallback; + private abstract static class LinkedListenerBase implements IBinder.DeathRecipient { + protected final CallerIdentity mCallerIdentity; + protected final String mListenerName; - private LinkedCallback(@NonNull IBatchedLocationCallback callback) { - mCallback = callback; + private LinkedListenerBase(@NonNull CallerIdentity callerIdentity, + @NonNull String listenerName) { + mCallerIdentity = callerIdentity; + mListenerName = listenerName; } + } + + private static class LinkedListener<TListener> extends LinkedListenerBase { + private final TListener mListener; + private final Consumer<TListener> mBinderDeathCallback; - @NonNull - public IBatchedLocationCallback getUnderlyingListener() { - return mCallback; + private LinkedListener(@NonNull TListener listener, String listenerName, + @NonNull CallerIdentity callerIdentity, + @NonNull Consumer<TListener> binderDeathCallback) { + super(callerIdentity, listenerName); + mListener = listener; + mBinderDeathCallback = binderDeathCallback; } @Override public void binderDied() { - Log.d(TAG, "Remote Batching Callback died: " + mCallback); - stopGnssBatch(); - removeGnssBatchingCallback(); + if (D) Log.d(TAG, "Remote " + mListenerName + " died."); + mBinderDeathCallback.accept(mListener); } } @Override public void removeGnssBatchingCallback() { synchronized (mLock) { - try { - mGnssBatchingCallback.asBinder().unlinkToDeath(mGnssBatchingDeathCallback, - 0 /* flags */); - } catch (NoSuchElementException e) { - // if the death callback isn't connected (it should be...), log error, swallow the - // exception and return - Log.e(TAG, "Couldn't unlink death callback.", e); - } + unlinkFromListenerDeathNotificationLocked(mGnssBatchingCallback.asBinder(), + mGnssBatchingDeathCallback); mGnssBatchingCallback = null; mGnssBatchingDeathCallback = null; } @@ -2264,10 +2280,8 @@ public class LocationManagerService extends ILocationManager.Stub { if (receiver == null) { receiver = new Receiver(listener, null, pid, uid, packageName, workSource, hideFromAppOps); - try { - receiver.getListener().asBinder().linkToDeath(receiver, 0); - } catch (RemoteException e) { - Slog.e(TAG, "linkToDeath failed:", e); + if (!linkToListenerDeathNotificationLocked(receiver.getListener().asBinder(), + receiver)) { return null; } mReceivers.put(binder, receiver); @@ -2482,7 +2496,8 @@ public class LocationManagerService extends ILocationManager.Stub { if (D) Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver))); if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) { - receiver.getListener().asBinder().unlinkToDeath(receiver, 0); + unlinkFromListenerDeathNotificationLocked(receiver.getListener().asBinder(), + receiver); receiver.clearPendingBroadcastsLocked(); } @@ -2694,18 +2709,52 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public boolean registerGnssStatusCallback(IGnssStatusListener callback, String packageName) { + public boolean registerGnssStatusCallback(IGnssStatusListener listener, String packageName) { if (!hasGnssPermissions(packageName) || mGnssStatusProvider == null) { return false; } - return mGnssStatusProvider.addListener(callback, new CallerIdentity(Binder.getCallingUid(), - Binder.getCallingPid(), packageName)); + CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), + Binder.getCallingPid(), packageName); + LinkedListener<IGnssStatusListener> linkedListener = new LinkedListener<>(listener, + "GnssStatusListener", callerIdentity, this::unregisterGnssStatusCallback); + IBinder binder = listener.asBinder(); + synchronized (mLock) { + if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) { + return false; + } + + mGnssStatusListeners.put(binder, linkedListener); + long identity = Binder.clearCallingIdentity(); + try { + if (isThrottlingExemptLocked(callerIdentity) + || isImportanceForeground( + mActivityManager.getPackageImportance(packageName))) { + mGnssStatusProvider.addListener(listener, callerIdentity); + } + return true; + } finally { + Binder.restoreCallingIdentity(identity); + } + } } @Override - public void unregisterGnssStatusCallback(IGnssStatusListener callback) { - mGnssStatusProvider.removeListener(callback); + public void unregisterGnssStatusCallback(IGnssStatusListener listener) { + if (mGnssStatusProvider == null) { + return; + } + + IBinder binder = listener.asBinder(); + synchronized (mLock) { + LinkedListener<IGnssStatusListener> linkedListener = + mGnssStatusListeners.remove(binder); + if (linkedListener == null) { + return; + } + unlinkFromListenerDeathNotificationLocked(binder, linkedListener); + mGnssStatusProvider.removeListener(listener); + } } @Override @@ -2715,22 +2764,75 @@ public class LocationManagerService extends ILocationManager.Stub { return false; } + CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), + Binder.getCallingPid(), packageName); + LinkedListener<IGnssMeasurementsListener> linkedListener = new LinkedListener<>(listener, + "GnssMeasurementsListener", callerIdentity, this::removeGnssMeasurementsListener); + IBinder binder = listener.asBinder(); synchronized (mLock) { - CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), - Binder.getCallingPid(), packageName); - mGnssMeasurementsListeners.put(listener.asBinder(), callerIdentity); + if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) { + return false; + } + + mGnssMeasurementsListeners.put(binder, linkedListener); long identity = Binder.clearCallingIdentity(); try { if (isThrottlingExemptLocked(callerIdentity) || isImportanceForeground( mActivityManager.getPackageImportance(packageName))) { - return mGnssMeasurementsProvider.addListener(listener, callerIdentity); + mGnssMeasurementsProvider.addListener(listener, callerIdentity); } + return true; } finally { Binder.restoreCallingIdentity(identity); } + } + } + + @Override + public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) { + if (mGnssMeasurementsProvider == null) { + return; + } + + IBinder binder = listener.asBinder(); + synchronized (mLock) { + LinkedListener<IGnssMeasurementsListener> linkedListener = + mGnssMeasurementsListeners.remove(binder); + if (linkedListener == null) { + return; + } + unlinkFromListenerDeathNotificationLocked(binder, linkedListener); + mGnssMeasurementsProvider.removeListener(listener); + } + } + + private boolean linkToListenerDeathNotificationLocked(IBinder binder, + LinkedListenerBase linkedListener) { + try { + binder.linkToDeath(linkedListener, 0 /* flags */); + return true; + } catch (RemoteException e) { + // if the remote process registering the listener is already dead, just swallow the + // exception and return + Log.w(TAG, "Could not link " + linkedListener.mListenerName + " death callback.", + e); + return false; + } + } + + private boolean unlinkFromListenerDeathNotificationLocked(IBinder binder, + LinkedListenerBase linkedListener) { + try { + binder.unlinkToDeath(linkedListener, 0 /* flags */); return true; + } catch (NoSuchElementException e) { + // if the death callback isn't connected (it should be...), log error, + // swallow the exception and return + Log.w(TAG, "Could not unlink " + linkedListener.mListenerName + " death callback.", + e); + return false; } } @@ -2759,52 +2861,53 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) { - if (mGnssMeasurementsProvider == null) { - return; - } - - synchronized (mLock) { - mGnssMeasurementsListeners.remove(listener.asBinder()); - mGnssMeasurementsProvider.removeListener(listener); - } - } - - @Override public boolean addGnssNavigationMessageListener( - IGnssNavigationMessageListener listener, - String packageName) { + IGnssNavigationMessageListener listener, String packageName) { if (!hasGnssPermissions(packageName) || mGnssNavigationMessageProvider == null) { return false; } + CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), + Binder.getCallingPid(), packageName); + LinkedListener<IGnssNavigationMessageListener> linkedListener = + new LinkedListener<>(listener, "GnssNavigationMessageListener", callerIdentity, + this::removeGnssNavigationMessageListener); + IBinder binder = listener.asBinder(); synchronized (mLock) { - CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), - Binder.getCallingPid(), packageName); + if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) { + return false; + } - mGnssNavigationMessageListeners.put(listener.asBinder(), callerIdentity); + mGnssNavigationMessageListeners.put(binder, linkedListener); long identity = Binder.clearCallingIdentity(); try { if (isThrottlingExemptLocked(callerIdentity) || isImportanceForeground( mActivityManager.getPackageImportance(packageName))) { - return mGnssNavigationMessageProvider.addListener(listener, callerIdentity); + mGnssNavigationMessageProvider.addListener(listener, callerIdentity); } + return true; } finally { Binder.restoreCallingIdentity(identity); } - - return true; } } @Override public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) { - if (mGnssNavigationMessageProvider != null) { - synchronized (mLock) { - mGnssNavigationMessageListeners.remove(listener.asBinder()); - mGnssNavigationMessageProvider.removeListener(listener); + if (mGnssNavigationMessageProvider == null) { + return; + } + + IBinder binder = listener.asBinder(); + synchronized (mLock) { + LinkedListener<IGnssNavigationMessageListener> linkedListener = + mGnssNavigationMessageListeners.remove(binder); + if (linkedListener == null) { + return; } + unlinkFromListenerDeathNotificationLocked(binder, linkedListener); + mGnssNavigationMessageProvider.removeListener(listener); } } @@ -3368,18 +3471,14 @@ public class LocationManagerService extends ILocationManager.Stub { pw.println(" " + record); } } + pw.println(" Active GnssMeasurement Listeners:"); - for (CallerIdentity callerIdentity : mGnssMeasurementsListeners.values()) { - pw.println(" " + callerIdentity.mPid + " " + callerIdentity.mUid + " " - + callerIdentity.mPackageName + ": " - + isThrottlingExemptLocked(callerIdentity)); - } + dumpGnssDataListenersLocked(pw, mGnssMeasurementsListeners); pw.println(" Active GnssNavigationMessage Listeners:"); - for (CallerIdentity callerIdentity : mGnssNavigationMessageListeners.values()) { - pw.println(" " + callerIdentity.mPid + " " + callerIdentity.mUid + " " - + callerIdentity.mPackageName + ": " - + isThrottlingExemptLocked(callerIdentity)); - } + dumpGnssDataListenersLocked(pw, mGnssNavigationMessageListeners); + pw.println(" Active GnssStatus Listeners:"); + dumpGnssDataListenersLocked(pw, mGnssStatusListeners); + pw.println(" Historical Records by Provider:"); for (Map.Entry<PackageProviderKey, PackageStatistics> entry : mRequestStatistics.statistics.entrySet()) { @@ -3432,4 +3531,15 @@ public class LocationManagerService extends ILocationManager.Stub { } } } + + @GuardedBy("mLock") + private void dumpGnssDataListenersLocked(PrintWriter pw, + ArrayMap<IBinder, ? extends LinkedListenerBase> gnssDataListeners) { + for (LinkedListenerBase listener : gnssDataListeners.values()) { + CallerIdentity callerIdentity = listener.mCallerIdentity; + pw.println(" " + callerIdentity.mPid + " " + callerIdentity.mUid + " " + + callerIdentity.mPackageName + ": " + + isThrottlingExemptLocked(callerIdentity)); + } + } } diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index e7d7434b5dc8..5da281a5ebc3 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -860,7 +860,7 @@ class StorageManagerService extends IStorageManager.Stub } else if (remote == 1) { res = true; } else { - res = false; + res = true; } Slog.d(TAG, "Isolated storage local flag " + local + " and remote flag " @@ -1533,7 +1533,7 @@ class StorageManagerService extends IStorageManager.Stub // Snapshot feature flag used for this boot SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString( - SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false))); + SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, true))); mContext = context; mResolver = mContext.getContentResolver(); diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java index 3939bee52aa2..9bbc3158757c 100644 --- a/services/core/java/com/android/server/WiredAccessoryManager.java +++ b/services/core/java/com/android/server/WiredAccessoryManager.java @@ -23,6 +23,7 @@ import android.os.Message; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.UEventObserver; +import android.util.Pair; import android.util.Slog; import android.media.AudioManager; import android.util.Log; @@ -31,6 +32,7 @@ import android.view.InputDevice; import com.android.internal.R; import com.android.server.input.InputManagerService; import com.android.server.input.InputManagerService.WiredAccessoryCallbacks; + import static com.android.server.input.InputManagerService.SW_HEADPHONE_INSERT; import static com.android.server.input.InputManagerService.SW_MICROPHONE_INSERT; import static com.android.server.input.InputManagerService.SW_LINEOUT_INSERT; @@ -41,6 +43,7 @@ import static com.android.server.input.InputManagerService.SW_LINEOUT_INSERT_BIT import java.io.File; import java.io.FileReader; import java.io.FileNotFoundException; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -52,7 +55,7 @@ import java.util.Locale; */ final class WiredAccessoryManager implements WiredAccessoryCallbacks { private static final String TAG = WiredAccessoryManager.class.getSimpleName(); - private static final boolean LOG = true; + private static final boolean LOG = false; private static final int BIT_HEADSET = (1 << 0); private static final int BIT_HEADSET_NO_MIC = (1 << 1); @@ -60,9 +63,9 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { private static final int BIT_USB_HEADSET_DGTL = (1 << 3); private static final int BIT_HDMI_AUDIO = (1 << 4); private static final int BIT_LINEOUT = (1 << 5); - private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC| - BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL| - BIT_HDMI_AUDIO|BIT_LINEOUT); + private static final int SUPPORTED_HEADSETS = (BIT_HEADSET | BIT_HEADSET_NO_MIC | + BIT_USB_HEADSET_ANLG | BIT_USB_HEADSET_DGTL | + BIT_HDMI_AUDIO | BIT_LINEOUT); private static final String NAME_H2W = "h2w"; private static final String NAME_USB_AUDIO = "usb_audio"; @@ -82,30 +85,34 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { private int mSwitchValues; private final WiredAccessoryObserver mObserver; + private final WiredAccessoryExtconObserver mExtconObserver; private final InputManagerService mInputManager; private final boolean mUseDevInputEventForAudioJack; public WiredAccessoryManager(Context context, InputManagerService inputManager) { - PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); + PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WiredAccessoryManager"); mWakeLock.setReferenceCounted(false); - mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); + mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mInputManager = inputManager; mUseDevInputEventForAudioJack = context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack); + mExtconObserver = new WiredAccessoryExtconObserver(); mObserver = new WiredAccessoryObserver(); } private void onSystemReady() { if (mUseDevInputEventForAudioJack) { int switchValues = 0; - if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_HEADPHONE_INSERT) == 1) { + if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_HEADPHONE_INSERT) + == 1) { switchValues |= SW_HEADPHONE_INSERT_BIT; } - if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_MICROPHONE_INSERT) == 1) { + if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_MICROPHONE_INSERT) + == 1) { switchValues |= SW_MICROPHONE_INSERT_BIT; } if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_LINEOUT_INSERT) == 1) { @@ -115,20 +122,31 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT); } - mObserver.init(); + + if (ExtconUEventObserver.extconExists()) { + if (mUseDevInputEventForAudioJack) { + Log.w(TAG, "Both input event and extcon are used for audio jack," + + " please just choose one."); + } + mExtconObserver.init(); + } else { + mObserver.init(); + } } @Override public void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask) { - if (LOG) Slog.v(TAG, "notifyWiredAccessoryChanged: when=" + whenNanos - + " bits=" + switchCodeToString(switchValues, switchMask) - + " mask=" + Integer.toHexString(switchMask)); + if (LOG) { + Slog.v(TAG, "notifyWiredAccessoryChanged: when=" + whenNanos + + " bits=" + switchCodeToString(switchValues, switchMask) + + " mask=" + Integer.toHexString(switchMask)); + } synchronized (mLock) { int headset; mSwitchValues = (mSwitchValues & ~switchMask) | switchValues; switch (mSwitchValues & - (SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT)) { + (SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT)) { case 0: headset = 0; break; @@ -155,7 +173,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { } updateLocked(NAME_H2W, - (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) | headset); + (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) | headset); } } @@ -175,7 +193,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { * results in support for the last one plugged in. Similarly, unplugging either is seen as * unplugging all. * - * @param newName One of the NAME_xxx variables defined above. + * @param newName One of the NAME_xxx variables defined above. * @param newState 0 or one of the BIT_xxx variables defined above. */ private void updateLocked(String newName, int newState) { @@ -186,10 +204,12 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { int h2w_headset = headsetState & (BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT); boolean h2wStateChange = true; boolean usbStateChange = true; - if (LOG) Slog.v(TAG, "newName=" + newName - + " newState=" + newState - + " headsetState=" + headsetState - + " prev headsetState=" + mHeadsetState); + if (LOG) { + Slog.v(TAG, "newName=" + newName + + " newState=" + newState + + " headsetState=" + headsetState + + " prev headsetState=" + mHeadsetState); + } if (mHeadsetState == headsetState) { Log.e(TAG, "No state change."); @@ -229,7 +249,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { public void handleMessage(Message msg) { switch (msg.what) { case MSG_NEW_DEVICE_STATE: - setDevicesState(msg.arg1, msg.arg2, (String)msg.obj); + setDevicesState(msg.arg1, msg.arg2, (String) msg.obj); mWakeLock.release(); break; case MSG_SYSTEM_READY: @@ -269,9 +289,9 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { if (headset == BIT_HEADSET) { outDevice = AudioManager.DEVICE_OUT_WIRED_HEADSET; inDevice = AudioManager.DEVICE_IN_WIRED_HEADSET; - } else if (headset == BIT_HEADSET_NO_MIC){ + } else if (headset == BIT_HEADSET_NO_MIC) { outDevice = AudioManager.DEVICE_OUT_WIRED_HEADPHONE; - } else if (headset == BIT_LINEOUT){ + } else if (headset == BIT_LINEOUT) { outDevice = AudioManager.DEVICE_OUT_LINE; } else if (headset == BIT_USB_HEADSET_ANLG) { outDevice = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET; @@ -280,7 +300,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { } else if (headset == BIT_HDMI_AUDIO) { outDevice = AudioManager.DEVICE_OUT_HDMI; } else { - Slog.e(TAG, "setDeviceState() invalid headset type: "+headset); + Slog.e(TAG, "setDeviceState() invalid headset type: " + headset); return; } @@ -290,10 +310,10 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { } if (outDevice != 0) { - mAudioManager.setWiredDeviceConnectionState(outDevice, state, "", headsetName); + mAudioManager.setWiredDeviceConnectionState(outDevice, state, "", headsetName); } if (inDevice != 0) { - mAudioManager.setWiredDeviceConnectionState(inDevice, state, "", headsetName); + mAudioManager.setWiredDeviceConnectionState(inDevice, state, "", headsetName); } } } @@ -340,7 +360,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { " not found while attempting to determine initial switch state"); } catch (Exception e) { Slog.e(TAG, "Error while attempting to determine initial switch state for " - + uei.getDevName() , e); + + uei.getDevName(), e); } } } @@ -350,7 +370,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { // observe three UEVENTs for (int i = 0; i < mUEventInfo.size(); ++i) { UEventInfo uei = mUEventInfo.get(i); - startObserving("DEVPATH="+uei.getDevPath()); + startObserving("DEVPATH=" + uei.getDevPath()); } } @@ -438,7 +458,9 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { mStateNbits = stateNbits; } - public String getDevName() { return mDevName; } + public String getDevName() { + return mDevName; + } public String getDevPath() { return String.format(Locale.US, "/devices/virtual/switch/%s", mDevName); @@ -456,11 +478,84 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { public int computeNewHeadsetState(int headsetState, int switchState) { int preserveMask = ~(mState1Bits | mState2Bits | mStateNbits); int setBits = ((switchState == 1) ? mState1Bits : - ((switchState == 2) ? mState2Bits : - ((switchState == mStateNbits) ? mStateNbits : 0))); + ((switchState == 2) ? mState2Bits : + ((switchState == mStateNbits) ? mStateNbits : 0))); return ((headsetState & preserveMask) | setBits); } } } + + private class WiredAccessoryExtconObserver extends ExtconStateObserver<Pair<Integer, Integer>> { + private final List<ExtconInfo> mExtconInfos; + + WiredAccessoryExtconObserver() { + mExtconInfos = ExtconInfo.getExtconInfos(".*audio.*"); + + } + + private void init() { + for (ExtconInfo extconInfo : mExtconInfos) { + Pair<Integer, Integer> state = null; + try { + state = parseStateFromFile(extconInfo); + } catch (FileNotFoundException e) { + Slog.w(TAG, extconInfo.getStatePath() + + " not found while attempting to determine initial state", e); + } catch (IOException e) { + Slog.e( + TAG, + "Error reading " + extconInfo.getStatePath() + + " while attempting to determine initial state", + e); + } + if (state != null) { + updateState(extconInfo, extconInfo.getName(), state); + } + if (LOG) Slog.d(TAG, "observing " + extconInfo.getName()); + startObserving(extconInfo); + } + + } + + @Override + public Pair<Integer, Integer> parseState(ExtconInfo extconInfo, String status) { + if (LOG) Slog.v(TAG, "status " + status); + int []maskAndState = {0,0}; + // extcon event state changes from kernel4.9 + // new state will be like STATE=MICROPHONE=1\nHEADPHONE=0 + updateBit(maskAndState, BIT_HEADSET_NO_MIC, status, "HEADPHONE") ; + updateBit(maskAndState, BIT_HEADSET, status,"MICROPHONE") ; + updateBit(maskAndState, BIT_HDMI_AUDIO, status,"HDMI") ; + updateBit(maskAndState, BIT_LINEOUT, status,"LINE-OUT") ; + if (LOG) Slog.v(TAG, "mask " + maskAndState[0] + " state " + maskAndState[1]); + return Pair.create(maskAndState[0],maskAndState[1]); + } + + @Override + public void updateState(ExtconInfo extconInfo, String name, + Pair<Integer, Integer> maskAndState) { + synchronized (mLock) { + int mask = maskAndState.first; + int state = maskAndState.second; + updateLocked(name, mHeadsetState | (mask & state) & ~(mask & ~state)); + return; + } + } + } + + /** + * Updates the mask bit at {@code position} to 1 and the state bit at {@code position} to true + * if {@code name=1} or false if {}@code name=0} is contained in {@code state}. + */ + private static void updateBit(int[] maskAndState, int position, String state, String name) { + maskAndState[0] |= position; + if (state.contains(name + "=1")) { + maskAndState[0] |= position; + maskAndState[1] |= position; + } else if (state.contains(name + "=0")) { + maskAndState[0] |= position; + maskAndState[1] &= ~position; + } + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 60a45bfe04bb..c4a9db6b7262 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -6630,8 +6630,7 @@ public class ActivityManagerService extends IActivityManager.Stub String msg; if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) { - throw new SecurityException("Content provider lookup " - + cpr.name.flattenToShortString() + throw new SecurityException("Content provider lookup " + name + " failed: association not allowed with package " + msg); } checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission"); diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index 01946247bd12..8ffb67a1405c 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -65,6 +65,8 @@ final class CoreSettingsObserver extends ContentObserver { Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES, String.class); sGlobalSettingToTypeMap.put( Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST, String.class); + sGlobalSettingToTypeMap.put( + Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX, String.class); sGlobalSettingToTypeMap.put(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, int.class); sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_APP, String.class); sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS, String.class); @@ -76,6 +78,7 @@ final class CoreSettingsObserver extends ContentObserver { sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_OPT_OUT_APPS, String.class); sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_BLACKLIST, String.class); sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_WHITELIST, String.class); + sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_BLACKLISTS, String.class); // add other global settings here... } diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java index 7ae00af626c8..b9aa34e89216 100644 --- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java +++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java @@ -18,6 +18,7 @@ package com.android.server.display.whitebalance; import android.annotation.NonNull; import android.util.Slog; +import android.util.Spline; import com.android.internal.util.Preconditions; import com.android.server.LocalServices; @@ -75,6 +76,9 @@ public class DisplayWhiteBalanceController implements // Override the ambient color temperature for debugging purposes. private float mAmbientColorTemperatureOverride; + // A piecewise linear relationship between ambient and display color temperatures + private Spline.LinearSpline mAmbientToDisplayTemperatureSpline; + /** * @param brightnessSensor * The sensor used to detect changes in the ambient brightness. @@ -109,7 +113,8 @@ public class DisplayWhiteBalanceController implements @NonNull AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor, @NonNull AmbientFilter colorTemperatureFilter, @NonNull DisplayWhiteBalanceThrottler throttler, - float lowLightAmbientBrightnessThreshold, float lowLightAmbientColorTemperature) { + float lowLightAmbientBrightnessThreshold, float lowLightAmbientColorTemperature, + float[] ambientTemperatures, float[] displayTemperatures) { validateArguments(brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter, throttler); mLoggingEnabled = false; @@ -127,6 +132,14 @@ public class DisplayWhiteBalanceController implements mLastAmbientColorTemperature = -1.0f; mAmbientColorTemperatureHistory = new History(HISTORY_SIZE); mAmbientColorTemperatureOverride = -1.0f; + + try { + mAmbientToDisplayTemperatureSpline = new Spline.LinearSpline(ambientTemperatures, + displayTemperatures); + } catch (Exception e) { + mAmbientToDisplayTemperatureSpline = null; + } + mColorDisplayServiceInternal = LocalServices.getService(ColorDisplayServiceInternal.class); } @@ -227,6 +240,9 @@ public class DisplayWhiteBalanceController implements writer.println(" mLastAmbientColorTemperature=" + mLastAmbientColorTemperature); writer.println(" mAmbientColorTemperatureHistory=" + mAmbientColorTemperatureHistory); writer.println(" mAmbientColorTemperatureOverride=" + mAmbientColorTemperatureOverride); + writer.println(" mAmbientToDisplayTemperatureSpline=" + + (mAmbientToDisplayTemperatureSpline == null ? "unused" : + mAmbientToDisplayTemperatureSpline)); } @Override // AmbientSensor.AmbientBrightnessSensor.Callbacks @@ -250,6 +266,11 @@ public class DisplayWhiteBalanceController implements final long time = System.currentTimeMillis(); float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time); + if (mAmbientToDisplayTemperatureSpline != null) { + ambientColorTemperature = + mAmbientToDisplayTemperatureSpline.interpolate(ambientColorTemperature); + } + final float ambientBrightness = mBrightnessFilter.getEstimate(time); if (ambientBrightness < mLowLightAmbientBrightnessThreshold) { if (mLoggingEnabled) { diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java index fd78ddbda9c8..56f4ca339eb3 100644 --- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java +++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java @@ -67,9 +67,14 @@ public class DisplayWhiteBalanceFactory { final float lowLightAmbientColorTemperature = getFloat(resources, com.android.internal.R.dimen .config_displayWhiteBalanceLowLightAmbientColorTemperature); + final float[] ambientTemperatures = getFloatArray(resources, + com.android.internal.R.array.config_displayWhiteBalanceAmbientTemperatureValues); + final float[] displayTemperatures = getFloatArray(resources, + com.android.internal.R.array.config_displayWhiteBalanceDisplayTemperatureValues); final DisplayWhiteBalanceController controller = new DisplayWhiteBalanceController( brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter, - throttler, lowLightAmbientBrightnessThreshold, lowLightAmbientColorTemperature); + throttler, lowLightAmbientBrightnessThreshold, lowLightAmbientColorTemperature, + ambientTemperatures, displayTemperatures); brightnessSensor.setCallbacks(controller); colorTemperatureSensor.setCallbacks(controller); return controller; diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index 20933db803b9..560f7a03b20f 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -342,7 +342,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { super.disableDevice(initiatedByCec, callback); assertRunOnServiceThread(); - if (!initiatedByCec && mIsActiveSource) { + if (!initiatedByCec && mIsActiveSource && mService.isControlEnabled()) { mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource( mAddress, mService.getPhysicalAddress())); } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 5e7ea05f799c..28393a209111 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -1805,9 +1805,10 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. - private int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) { + private int interceptMotionBeforeQueueingNonInteractive(int displayId, + long whenNanos, int policyFlags) { return mWindowManagerCallbacks.interceptMotionBeforeQueueingNonInteractive( - whenNanos, policyFlags); + displayId, whenNanos, policyFlags); } // Native callback. @@ -2021,7 +2022,13 @@ public class InputManagerService extends IInputManager.Stub public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags); - public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags); + /** + * Provides an opportunity for the window manager policy to intercept early motion event + * processing when the device is in a non-interactive state since these events are normally + * dropped. + */ + int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos, + int policyFlags); public long interceptKeyBeforeDispatching(IBinder token, KeyEvent event, int policyFlags); diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java index f03c99b0272e..aa8a25a36333 100644 --- a/services/core/java/com/android/server/location/RemoteListenerHelper.java +++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java @@ -31,9 +31,11 @@ import java.util.HashMap; import java.util.Map; /** - * A helper class, that handles operations in remote listeners, and tracks for remote process death. + * A helper class that handles operations in remote listeners. + * + * @param <TListener> the type of GNSS data listener. */ -abstract class RemoteListenerHelper<TListener extends IInterface> { +public abstract class RemoteListenerHelper<TListener extends IInterface> { protected static final int RESULT_SUCCESS = 0; protected static final int RESULT_NOT_AVAILABLE = 1; @@ -46,7 +48,7 @@ abstract class RemoteListenerHelper<TListener extends IInterface> { protected final Handler mHandler; private final String mTag; - private final Map<IBinder, LinkedListener> mListenerMap = new HashMap<>(); + private final Map<IBinder, IdentifiedListener> mListenerMap = new HashMap<>(); protected final Context mContext; protected final AppOpsManager mAppOps; @@ -71,24 +73,21 @@ abstract class RemoteListenerHelper<TListener extends IInterface> { return mIsRegistered; } - public boolean addListener(@NonNull TListener listener, CallerIdentity callerIdentity) { + /** + * Adds GNSS data listener {@code listener} with caller identify {@code callerIdentify}. + */ + public void addListener(@NonNull TListener listener, CallerIdentity callerIdentity) { Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener."); IBinder binder = listener.asBinder(); - LinkedListener deathListener = new LinkedListener(listener, callerIdentity); synchronized (mListenerMap) { if (mListenerMap.containsKey(binder)) { // listener already added - return true; - } - try { - binder.linkToDeath(deathListener, 0 /* flags */); - } catch (RemoteException e) { - // if the remote process registering the listener is already death, just swallow the - // exception and return - Log.v(mTag, "Remote listener already died.", e); - return false; + return; } - mListenerMap.put(binder, deathListener); + + IdentifiedListener identifiedListener = new IdentifiedListener(listener, + callerIdentity); + mListenerMap.put(binder, identifiedListener); // update statuses we already know about, starting from the ones that will never change int result; @@ -107,26 +106,23 @@ abstract class RemoteListenerHelper<TListener extends IInterface> { } else { // at this point if the supported flag is not set, the notification will be sent // asynchronously in the future - return true; + return; } - post(deathListener, getHandlerOperation(result)); + post(identifiedListener, getHandlerOperation(result)); } - return true; } + /** + * Remove GNSS data listener {@code listener}. + */ public void removeListener(@NonNull TListener listener) { Preconditions.checkNotNull(listener, "Attempted to remove a 'null' listener."); - IBinder binder = listener.asBinder(); - LinkedListener linkedListener; synchronized (mListenerMap) { - linkedListener = mListenerMap.remove(binder); + mListenerMap.remove(listener.asBinder()); if (mListenerMap.isEmpty()) { tryUnregister(); } } - if (linkedListener != null) { - binder.unlinkToDeath(linkedListener, 0 /* flags */); - } } protected abstract boolean isAvailableInPlatform(); @@ -198,14 +194,15 @@ abstract class RemoteListenerHelper<TListener extends IInterface> { } private void foreachUnsafe(ListenerOperation<TListener> operation) { - for (LinkedListener linkedListener : mListenerMap.values()) { - post(linkedListener, operation); + for (IdentifiedListener identifiedListener : mListenerMap.values()) { + post(identifiedListener, operation); } } - private void post(LinkedListener linkedListener, ListenerOperation<TListener> operation) { + private void post(IdentifiedListener identifiedListener, + ListenerOperation<TListener> operation) { if (operation != null) { - mHandler.post(new HandlerRunnable(linkedListener, operation)); + mHandler.post(new HandlerRunnable(identifiedListener, operation)); } } @@ -259,35 +256,31 @@ abstract class RemoteListenerHelper<TListener extends IInterface> { return RESULT_SUCCESS; } - private class LinkedListener implements IBinder.DeathRecipient { + private class IdentifiedListener { private final TListener mListener; private final CallerIdentity mCallerIdentity; - LinkedListener(@NonNull TListener listener, CallerIdentity callerIdentity) { + private IdentifiedListener(@NonNull TListener listener, CallerIdentity callerIdentity) { mListener = listener; mCallerIdentity = callerIdentity; } - - @Override - public void binderDied() { - Log.d(mTag, "Remote Listener died: " + mListener); - removeListener(mListener); - } } private class HandlerRunnable implements Runnable { - private final LinkedListener mLinkedListener; + private final IdentifiedListener mIdentifiedListener; private final ListenerOperation<TListener> mOperation; - HandlerRunnable(LinkedListener linkedListener, ListenerOperation<TListener> operation) { - mLinkedListener = linkedListener; + private HandlerRunnable(IdentifiedListener identifiedListener, + ListenerOperation<TListener> operation) { + mIdentifiedListener = identifiedListener; mOperation = operation; } @Override public void run() { try { - mOperation.execute(mLinkedListener.mListener, mLinkedListener.mCallerIdentity); + mOperation.execute(mIdentifiedListener.mListener, + mIdentifiedListener.mCallerIdentity); } catch (RemoteException e) { Log.v(mTag, "Error in monitored listener.", e); } diff --git a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java index 5c7317892f3f..62d9b20b636d 100644 --- a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java @@ -284,13 +284,16 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { * Tells the system UI that volume has changed on an active remote session. */ public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) { - if (mRvc == null || !session.isActive()) { - return; - } - try { - mRvc.remoteVolumeChanged(session.getSessionToken(), flags); - } catch (Exception e) { - Log.wtf(TAG, "Error sending volume change to system UI.", e); + synchronized (mLock) { + if (mRvc == null || !session.isActive()) { + return; + } + try { + mRvc.remoteVolumeChanged(session.getSessionToken(), flags); + } catch (Exception e) { + Log.w(TAG, "Error sending volume change to system UI.", e); + mRvc = null; + } } } @@ -563,7 +566,7 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { String callerPackageName, SessionCallbackLink cb, String tag) { FullUserRecord user = getFullUserRecordLocked(userId); if (user == null) { - Log.wtf(TAG, "Request from invalid user: " + userId); + Log.w(TAG, "Request from invalid user: " + userId + ", pkg=" + callerPackageName); throw new RuntimeException("Session request from invalid user."); } @@ -643,7 +646,8 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId); mRvc.updateRemoteController(record == null ? null : record.getSessionToken()); } catch (RemoteException e) { - Log.wtf(TAG, "Error sending default remote volume to sys ui.", e); + Log.w(TAG, "Error sending default remote volume to sys ui.", e); + mRvc = null; } } } @@ -1661,7 +1665,9 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { final long token = Binder.clearCallingIdentity(); try { enforceSystemUiPermission("listen for volume changes", pid, uid); - mRvc = rvc; + synchronized (mLock) { + mRvc = rvc; + } } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java new file mode 100644 index 000000000000..dac4b6ff39c3 --- /dev/null +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -0,0 +1,221 @@ +/* + * 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.s + */ + +package com.android.server.pm; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.apex.ApexInfo; +import android.apex.ApexInfoList; +import android.apex.ApexSessionInfo; +import android.apex.IApexService; +import android.content.pm.PackageInfo; +import android.content.pm.PackageParser; +import android.content.pm.PackageParser.PackageParserException; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Slog; + +import com.android.internal.util.IndentingPrintWriter; + +import java.io.File; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * ApexManager class handles communications with the apex service to perform operation and queries, + * as well as providing caching to avoid unnecessary calls to the service. + */ +class ApexManager { + static final String TAG = "ApexManager"; + private final IApexService mApexService; + private final Map<String, PackageInfo> mActivePackagesCache; + + ApexManager() { + mApexService = IApexService.Stub.asInterface( + ServiceManager.getService("apexservice")); + mActivePackagesCache = populateActivePackagesCache(); + } + + @NonNull + private Map<String, PackageInfo> populateActivePackagesCache() { + try { + List<PackageInfo> list = new ArrayList<>(); + final ApexInfo[] activePkgs = mApexService.getActivePackages(); + for (ApexInfo ai : activePkgs) { + // If the device is using flattened APEX, don't report any APEX + // packages since they won't be managed or updated by PackageManager. + if ((new File(ai.packagePath)).isDirectory()) { + break; + } + try { + list.add(PackageParser.generatePackageInfoFromApex( + new File(ai.packagePath), true /* collect certs */)); + } catch (PackageParserException pe) { + throw new IllegalStateException("Unable to parse: " + ai, pe); + } + } + return list.stream().collect(Collectors.toMap(p -> p.packageName, Function.identity())); + } catch (RemoteException re) { + Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString()); + throw new RuntimeException(re); + } + } + + /** + * Retrieves information about an active APEX package. + * + * @param packageName the package name to look for. Note that this is the package name reported + * in the APK container manifest (i.e. AndroidManifest.xml), which might + * differ from the one reported in the APEX manifest (i.e. + * apex_manifest.json). + * @return a PackageInfo object with the information about the package, or null if the package + * is not found. + */ + @Nullable PackageInfo getActivePackage(String packageName) { + return mActivePackagesCache.get(packageName); + } + + /** + * Retrieves information about all active APEX packages. + * + * @return a Collection of PackageInfo object, each one containing information about a different + * active package. + */ + Collection<PackageInfo> getActivePackages() { + return mActivePackagesCache.values(); + } + + /** + * Retrieves information about an apexd staged session i.e. the internal state used by apexd to + * track the different states of a session. + * + * @param sessionId the identifier of the session. + * @return an ApexSessionInfo object, or null if the session is not known. + */ + @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) { + try { + ApexSessionInfo apexSessionInfo = mApexService.getStagedSessionInfo(sessionId); + if (apexSessionInfo.isUnknown) { + return null; + } + return apexSessionInfo; + } catch (RemoteException re) { + Slog.e(TAG, "Unable to contact apexservice", re); + throw new RuntimeException(re); + } + } + + /** + * Submit a staged session to apex service. This causes the apex service to perform some initial + * verification and accept or reject the session. Submitting a session successfully is not + * enough for it to be activated at the next boot, the caller needs to call + * {@link #markStagedSessionReady(int)}. + * + * @param sessionId the identifier of the {@link PackageInstallerSession} being submitted. + * @param childSessionIds if {@code sessionId} is a multi-package session, this should contain + * an array of identifiers of all the child sessions. Otherwise it should + * be an empty array. + * @param apexInfoList this is an output parameter, which needs to be initialized by tha caller + * and will be filled with a list of {@link ApexInfo} objects, each of which + * contains metadata about one of the packages being submitted as part of + * the session. + * @return whether the submission of the session was successful. + */ + boolean submitStagedSession( + int sessionId, @NonNull int[] childSessionIds, @NonNull ApexInfoList apexInfoList) { + try { + return mApexService.submitStagedSession(sessionId, childSessionIds, apexInfoList); + } catch (RemoteException re) { + Slog.e(TAG, "Unable to contact apexservice", re); + throw new RuntimeException(re); + } + } + + /** + * Mark a staged session previously submitted using {@cde submitStagedSession} as ready to be + * applied at next reboot. + * + * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as ready. + * @return true upon success, false if the session is unknown. + */ + boolean markStagedSessionReady(int sessionId) { + try { + return mApexService.markStagedSessionReady(sessionId); + } catch (RemoteException re) { + Slog.e(TAG, "Unable to contact apexservice", re); + throw new RuntimeException(re); + } + } + + /** + * Dumps various state information to the provided {@link PrintWriter} object. + * + * @param pw the {@link PrintWriter} object to send information to. + * @param packageName a {@link String} containing a package name, or {@code null}. If set, only + * information about that specific package will be dumped. + */ + void dump(PrintWriter pw, @Nullable String packageName) { + final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); + ipw.println(); + ipw.println("Active APEX packages:"); + ipw.increaseIndent(); + try { + populateActivePackagesCache(); + for (PackageInfo pi : mActivePackagesCache.values()) { + if (packageName != null && !packageName.equals(pi.packageName)) { + continue; + } + ipw.println(pi.packageName); + ipw.increaseIndent(); + ipw.println("Version: " + pi.versionCode); + ipw.println("Path: " + pi.applicationInfo.sourceDir); + ipw.decreaseIndent(); + } + ipw.decreaseIndent(); + ipw.println(); + ipw.println("APEX session state:"); + ipw.increaseIndent(); + final ApexSessionInfo[] sessions = mApexService.getSessions(); + for (ApexSessionInfo si : sessions) { + ipw.println("Session ID: " + Integer.toString(si.sessionId)); + ipw.increaseIndent(); + if (si.isUnknown) { + ipw.println("State: UNKNOWN"); + } else if (si.isVerified) { + ipw.println("State: VERIFIED"); + } else if (si.isStaged) { + ipw.println("State: STAGED"); + } else if (si.isActivated) { + ipw.println("State: ACTIVATED"); + } else if (si.isActivationPendingRetry) { + ipw.println("State: ACTIVATION PENDING RETRY"); + } else if (si.isActivationFailed) { + ipw.println("State: ACTIVATION FAILED"); + } + ipw.decreaseIndent(); + } + ipw.decreaseIndent(); + } catch (RemoteException e) { + ipw.println("Couldn't communicate with apexd."); + } + } +} diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 21965e4e83a2..86083494f3d6 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -186,7 +186,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } }; - public PackageInstallerService(Context context, PackageManagerService pm) { + public PackageInstallerService(Context context, PackageManagerService pm, ApexManager am) { mContext = context; mPm = pm; mPermissionManager = LocalServices.getService(PermissionManagerInternal.class); @@ -204,7 +204,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions"); mSessionsDir.mkdirs(); - mStagingManager = new StagingManager(pm, this); + mStagingManager = new StagingManager(pm, this, am); } private void setBootCompleted() { @@ -481,6 +481,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } } + if (params.isStaged) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, TAG); + } + if (!params.isMultiPackage) { // Only system components can circumvent runtime permissions when installing. if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0 diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 874d1a719ee6..400443a16fab 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -119,13 +119,11 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; -import android.apex.ApexInfo; -import android.apex.ApexSessionInfo; -import android.apex.IApexService; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppDetailsActivity; import android.app.AppOpsManager; +import android.app.BroadcastOptions; import android.app.IActivityManager; import android.app.ResourcesManager; import android.app.admin.IDevicePolicyManager; @@ -241,6 +239,7 @@ import android.os.storage.StorageManagerInternal; import android.os.storage.VolumeInfo; import android.os.storage.VolumeRecord; import android.permission.PermissionControllerManager; +import android.provider.DeviceConfig; import android.provider.MediaStore; import android.provider.Settings.Global; import android.provider.Settings.Secure; @@ -733,10 +732,10 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mPackages") final private ArraySet<PackageListObserver> mPackageListObservers = new ArraySet<>(); - private PackageManager mPackageManager; - private final ModuleInfoProvider mModuleInfoProvider; + private final ApexManager mApexManager; + class PackageParserCallback implements PackageParser.Callback { @Override public final boolean hasFeature(String feature) { return PackageManagerService.this.hasSystemFeature(feature, 0); @@ -1046,12 +1045,17 @@ public class PackageManagerService extends IPackageManager.Stub verificationIntent.setComponent(mIntentFilterVerifierComponent); verificationIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + final long whitelistTimeout = getVerificationTimeout(); + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setTemporaryAppWhitelistDuration(whitelistTimeout); + DeviceIdleController.LocalService idleController = getDeviceIdleController(); idleController.addPowerSaveTempWhitelistApp(Process.myUid(), - mIntentFilterVerifierComponent.getPackageName(), getVerificationTimeout(), + mIntentFilterVerifierComponent.getPackageName(), whitelistTimeout, UserHandle.USER_SYSTEM, true, "intent filter verifier"); - mContext.sendBroadcastAsUser(verificationIntent, UserHandle.SYSTEM); + mContext.sendBroadcastAsUser(verificationIntent, UserHandle.SYSTEM, + null, options.toBundle()); if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Sending IntentFilter verification broadcast"); } @@ -3074,7 +3078,8 @@ public class PackageManagerService extends IPackageManager.Stub } } - mInstallerService = new PackageInstallerService(context, this); + mApexManager = new ApexManager(); + mInstallerService = new PackageInstallerService(context, this, mApexManager); final Pair<ComponentName, String> instantAppResolverComponent = getInstantAppResolverLPr(); if (instantAppResolverComponent != null) { @@ -3934,27 +3939,7 @@ public class PackageManagerService extends IPackageManager.Stub } // if (!matchFactoryOnly && (flags & MATCH_APEX) != 0) { - //TODO(b/123052859) Don't do file operations every time there is a query. - final IApexService apex = IApexService.Stub.asInterface( - ServiceManager.getService("apexservice")); - if (apex != null) { - try { - final ApexInfo activePkg = apex.getActivePackage(packageName); - if (activePkg != null && !TextUtils.isEmpty(activePkg.packagePath)) { - try { - return PackageParser.generatePackageInfoFromApex( - new File(activePkg.packagePath), true /* collect certs */); - } catch (PackageParserException pe) { - Log.e(TAG, "Unable to parse package at " - + activePkg.packagePath, pe); - } - } - } catch (RemoteException e) { - Log.e(TAG, "Unable to retrieve packages from apexservice: " + e.toString()); - } - } else { - Log.e(TAG, "Unable to connect to apexservice for querying packages."); - } + return mApexManager.getActivePackage(packageName); } } return null; @@ -7851,25 +7836,7 @@ public class PackageManagerService extends IPackageManager.Stub if (listApex) { // TODO(b/119767311): include uninstalled/inactive APEX if // MATCH_UNINSTALLED_PACKAGES is set. - final IApexService apex = IApexService.Stub.asInterface( - ServiceManager.getService("apexservice")); - if (apex != null) { - try { - final ApexInfo[] activePkgs = apex.getActivePackages(); - for (ApexInfo ai : activePkgs) { - try { - list.add(PackageParser.generatePackageInfoFromApex( - new File(ai.packagePath), true /* collect certs */)); - } catch (PackageParserException pe) { - throw new IllegalStateException("Unable to parse: " + ai, pe); - } - } - } catch (RemoteException e) { - Log.e(TAG, "Unable to retrieve packages from apexservice: " + e.toString()); - } - } else { - Log.e(TAG, "Unable to connect to apexservice for querying packages."); - } + list.addAll(mApexManager.getActivePackages()); } return new ParceledListSlice<>(list); } @@ -14604,14 +14571,19 @@ public class PackageManagerService extends IPackageManager.Stub new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - // TODO(ruhler) b/112431924 Have a configurable setting to - // allow changing the timeout and fall back to the default - // if no such specified. + // the duration to wait for rollback to be enabled, in millis + long rollbackTimeout = DEFAULT_ENABLE_ROLLBACK_TIMEOUT; + try { + rollbackTimeout = Long.valueOf( + DeviceConfig.getProperty( + DeviceConfig.Rollback.NAMESPACE, + DeviceConfig.Rollback.ENABLE_ROLLBACK_TIMEOUT)); + } catch (NumberFormatException ignore) { + } final Message msg = mHandler.obtainMessage( ENABLE_ROLLBACK_TIMEOUT); msg.arg1 = enableRollbackToken; - mHandler.sendMessageDelayed(msg, - DEFAULT_ENABLE_ROLLBACK_TIMEOUT); + mHandler.sendMessageDelayed(msg, rollbackTimeout); } }, null, 0, null, null); @@ -21319,51 +21291,7 @@ public class PackageManagerService extends IPackageManager.Stub } if (!checkin && dumpState.isDumping(DumpState.DUMP_APEX)) { - final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); - ipw.println(); - ipw.println("Active APEX packages:"); - ipw.increaseIndent(); - final IApexService apex = IApexService.Stub.asInterface( - ServiceManager.getService("apexservice")); - try { - final ApexInfo[] activeApexes = apex.getActivePackages(); - for (ApexInfo ai : activeApexes) { - if (packageName != null && !packageName.equals(ai.packageName)) { - continue; - } - ipw.println(ai.packageName); - ipw.increaseIndent(); - ipw.println("Version: " + Long.toString(ai.versionCode)); - ipw.println("Path: " + ai.packagePath); - ipw.decreaseIndent(); - } - ipw.decreaseIndent(); - ipw.println(); - ipw.println("APEX session state:"); - ipw.increaseIndent(); - final ApexSessionInfo[] sessions = apex.getSessions(); - for (ApexSessionInfo si : sessions) { - ipw.println("Session ID: " + Integer.toString(si.sessionId)); - ipw.increaseIndent(); - if (si.isUnknown) { - ipw.println("State: UNKNOWN"); - } else if (si.isVerified) { - ipw.println("State: VERIFIED"); - } else if (si.isStaged) { - ipw.println("State: STAGED"); - } else if (si.isActivated) { - ipw.println("State: ACTIVATED"); - } else if (si.isActivationPendingRetry) { - ipw.println("State: ACTIVATION PENDING RETRY"); - } else if (si.isActivationFailed) { - ipw.println("State: ACTIVATION FAILED"); - } - ipw.decreaseIndent(); - } - ipw.decreaseIndent(); - } catch (RemoteException e) { - ipw.println("Couldn't communicate with apexd."); - } + mApexManager.dump(pw, packageName); } } diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 84c8b606a9d9..d9a5eb901344 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -18,6 +18,7 @@ package com.android.server.pm; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.Person; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; @@ -33,6 +34,7 @@ import android.util.Log; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.pm.ShortcutService.DumpFilter; @@ -71,6 +73,7 @@ class ShortcutPackage extends ShortcutPackageItem { private static final String TAG_EXTRAS = "extras"; private static final String TAG_SHORTCUT = "shortcut"; private static final String TAG_CATEGORIES = "categories"; + private static final String TAG_PERSON = "person"; private static final String ATTR_NAME = "name"; private static final String ATTR_CALL_COUNT = "call-count"; @@ -96,6 +99,12 @@ class ShortcutPackage extends ShortcutPackageItem { private static final String ATTR_ICON_RES_NAME = "icon-resname"; private static final String ATTR_BITMAP_PATH = "bitmap-path"; + private static final String ATTR_PERSON_NAME = "name"; + private static final String ATTR_PERSON_URI = "uri"; + private static final String ATTR_PERSON_KEY = "key"; + private static final String ATTR_PERSON_IS_BOT = "is-bot"; + private static final String ATTR_PERSON_IS_IMPORTANT = "is-important"; + private static final String NAME_CATEGORIES = "categories"; private static final String TAG_STRING_ARRAY_XMLUTILS = "string-array"; @@ -1499,6 +1508,22 @@ class ShortcutPackage extends ShortcutPackageItem { out.endTag(null, TAG_CATEGORIES); } } + if (!forBackup) { // Don't backup the persons field. + final Person[] persons = si.getPersons(); + if (!ArrayUtils.isEmpty(persons)) { + for (int i = 0; i < persons.length; i++) { + final Person p = persons[i]; + + out.startTag(null, TAG_PERSON); + ShortcutService.writeAttr(out, ATTR_PERSON_NAME, p.getName()); + ShortcutService.writeAttr(out, ATTR_PERSON_URI, p.getUri()); + ShortcutService.writeAttr(out, ATTR_PERSON_KEY, p.getKey()); + ShortcutService.writeAttr(out, ATTR_PERSON_IS_BOT, p.isBot()); + ShortcutService.writeAttr(out, ATTR_PERSON_IS_IMPORTANT, p.isImportant()); + out.endTag(null, TAG_PERSON); + } + } + } final Intent[] intentsNoExtras = si.getIntentsNoExtras(); final PersistableBundle[] intentsExtras = si.getIntentPersistableExtrases(); final int numIntents = intentsNoExtras.length; @@ -1588,6 +1613,7 @@ class ShortcutPackage extends ShortcutPackageItem { String bitmapPath; int backupVersionCode; ArraySet<String> categories = null; + ArrayList<Person> persons = new ArrayList<>(); id = ShortcutService.parseStringAttribute(parser, ATTR_ID); activityComponent = ShortcutService.parseComponentNameAttribute(parser, @@ -1638,6 +1664,9 @@ class ShortcutPackage extends ShortcutPackageItem { case TAG_CATEGORIES: // This just contains string-array. continue; + case TAG_PERSON: + persons.add(parsePerson(parser)); + continue; case TAG_STRING_ARRAY_XMLUTILS: if (NAME_CATEGORIES.equals(ShortcutService.parseStringAttribute(parser, ATTR_NAME_XMLUTILS))) { @@ -1680,7 +1709,8 @@ class ShortcutPackage extends ShortcutPackageItem { categories, intents.toArray(new Intent[intents.size()]), rank, extras, lastChangedTimestamp, flags, - iconResId, iconResName, bitmapPath, disabledReason); + iconResId, iconResName, bitmapPath, disabledReason, + persons.toArray(new Person[persons.size()])); } private static Intent parseIntent(XmlPullParser parser) @@ -1713,6 +1743,20 @@ class ShortcutPackage extends ShortcutPackageItem { return intent; } + private static Person parsePerson(XmlPullParser parser) + throws IOException, XmlPullParserException { + CharSequence name = ShortcutService.parseStringAttribute(parser, ATTR_PERSON_NAME); + String uri = ShortcutService.parseStringAttribute(parser, ATTR_PERSON_URI); + String key = ShortcutService.parseStringAttribute(parser, ATTR_PERSON_KEY); + boolean isBot = ShortcutService.parseBooleanAttribute(parser, ATTR_PERSON_IS_BOT); + boolean isImportant = ShortcutService.parseBooleanAttribute(parser, + ATTR_PERSON_IS_IMPORTANT); + + Person.Builder builder = new Person.Builder(); + builder.setName(name).setUri(uri).setKey(key).setBot(isBot).setImportant(isImportant); + return builder.build(); + } + @VisibleForTesting List<ShortcutInfo> getAllShortcutsForTest() { return new ArrayList<>(mShortcuts.values()); diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java index 90f08c30139a..668fc88b6b58 100644 --- a/services/core/java/com/android/server/pm/ShortcutParser.java +++ b/services/core/java/com/android/server/pm/ShortcutParser.java @@ -449,7 +449,8 @@ public class ShortcutParser { iconResId, null, // icon res name null, // bitmap path - disabledReason); + disabledReason, + null /* persons */); } private static String parseCategory(ShortcutService service, AttributeSet attrs) { diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index fa8360b1e5ba..30c2281b07f1 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -20,12 +20,12 @@ import android.annotation.NonNull; import android.apex.ApexInfo; import android.apex.ApexInfoList; import android.apex.ApexSessionInfo; -import android.apex.IApexService; import android.content.Context; import android.content.IIntentReceiver; import android.content.IIntentSender; import android.content.Intent; import android.content.IntentSender; +import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionInfo; import android.content.pm.PackageManager; @@ -41,7 +41,6 @@ import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; -import android.text.TextUtils; import android.util.Slog; import android.util.SparseArray; import android.util.apk.ApkSignatureVerifier; @@ -68,14 +67,16 @@ public class StagingManager { private final PackageInstallerService mPi; private final PackageManagerService mPm; + private final ApexManager mApexManager; private final Handler mBgHandler; @GuardedBy("mStagedSessions") private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>(); - StagingManager(PackageManagerService pm, PackageInstallerService pi) { + StagingManager(PackageManagerService pm, PackageInstallerService pi, ApexManager am) { mPm = pm; mPi = pi; + mApexManager = am; mBgHandler = BackgroundThread.getHandler(); } @@ -100,7 +101,7 @@ public class StagingManager { return new ParceledListSlice<>(result); } - private static boolean validateApexSignature(String apexPath, String packageName) { + private boolean validateApexSignature(String apexPath, String packageName) { final SigningDetails signingDetails; try { signingDetails = ApkSignatureVerifier.verify(apexPath, SignatureSchemeVersion.JAR); @@ -109,17 +110,9 @@ public class StagingManager { return false; } - final IApexService apex = IApexService.Stub.asInterface( - ServiceManager.getService("apexservice")); - final ApexInfo apexInfo; - try { - apexInfo = apex.getActivePackage(packageName); - } catch (RemoteException re) { - Slog.e(TAG, "Unable to contact APEXD", re); - return false; - } + final PackageInfo packageInfo = mApexManager.getActivePackage(packageName); - if (apexInfo == null || TextUtils.isEmpty(apexInfo.packageName)) { + if (packageInfo == null) { // TODO: What is the right thing to do here ? This implies there's no active package // with the given name. This should never be the case in production (where we only // accept updates to existing APEXes) but may be required for testing. @@ -129,9 +122,10 @@ public class StagingManager { final SigningDetails existingSigningDetails; try { existingSigningDetails = ApkSignatureVerifier.verify( - apexInfo.packagePath, SignatureSchemeVersion.JAR); + packageInfo.applicationInfo.sourceDir, SignatureSchemeVersion.JAR); } catch (PackageParserException e) { - Slog.e(TAG, "Unable to parse APEX package: " + apexInfo.packagePath, e); + Slog.e(TAG, "Unable to parse APEX package: " + + packageInfo.applicationInfo.sourceDir, e); return false; } @@ -143,10 +137,10 @@ public class StagingManager { return false; } - private static boolean submitSessionToApexService(@NonNull PackageInstallerSession session, - List<PackageInstallerSession> childSessions, - ApexInfoList apexInfoList) { - return sendSubmitStagedSessionRequest( + private boolean submitSessionToApexService(@NonNull PackageInstallerSession session, + List<PackageInstallerSession> childSessions, + ApexInfoList apexInfoList) { + return mApexManager.submitStagedSession( session.sessionId, childSessions != null ? childSessions.stream().mapToInt(s -> s.sessionId).toArray() : @@ -154,33 +148,6 @@ public class StagingManager { apexInfoList); } - private static boolean sendSubmitStagedSessionRequest( - int sessionId, int[] childSessionIds, ApexInfoList apexInfoList) { - final IApexService apex = IApexService.Stub.asInterface( - ServiceManager.getService("apexservice")); - boolean success; - try { - success = apex.submitStagedSession(sessionId, childSessionIds, apexInfoList); - } catch (RemoteException re) { - Slog.e(TAG, "Unable to contact apexservice", re); - return false; - } - return success; - } - - private static boolean sendMarkStagedSessionReadyRequest(int sessionId) { - final IApexService apex = IApexService.Stub.asInterface( - ServiceManager.getService("apexservice")); - boolean success; - try { - success = apex.markStagedSessionReady(sessionId); - } catch (RemoteException re) { - Slog.e(TAG, "Unable to contact apexservice", re); - return false; - } - return success; - } - private static boolean isApexSession(@NonNull PackageInstallerSession session) { return (session.params.installFlags & PackageManager.INSTALL_APEX) != 0; } @@ -260,7 +227,7 @@ public class StagingManager { } session.setStagedSessionReady(); - if (!sendMarkStagedSessionReadyRequest(session.sessionId)) { + if (!mApexManager.markStagedSessionReady(session.sessionId)) { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, "APEX staging failed, check logcat messages from apexd for more " + "details."); @@ -284,16 +251,12 @@ public class StagingManager { private void resumeSession(@NonNull PackageInstallerSession session) { if (sessionContainsApex(session)) { - // Check with apexservice whether the apex - // packages have been activated. - final IApexService apex = IApexService.Stub.asInterface( - ServiceManager.getService("apexservice")); - ApexSessionInfo apexSessionInfo; - try { - apexSessionInfo = apex.getStagedSessionInfo(session.sessionId); - } catch (RemoteException re) { - Slog.e(TAG, "Unable to contact apexservice", re); - // TODO should we retry here? Mark the session as failed? + // Check with apexservice whether the apex packages have been activated. + ApexSessionInfo apexSessionInfo = mApexManager.getStagedSessionInfo(session.sessionId); + if (apexSessionInfo == null) { + session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, + "apexd did not know anything about a staged session supposed to be" + + "activated"); return; } if (apexSessionInfo.isActivationFailed || apexSessionInfo.isUnknown) { @@ -323,8 +286,8 @@ public class StagingManager { // The APEX part of the session is activated, proceed with the installation of APKs. if (!installApksInSession(session)) { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, - "APEX activation failed. Check logcat messages from apexd for " - + "more information."); + "Staged installation of APKs failed. Check logcat messages for" + + "more information."); return; } session.setStagedSessionApplied(); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index b00193f5453f..2e3e3e430839 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -22,7 +22,6 @@ import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; import static android.app.AppOpsManager.OP_TOAST_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.content.Context.CONTEXT_RESTRICTED; -import static android.content.Context.DISPLAY_SERVICE; import static android.content.Context.WINDOW_SERVICE; import static android.content.pm.PackageManager.FEATURE_HDMI_CEC; import static android.content.pm.PackageManager.FEATURE_LEANBACK; @@ -36,6 +35,7 @@ import static android.provider.Settings.Secure.VOLUME_HUSH_OFF; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.Display.STATE_OFF; +import static android.view.KeyEvent.KEYCODE_UNKNOWN; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW; @@ -84,10 +84,8 @@ import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED; -import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs - .CAMERA_LENS_COVER_ABSENT; -import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs - .CAMERA_LENS_UNCOVERED; +import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT; +import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN; import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_DELEGATE; @@ -371,6 +369,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { IStatusBarService mStatusBarService; StatusBarManagerInternal mStatusBarManagerInternal; AudioManagerInternal mAudioManagerInternal; + DisplayManager mDisplayManager; boolean mPreloadedRecentApps; final Object mServiceAquireLock = new Object(); Vibrator mVibrator; // Vibrator for giving feedback of orientation changes @@ -1717,7 +1716,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class); mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); - mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); + mAppOpsManager = mContext.getSystemService(AppOpsManager.class); + mDisplayManager = mContext.getSystemService(DisplayManager.class); mHasFeatureWatch = mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH); mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK); mHasFeatureHdmiCec = mContext.getPackageManager().hasSystemFeature(FEATURE_HDMI_CEC); @@ -2508,8 +2508,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { return context; } - final DisplayManager dm = (DisplayManager) context.getSystemService(DISPLAY_SERVICE); - final Display targetDisplay = dm.getDisplay(displayId); + final Display targetDisplay = mDisplayManager.getDisplay(displayId); if (targetDisplay == null) { // Failed to obtain the non-default display where splash screen should be shown, // lets not show at all. @@ -3655,7 +3654,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Reset the pending key mPendingWakeKey = PENDING_KEY_NULL; } - } else if (!interactive && shouldDispatchInputWhenNonInteractive(event)) { + } else if (!interactive && shouldDispatchInputWhenNonInteractive(displayId, keyCode)) { // If we're currently dozing with the screen on and the keyguard showing, pass the key // to the application but preserve its wake key status to make sure we still move // from dozing to fully interactive if we would normally go from off to fully @@ -4126,7 +4125,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // TODO(b/117479243): handle it in InputPolicy /** {@inheritDoc} */ @Override - public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) { + public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos, + int policyFlags) { if ((policyFlags & FLAG_WAKE) != 0) { if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotion, PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION")) { @@ -4134,7 +4134,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - if (shouldDispatchInputWhenNonInteractive(null)) { + if (shouldDispatchInputWhenNonInteractive(displayId, KEYCODE_UNKNOWN)) { return ACTION_PASS_TO_USER; } @@ -4149,9 +4149,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { return 0; } - private boolean shouldDispatchInputWhenNonInteractive(KeyEvent event) { - final boolean displayOff = (mDefaultDisplay == null - || mDefaultDisplay.getState() == STATE_OFF); + private boolean shouldDispatchInputWhenNonInteractive(int displayId, int keyCode) { + // Apply the default display policy to unknown displays as well. + final boolean isDefaultDisplay = displayId == DEFAULT_DISPLAY + || displayId == INVALID_DISPLAY; + final Display display = isDefaultDisplay + ? mDefaultDisplay + : mDisplayManager.getDisplay(displayId); + final boolean displayOff = (display == null + || display.getState() == STATE_OFF); if (displayOff && !mHasFeatureWatch) { return false; @@ -4163,25 +4169,25 @@ public class PhoneWindowManager implements WindowManagerPolicy { } // Watches handle BACK specially - if (mHasFeatureWatch - && event != null - && (event.getKeyCode() == KeyEvent.KEYCODE_BACK - || event.getKeyCode() == KeyEvent.KEYCODE_STEM_PRIMARY)) { + if (mHasFeatureWatch && (keyCode == KeyEvent.KEYCODE_BACK + || keyCode == KeyEvent.KEYCODE_STEM_PRIMARY)) { return false; } - // Send events to a dozing dream even if the screen is off since the dream - // is in control of the state of the screen. - IDreamManager dreamManager = getDreamManager(); + // TODO(b/123372519): Refine when dream can support multi display. + if (isDefaultDisplay) { + // Send events to a dozing dream even if the screen is off since the dream + // is in control of the state of the screen. + IDreamManager dreamManager = getDreamManager(); - try { - if (dreamManager != null && dreamManager.isDreaming()) { - return true; + try { + if (dreamManager != null && dreamManager.isDreaming()) { + return true; + } + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException when checking if dreaming", e); } - } catch (RemoteException e) { - Slog.e(TAG, "RemoteException when checking if dreaming", e); } - // Otherwise, consume events since the user can't see what is being // interacted with. return false; diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index d1bd102f11dc..870d61b2ab90 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -1003,11 +1003,13 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { * affect the power state of the device, for example, waking on motions. * Generally, it's best to keep as little as possible in the queue thread * because it's the most fragile. + * @param displayId The display ID of the motion event. * @param policyFlags The policy flags associated with the motion. * * @return Actions flags: may be {@link #ACTION_PASS_TO_USER}. */ - public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags); + int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos, + int policyFlags); /** * Called from the input dispatcher thread before a key is dispatched to a window. diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index dd2cda20e0f1..f3393e2f29da 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -167,6 +167,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { public static final int CODE_DATA_BROADCAST = 1; public static final int CODE_SUBSCRIBER_BROADCAST = 1; + public static final int CODE_ACTIVE_CONFIGS_BROADCAST = 1; /** * The last report time is provided with each intent registered to * StatsManager#setFetchReportsOperation. This allows easy de-duping in the receiver if @@ -356,6 +357,22 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } @Override + public void sendActiveConfigsChangedBroadcast(IBinder intentSenderBinder, long[] configIds) { + enforceCallingPermission(); + IntentSender intentSender = new IntentSender(intentSenderBinder); + Intent intent = new Intent(); + intent.putExtra(StatsManager.EXTRA_STATS_ACTIVE_CONFIG_KEYS, configIds); + try { + intentSender.sendIntent(mContext, CODE_ACTIVE_CONFIGS_BROADCAST, intent, null, null); + if (DEBUG) { + Slog.d(TAG, "Sent broadcast with config ids " + Arrays.toString(configIds)); + } + } catch (IntentSender.SendIntentException e) { + Slog.w(TAG, "Unable to send active configs changed broadcast using IntentSender"); + } + } + + @Override public void sendSubscriberBroadcast(IBinder intentSenderBinder, long configUid, long configKey, long subscriptionId, long subscriptionRuleId, String[] cookies, StatsDimensionsValue dimensionsValue) { @@ -1169,7 +1186,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { BinderCallsStatsService.Internal binderStats = LocalServices.getService(BinderCallsStatsService.Internal.class); if (binderStats == null) { - return; + throw new IllegalStateException("binderStats is null"); } List<ExportedCallStat> callStats = binderStats.getExportedCallStats(); @@ -1200,7 +1217,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { BinderCallsStatsService.Internal binderStats = LocalServices.getService(BinderCallsStatsService.Internal.class); if (binderStats == null) { - return; + throw new IllegalStateException("binderStats is null"); } ArrayMap<String, Integer> exceptionStats = binderStats.getExportedExceptionStats(); @@ -1218,7 +1235,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { List<StatsLogEventWrapper> pulledData) { LooperStats looperStats = LocalServices.getService(LooperStats.class); if (looperStats == null) { - return; + throw new IllegalStateException("looperStats null"); } List<LooperStats.ExportedEntry> entries = looperStats.getEntries(); @@ -1689,18 +1706,19 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { private void pullCpuTimePerThreadFreq(int tagId, long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) { if (this.mKernelCpuThreadReader == null) { - return; + throw new IllegalStateException("mKernelCpuThreadReader is null"); } ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsages = this.mKernelCpuThreadReader.getProcessCpuUsageByUids(); if (processCpuUsages == null) { - return; + throw new IllegalStateException("processCpuUsages is null"); } int[] cpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz(); if (cpuFrequencies.length > CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES) { - Slog.w(TAG, "Expected maximum " + CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES - + " frequencies, but got " + cpuFrequencies.length); - return; + String message = "Expected maximum " + CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES + + " frequencies, but got " + cpuFrequencies.length; + Slog.w(TAG, message); + throw new IllegalStateException(message); } for (int i = 0; i < processCpuUsages.size(); i++) { KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = processCpuUsages.get(i); @@ -1709,10 +1727,11 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { for (int j = 0; j < threadCpuUsages.size(); j++) { KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage = threadCpuUsages.get(j); if (threadCpuUsage.usageTimesMillis.length != cpuFrequencies.length) { - Slog.w(TAG, "Unexpected number of usage times," + String message = "Unexpected number of usage times," + " expected " + cpuFrequencies.length - + " but got " + threadCpuUsage.usageTimesMillis.length); - continue; + + " but got " + threadCpuUsage.usageTimesMillis.length; + Slog.w(TAG, message); + throw new IllegalStateException(message); } StatsLogEventWrapper e = diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index f9c9d33c561a..f46835eb51fc 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -163,15 +163,12 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags); } - /** - * Provides an opportunity for the window manager policy to intercept early motion event - * processing when the device is in a non-interactive state since these events are normally - * dropped. - */ + /** {@inheritDoc} */ @Override - public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) { + public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos, + int policyFlags) { return mService.mPolicy.interceptMotionBeforeQueueingNonInteractive( - whenNanos, policyFlags); + displayId, whenNanos, policyFlags); } /** diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java index fb5b1d85a4a2..5c91d9e6fff2 100644 --- a/services/core/java/com/android/server/wm/RootActivityContainer.java +++ b/services/core/java/com/android/server/wm/RootActivityContainer.java @@ -300,7 +300,7 @@ class RootActivityContainer extends ConfigurationContainer * corresponding record in display manager. */ // TODO: Look into consolidating with getActivityDisplay() - ActivityDisplay getActivityDisplayOrCreate(int displayId) { + @Nullable ActivityDisplay getActivityDisplayOrCreate(int displayId) { ActivityDisplay activityDisplay = getActivityDisplay(displayId); if (activityDisplay != null) { return activityDisplay; @@ -1317,6 +1317,9 @@ class RootActivityContainer extends ConfigurationContainer if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId); synchronized (mService.mGlobalLock) { final ActivityDisplay display = getActivityDisplayOrCreate(displayId); + if (display == null) { + return; + } // Do not start home before booting, or it may accidentally finish booting before it // starts. Instead, we expect home activities to be launched when the system is ready // (ActivityManagerService#systemReady). diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index c18e98bab9c4..57377c633c9a 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -249,7 +249,8 @@ public: virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags); virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig); virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags); - virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags); + virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when, + uint32_t& policyFlags); virtual nsecs_t interceptKeyBeforeDispatching( const sp<IBinder>& token, const KeyEvent* keyEvent, uint32_t policyFlags); @@ -1066,7 +1067,8 @@ void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent, } } -void NativeInputManager::interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) { +void NativeInputManager::interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when, + uint32_t& policyFlags) { ATRACE_CALL(); // Policy: // - Ignore untrusted events and pass them along. @@ -1084,7 +1086,7 @@ void NativeInputManager::interceptMotionBeforeQueueing(nsecs_t when, uint32_t& p JNIEnv* env = jniEnv(); jint wmActions = env->CallIntMethod(mServiceObj, gServiceClassInfo.interceptMotionBeforeQueueingNonInteractive, - when, policyFlags); + displayId, when, policyFlags); if (checkAndClearExceptionFromCallback(env, "interceptMotionBeforeQueueingNonInteractive")) { wmActions = 0; @@ -1794,7 +1796,7 @@ int register_android_server_InputManager(JNIEnv* env) { "interceptKeyBeforeQueueing", "(Landroid/view/KeyEvent;I)I"); GET_METHOD_ID(gServiceClassInfo.interceptMotionBeforeQueueingNonInteractive, clazz, - "interceptMotionBeforeQueueingNonInteractive", "(JI)I"); + "interceptMotionBeforeQueueingNonInteractive", "(IJI)I"); GET_METHOD_ID(gServiceClassInfo.interceptKeyBeforeDispatching, clazz, "interceptKeyBeforeDispatching", diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index cbc3791264bf..d178c3abc906 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -973,7 +973,10 @@ void GnssMeasurementCallback::translateSingleGnssMeasurement JavaObject& object) { translateSingleGnssMeasurement(&(measurement_V2_0->v1_1), object); - SET(CodeType, (static_cast<int32_t>(measurement_V2_0->codeType))); + SET(CodeType, static_cast<int32_t>(measurement_V2_0->codeType)); + + // Overwrite with v2_0.state since v2_0->v1_1->v1_0.state is deprecated. + SET(State, static_cast<int32_t>(measurement_V2_0->state)); } jobject GnssMeasurementCallback::translateGnssClock( diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 093ac89b97c0..9523202cbb0e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -17,8 +17,8 @@ package com.android.server.devicepolicy; import static android.Manifest.permission.BIND_DEVICE_ADMIN; -import static android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY; import static android.Manifest.permission.MANAGE_CA_CERTIFICATES; +import static android.Manifest.permission.REQUEST_SCREEN_LOCK_COMPLEXITY; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE; import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER; @@ -4778,8 +4778,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int callingUserId = mInjector.userHandleGetCallingUserId(); enforceUserUnlocked(callingUserId); mContext.enforceCallingOrSelfPermission( - GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY, - "Must have " + GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY + " permission."); + REQUEST_SCREEN_LOCK_COMPLEXITY, + "Must have " + REQUEST_SCREEN_LOCK_COMPLEXITY + " permission."); synchronized (getLockObject()) { int targetUserId = getCredentialOwner(callingUserId, /* parent= */ false); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 9ac91ddba5ec..de782a52eb53 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -5199,7 +5199,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { public void testGetPasswordComplexity_currentUserNoPassword() { when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE)) .thenReturn(true); - mServiceContext.permissions.add(permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY); + mServiceContext.permissions.add(permission.REQUEST_SCREEN_LOCK_COMPLEXITY); when(getServices().userManager.getCredentialOwnerProfile(DpmMockContext.CALLER_USER_HANDLE)) .thenReturn(DpmMockContext.CALLER_USER_HANDLE); @@ -5209,7 +5209,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { public void testGetPasswordComplexity_currentUserHasPassword() { when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE)) .thenReturn(true); - mServiceContext.permissions.add(permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY); + mServiceContext.permissions.add(permission.REQUEST_SCREEN_LOCK_COMPLEXITY); when(getServices().userManager.getCredentialOwnerProfile(DpmMockContext.CALLER_USER_HANDLE)) .thenReturn(DpmMockContext.CALLER_USER_HANDLE); dpms.mUserPasswordMetrics.put( @@ -5222,7 +5222,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { public void testGetPasswordComplexity_unifiedChallengeReturnsParentUserPassword() { when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE)) .thenReturn(true); - mServiceContext.permissions.add(permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY); + mServiceContext.permissions.add(permission.REQUEST_SCREEN_LOCK_COMPLEXITY); UserInfo parentUser = new UserInfo(); parentUser.id = DpmMockContext.CALLER_USER_HANDLE + 10; diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java index 8d0365b534b5..95043810128a 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java @@ -32,6 +32,7 @@ import static org.mockito.Mockito.verify; import android.Manifest.permission; import android.app.ActivityManager; +import android.app.Person; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ShortcutInfo; @@ -890,6 +891,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setText("text") .setDisabledMessage("dismes") .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) + .setPerson(makePerson("person", "personKey", "personUri")) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) .setRank(123) .setExtras(pb) @@ -901,6 +903,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setTitle("x") .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class)) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) + .setPersons(list(makePerson("person1", "personKey1", "personUri1"), + makePerson("person2", "personKey2", "personUri2")).toArray(new Person[2])) .setRank(456) .build(); sorig2.setTimestamp(mInjectedCurrentTimeMillis); @@ -936,6 +940,10 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals("action", si.getIntent().getAction()); assertEquals("val", si.getIntent().getStringExtra("key")); + assertEquals(1, si.getPersons().length); + assertEquals("person", si.getPersons()[0].getName()); + assertEquals("personKey", si.getPersons()[0].getKey()); + assertEquals("personUri", si.getPersons()[0].getUri()); assertEquals(0, si.getRank()); assertEquals(1, si.getExtras().getInt("k")); @@ -949,6 +957,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { // to test it. si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_10); assertEquals(1, si.getRank()); + assertEquals(2, si.getPersons().length); + assertEquals("personUri2", si.getPersons()[1].getUri()); dumpUserFile(USER_10); } @@ -1114,6 +1124,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setDisabledMessage("dismes") .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz")) .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val")) + .setPerson(makePerson("person", "personKey", "personUri")) .setRank(123) .setExtras(pb) .build(); @@ -1150,6 +1161,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories()); assertEquals("action", si.getIntent().getAction()); assertEquals("val", si.getIntent().getStringExtra("key")); + assertEquals(0, si.getPersons().length); // Don't backup the persons field assertEquals(0, si.getRank()); assertEquals(1, si.getExtras().getInt("k")); diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java index bfb9193551f2..849772a4a26d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -156,7 +156,8 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) { + public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos, + int policyFlags) { return 0; } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 0c63b60b030d..1b7228ae9a33 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -2424,6 +2424,14 @@ public class CarrierConfigManager { "5g_icon_configuration_string"; /** + * Support ASCII 7-BIT encoding for long SMS. This carrier config is used to enable + * this feature. + * @hide + */ + public static final String KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL = + "ascii_7_bit_support_for_long_message_bool"; + + /** * Controls RSRP threshold at which OpportunisticNetworkService will decide whether * the opportunistic network is good enough for internet data. */ @@ -2961,6 +2969,7 @@ public class CarrierConfigManager { sDefaults.putInt(KEY_CALL_WAITING_SERVICE_CLASS_INT, 1 /* SERVICE_CLASS_VOICE */); sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING, "connected_mmwave:None,connected:5G,not_restricted:None,restricted:None"); + sDefaults.putBoolean(KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL, false); /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108); /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */ diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java index 6b1b84cd3458..856f08107fd7 100644 --- a/telephony/java/android/telephony/CellIdentityNr.java +++ b/telephony/java/android/telephony/CellIdentityNr.java @@ -30,6 +30,7 @@ public final class CellIdentityNr extends CellIdentity { private final int mNrArfcn; private final int mPci; private final int mTac; + private final long mNci; /** * @@ -44,11 +45,12 @@ public final class CellIdentityNr extends CellIdentity { * @hide */ public CellIdentityNr(int pci, int tac, int nrArfcn, String mccStr, String mncStr, - String alphal, String alphas) { + long nci, String alphal, String alphas) { super(TAG, CellInfo.TYPE_NR, mccStr, mncStr, alphal, alphas); mPci = pci; mTac = tac; mNrArfcn = nrArfcn; + mNci = nci; } /** @@ -62,7 +64,7 @@ public final class CellIdentityNr extends CellIdentity { @Override public int hashCode() { - return Objects.hash(super.hashCode(), mPci, mTac, mNrArfcn); + return Objects.hash(super.hashCode(), mPci, mTac, mNrArfcn, mNci); } @Override @@ -72,7 +74,17 @@ public final class CellIdentityNr extends CellIdentity { } CellIdentityNr o = (CellIdentityNr) other; - return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn; + return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn + && mNci == o.mNci; + } + + /** + * Get the NR Cell Identity. + * + * @return The NR Cell Identity in range [0, 68719476735] or Long.MAX_VALUE if unknown. + */ + public long getNci() { + return mNci; } /** @@ -122,6 +134,7 @@ public final class CellIdentityNr extends CellIdentity { .append(" mNrArfcn = ").append(mNrArfcn) .append(" mMcc = ").append(mMccStr) .append(" mMnc = ").append(mMncStr) + .append(" mNci = ").append(mNci) .append(" mAlphaLong = ").append(mAlphaLong) .append(" mAlphaShort = ").append(mAlphaShort) .append(" }") @@ -134,6 +147,7 @@ public final class CellIdentityNr extends CellIdentity { dest.writeInt(mPci); dest.writeInt(mTac); dest.writeInt(mNrArfcn); + dest.writeLong(mNci); } /** Construct from Parcel, type has already been processed */ @@ -142,6 +156,7 @@ public final class CellIdentityNr extends CellIdentity { mPci = in.readInt(); mTac = in.readInt(); mNrArfcn = in.readInt(); + mNci = in.readLong(); } /** Implement the Parcelable interface */ diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java index ceb76b57ae0c..c37b492a9cf1 100644 --- a/telephony/java/android/telephony/NetworkRegistrationState.java +++ b/telephony/java/android/telephony/NetworkRegistrationState.java @@ -27,6 +27,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.Objects; +import java.util.stream.Collectors; /** * Description of a mobile network registration state @@ -360,7 +361,34 @@ public class NetworkRegistrationState implements Parcelable { return 0; } - private static String regStateToString(int regState) { + /** + * Convert service type to string + * + * @hide + * + * @param serviceType The service type + * @return The service type in string format + */ + public static String serviceTypeToString(@ServiceType int serviceType) { + switch (serviceType) { + case SERVICE_TYPE_VOICE: return "VOICE"; + case SERVICE_TYPE_DATA: return "DATA"; + case SERVICE_TYPE_SMS: return "SMS"; + case SERVICE_TYPE_VIDEO: return "VIDEO"; + case SERVICE_TYPE_EMERGENCY: return "EMERGENCY"; + } + return "Unknown service type " + serviceType; + } + + /** + * Convert registration state to string + * + * @hide + * + * @param regState The registration state + * @return The reg state in string + */ + public static String regStateToString(@RegState int regState) { switch (regState) { case REG_STATE_NOT_REG_NOT_SEARCHING: return "NOT_REG_NOT_SEARCHING"; case REG_STATE_HOME: return "HOME"; @@ -389,14 +417,17 @@ public class NetworkRegistrationState implements Parcelable { public String toString() { return new StringBuilder("NetworkRegistrationState{") .append(" domain=").append((mDomain == DOMAIN_CS) ? "CS" : "PS") - .append("transportType=").append(mTransportType) + .append(" transportType=").append(TransportType.toString(mTransportType)) .append(" regState=").append(regStateToString(mRegState)) - .append(" roamingType=").append(mRoamingType) + .append(" roamingType=").append(ServiceState.roamingTypeToString(mRoamingType)) .append(" accessNetworkTechnology=") .append(TelephonyManager.getNetworkTypeName(mAccessNetworkTechnology)) .append(" rejectCause=").append(mRejectCause) .append(" emergencyEnabled=").append(mEmergencyOnly) - .append(" supportedServices=").append(mAvailableServices) + .append(" availableServices=").append("[" + (mAvailableServices != null + ? Arrays.stream(mAvailableServices) + .mapToObj(type -> serviceTypeToString(type)) + .collect(Collectors.joining(",")) : null) + "]") .append(" cellIdentity=").append(mCellIdentity) .append(" voiceSpecificStates=").append(mVoiceSpecificStates) .append(" dataSpecificStates=").append(mDataSpecificStates) diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 33178766f3a3..402763e52cfa 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -887,6 +887,24 @@ public class ServiceState implements Parcelable { } /** + * Convert roaming type to string + * + * @param roamingType roaming type + * @return The roaming type in string format + * + * @hide + */ + public static String roamingTypeToString(@RoamingType int roamingType) { + switch (roamingType) { + case ROAMING_TYPE_NOT_ROAMING: return "NOT_ROAMING"; + case ROAMING_TYPE_UNKNOWN: return "UNKNOWN"; + case ROAMING_TYPE_DOMESTIC: return "DOMESTIC"; + case ROAMING_TYPE_INTERNATIONAL: return "INTERNATIONAL"; + } + return "Unknown roaming type " + roamingType; + } + + /** * Convert radio technology to String * * @param rt radioTechnology diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 313146d5538b..3a4d33c18485 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -59,6 +59,7 @@ import android.util.DisplayMetrics; import android.util.Log; import com.android.internal.telephony.IOnSubscriptionsChangedListener; +import com.android.internal.telephony.ISetOpportunisticDataCallback; import com.android.internal.telephony.ISub; import com.android.internal.telephony.ITelephonyRegistry; import com.android.internal.telephony.PhoneConstants; @@ -72,6 +73,7 @@ import java.util.List; import java.util.Locale; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; import java.util.stream.Collectors; /** @@ -2573,17 +2575,35 @@ public class SubscriptionManager { * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}, it means * it's unset and {@link SubscriptionManager#getDefaultDataSubscriptionId()} * is used to determine which modem is preferred. + * @param needValidation whether validation is needed before switch happens. + * @param executor The executor of where the callback will execute. + * @param callback Callback will be triggered once it succeeds or failed. + * See {@link TelephonyManager.SetOpportunisticSubscriptionResult} + * for more details. Pass null if don't care about the result. + * * @hide * */ + @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public void setPreferredDataSubscriptionId(int subId) { + public void setPreferredDataSubscriptionId(int subId, boolean needValidation, + @NonNull @CallbackExecutor Executor executor, Consumer<Integer> callback) { if (VDBG) logd("[setPreferredDataSubscriptionId]+ subId:" + subId); try { ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); - if (iSub != null) { - iSub.setPreferredDataSubscriptionId(subId); - } + if (iSub == null) return; + + ISetOpportunisticDataCallback callbackStub = new ISetOpportunisticDataCallback.Stub() { + @Override + public void onComplete(int result) { + Binder.withCleanCallingIdentity(() -> executor.execute(() -> { + if (callback != null) { + callback.accept(result); + } + })); + } + }; + iSub.setPreferredDataSubscriptionId(subId, needValidation, callbackStub); } catch (RemoteException ex) { // ignore it } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 2a03924fd58c..6690bd0f10b5 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -10094,6 +10094,29 @@ public class TelephonyManager { return false; } + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"SET_OPPORTUNISTIC_SUB"}, value = { + SET_OPPORTUNISTIC_SUB_SUCCESS, + SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED, + SET_OPPORTUNISTIC_SUB_INVALID_PARAMETER}) + public @interface SetOpportunisticSubscriptionResult {} + + /** + * No error. Operation succeeded. + */ + public static final int SET_OPPORTUNISTIC_SUB_SUCCESS = 0; + + /** + * Validation failed when trying to switch to preferred subscription. + */ + public static final int SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED = 1; + + /** + * The parameter passed in is invalid. + */ + public static final int SET_OPPORTUNISTIC_SUB_INVALID_PARAMETER = 2; + /** * Set preferred opportunistic data subscription id. * diff --git a/telephony/java/com/android/internal/telephony/ISetOpportunisticDataCallback.aidl b/telephony/java/com/android/internal/telephony/ISetOpportunisticDataCallback.aidl new file mode 100644 index 000000000000..7a78f3454aac --- /dev/null +++ b/telephony/java/com/android/internal/telephony/ISetOpportunisticDataCallback.aidl @@ -0,0 +1,25 @@ +/* + * Copyright 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.internal.telephony; + +/** + * Callback to provide asynchronous result of setPreferredOpportunisticData. + * @hide + */ +oneway interface ISetOpportunisticDataCallback { + void onComplete(int result); +} diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl index 6ce9de4ca677..75a4d8227e23 100755 --- a/telephony/java/com/android/internal/telephony/ISub.aidl +++ b/telephony/java/com/android/internal/telephony/ISub.aidl @@ -17,6 +17,7 @@ package com.android.internal.telephony; import android.telephony.SubscriptionInfo; +import com.android.internal.telephony.ISetOpportunisticDataCallback; interface ISub { /** @@ -217,10 +218,14 @@ interface ISub { * designed to overwrite default data subscription temporarily. * * @param subId which subscription is preferred to for cellular data. + * @param needValidation whether validation is needed before switching. + * @param callback callback upon request completion. + * * @hide * */ - void setPreferredDataSubscriptionId(int subId); + void setPreferredDataSubscriptionId(int subId, boolean needValidation, + ISetOpportunisticDataCallback callback); /** * Get which subscription is preferred for cellular data. diff --git a/telephony/java/com/android/internal/telephony/cdma/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/BearerData.java index a4cd56b9e3e2..694cc69c2b3f 100644 --- a/telephony/java/com/android/internal/telephony/cdma/BearerData.java +++ b/telephony/java/com/android/internal/telephony/cdma/BearerData.java @@ -596,6 +596,45 @@ public final class BearerData { System.arraycopy(payload, 0, uData.payload, udhBytes, payload.length); } + private static void encode7bitAsciiEms(UserData uData, byte[] udhData, boolean force) + throws CodingException + { + try { + Rlog.d(LOG_TAG, "encode7bitAsciiEms"); + int udhBytes = udhData.length + 1; // Add length octet. + int udhSeptets = ((udhBytes * 8) + 6) / 7; + int paddingBits = (udhSeptets * 7) - (udhBytes * 8); + String msg = uData.payloadStr; + byte[] payload ; + int msgLen = msg.length(); + BitwiseOutputStream outStream = new BitwiseOutputStream(msgLen + + (paddingBits > 0 ? 1 : 0)); + outStream.write(paddingBits, 0); + for (int i = 0; i < msgLen; i++) { + int charCode = UserData.charToAscii.get(msg.charAt(i), -1); + if (charCode == -1) { + if (force) { + outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR); + } else { + throw new CodingException("cannot ASCII encode (" + msg.charAt(i) + ")"); + } + } else { + outStream.write(7, charCode); + } + } + payload = outStream.toByteArray(); + uData.msgEncoding = UserData.ENCODING_7BIT_ASCII; + uData.msgEncodingSet = true; + uData.numFields = udhSeptets + uData.payloadStr.length(); + uData.payload = new byte[udhBytes + payload.length]; + uData.payload[0] = (byte)udhData.length; + System.arraycopy(udhData, 0, uData.payload, 1, udhData.length); + System.arraycopy(payload, 0, uData.payload, udhBytes, payload.length); + } catch (BitwiseOutputStream.AccessException ex) { + throw new CodingException("7bit ASCII encode failed: " + ex); + } + } + private static void encodeEmsUserDataPayload(UserData uData) throws CodingException { @@ -605,6 +644,8 @@ public final class BearerData { encode7bitEms(uData, headerData, true); } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) { encode16bitEms(uData, headerData); + } else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) { + encode7bitAsciiEms(uData, headerData, true); } else { throw new CodingException("unsupported EMS user data encoding (" + uData.msgEncoding + ")"); @@ -1056,15 +1097,18 @@ public final class BearerData { throws CodingException { try { - offset *= 8; + int offsetBits = offset * 8; + int offsetSeptets = (offsetBits + 6) / 7; + numFields -= offsetSeptets; + StringBuffer strBuf = new StringBuffer(numFields); BitwiseInputStream inStream = new BitwiseInputStream(data); - int wantedBits = (offset * 8) + (numFields * 7); + int wantedBits = (offsetSeptets * 7) + (numFields * 7); if (inStream.available() < wantedBits) { throw new CodingException("insufficient data (wanted " + wantedBits + " bits, but only have " + inStream.available() + ")"); } - inStream.skip(offset); + inStream.skip(offsetSeptets * 7); for (int i = 0; i < numFields; i++) { int charCode = inStream.read(7); if ((charCode >= UserData.ASCII_MAP_BASE_INDEX) && diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java index 9080e23eb88f..1da5eac27002 100644 --- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java @@ -864,8 +864,9 @@ public class SmsMessage extends SmsMessageBase { Rlog.d(LOG_TAG, "MO raw BearerData = '" + HexDump.toHexString(encodedBearerData) + "'"); } - int teleservice = bearerData.hasUserDataHeader ? - SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT; + int teleservice = (bearerData.hasUserDataHeader + && userData.msgEncoding != UserData.ENCODING_7BIT_ASCII) + ? SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT; SmsEnvelope envelope = new SmsEnvelope(); envelope.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT; diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index 85bf6f218ba0..582a5b869c65 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -294,22 +294,6 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, } } - if (el->FindAttribute("", "platformBuildVersionCode") == nullptr) { - auto versionCode = el->FindAttribute(xml::kSchemaAndroid, "versionCode"); - if (versionCode != nullptr) { - el->attributes.push_back(xml::Attribute{"", "platformBuildVersionCode", - versionCode->value}); - } - } - - if (el->FindAttribute("", "platformBuildVersionName") == nullptr) { - auto versionName = el->FindAttribute(xml::kSchemaAndroid, "versionName"); - if (versionName != nullptr) { - el->attributes.push_back(xml::Attribute{"", "platformBuildVersionName", - versionName->value}); - } - } - return true; }); @@ -489,8 +473,14 @@ bool ManifestFixer::Consume(IAaptContext* context, xml::XmlResource* doc) { // Make sure we un-compile the value if it was set to something else. attr->compiled_value = {}; + attr->value = options_.compile_sdk_version.value(); + + attr = root->FindOrCreateAttribute("", "platformBuildVersionCode"); + // Make sure we un-compile the value if it was set to something else. + attr->compiled_value = {}; attr->value = options_.compile_sdk_version.value(); + } if (options_.compile_sdk_version_codename) { @@ -499,7 +489,12 @@ bool ManifestFixer::Consume(IAaptContext* context, xml::XmlResource* doc) { // Make sure we un-compile the value if it was set to something else. attr->compiled_value = {}; + attr->value = options_.compile_sdk_version_codename.value(); + attr = root->FindOrCreateAttribute("", "platformBuildVersionName"); + + // Make sure we un-compile the value if it was set to something else. + attr->compiled_value = {}; attr->value = options_.compile_sdk_version_codename.value(); } diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp index adea6273bc8b..4842f62e53b5 100644 --- a/tools/aapt2/link/ManifestFixer_test.cpp +++ b/tools/aapt2/link/ManifestFixer_test.cpp @@ -725,6 +725,43 @@ TEST_F(ManifestFixerTest, InsertCompileSdkVersions) { attr = manifest->root->FindAttribute(xml::kSchemaAndroid, "compileSdkVersionCodename"); ASSERT_THAT(attr, NotNull()); EXPECT_THAT(attr->value, StrEq("P")); + + attr = manifest->root->FindAttribute("", "platformBuildVersionCode"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("28")); + + attr = manifest->root->FindAttribute("", "platformBuildVersionName"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("P")); +} + +TEST_F(ManifestFixerTest, OverrideCompileSdkVersions) { + std::string input = R"( + <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android" + compileSdkVersion="27" compileSdkVersionCodename="O" + platformBuildVersionCode="27" platformBuildVersionName="O"/>)"; + ManifestFixerOptions options; + options.compile_sdk_version = {"28"}; + options.compile_sdk_version_codename = {"P"}; + + std::unique_ptr<xml::XmlResource> manifest = VerifyWithOptions(input, options); + ASSERT_THAT(manifest, NotNull()); + + xml::Attribute* attr = manifest->root->FindAttribute(xml::kSchemaAndroid, "compileSdkVersion"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("28")); + + attr = manifest->root->FindAttribute(xml::kSchemaAndroid, "compileSdkVersionCodename"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("P")); + + attr = manifest->root->FindAttribute("", "platformBuildVersionCode"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("28")); + + attr = manifest->root->FindAttribute("", "platformBuildVersionName"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("P")); } TEST_F(ManifestFixerTest, UnexpectedElementsInManifest) { @@ -750,59 +787,6 @@ TEST_F(ManifestFixerTest, UnexpectedElementsInManifest) { ASSERT_THAT(manifest, IsNull()); } -TEST_F(ManifestFixerTest, InsertPlatformBuildVersions) { - // Test for insertion when versionCode and versionName are included in the manifest - { - std::string input = R"( - <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android" - android:versionCode="27" android:versionName="O"/>)"; - std::unique_ptr<xml::XmlResource> manifest = Verify(input); - ASSERT_THAT(manifest, NotNull()); - - xml::Attribute* attr = manifest->root->FindAttribute("", "platformBuildVersionCode"); - ASSERT_THAT(attr, NotNull()); - EXPECT_THAT(attr->value, StrEq("27")); - attr = manifest->root->FindAttribute("", "platformBuildVersionName"); - ASSERT_THAT(attr, NotNull()); - EXPECT_THAT(attr->value, StrEq("O")); - } - - // Test for insertion when versionCode and versionName defaults are specified - { - std::string input = R"( - <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android"/>)"; - ManifestFixerOptions options; - options.version_code_default = {"27"}; - options.version_name_default = {"O"}; - std::unique_ptr<xml::XmlResource> manifest = VerifyWithOptions(input, options); - ASSERT_THAT(manifest, NotNull()); - - xml::Attribute* attr = manifest->root->FindAttribute("", "platformBuildVersionCode"); - ASSERT_THAT(attr, NotNull()); - EXPECT_THAT(attr->value, StrEq("27")); - attr = manifest->root->FindAttribute("", "platformBuildVersionName"); - ASSERT_THAT(attr, NotNull()); - EXPECT_THAT(attr->value, StrEq("O")); - } - - // Test that the platform build version attributes are not changed if they are currently present - { - std::string input = R"( - <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android" - android:versionCode="28" android:versionName="P" - platformBuildVersionCode="27" platformBuildVersionName="O"/>)"; - std::unique_ptr<xml::XmlResource> manifest = Verify(input); - ASSERT_THAT(manifest, NotNull()); - - xml::Attribute* attr = manifest->root->FindAttribute("", "platformBuildVersionCode"); - ASSERT_THAT(attr, NotNull()); - EXPECT_THAT(attr->value, StrEq("27")); - attr = manifest->root->FindAttribute("", "platformBuildVersionName"); - ASSERT_THAT(attr, NotNull()); - EXPECT_THAT(attr->value, StrEq("O")); - } -} - TEST_F(ManifestFixerTest, UsesLibraryMustHaveNonEmptyName) { std::string input = R"( <manifest xmlns:android="http://schemas.android.com/apk/res/android" diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 2cc1d8313225..5e5a59566ce5 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1149,9 +1149,6 @@ public class WifiManager { * Return a list of all the networks configured for the current foreground * user. * - * Requires the same permissions as {@link #getScanResults}. - * If such access is not allowed, this API will always return an empty list. - * * Not all fields of WifiConfiguration are returned. Only the following * fields are filled in: * <ul> @@ -1176,8 +1173,12 @@ public class WifiManager { * when auto-connecting to wifi. * <b>Compatibility Note:</b> For applications targeting * {@link android.os.Build.VERSION_CODES#Q} or above, this API will return an empty list, - * except to callers with Carrier privilege which will receive a restricted list only - * containing configurations which they created. + * except for: + * <ul> + * <li>Device Owner (DO) & Profile Owner (PO) apps will have access to the full list. + * <li>Callers with Carrier privilege will receive a restricted list only containing + * configurations which they created. + * </ul> */ @Deprecated @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE}) @@ -4721,7 +4722,6 @@ public class WifiManager { * * @hide */ - @SystemApi private static class EasyConnectCallbackProxy extends IDppCallback.Stub { private final Executor mExecutor; private final EasyConnectStatusCallback mEasyConnectStatusCallback; |