Merge "Refine ColorSpace restrictions for Bitmaps"
diff --git a/Android.bp b/Android.bp
index 183121b..995fe3e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -773,7 +773,7 @@
"android.hardware.vibrator-V1.3-java",
"android.hardware.wifi-V1.0-java-constants",
"networkstack-aidl-interfaces-java",
- "netd_aidl_interface-java",
+ "netd_aidl_parcelables-java",
"devicepolicyprotosnano",
],
@@ -1187,6 +1187,15 @@
"org/apache/http/params",
]
+// Make the api/current.txt file available for use by modules in other
+// directories.
+filegroup {
+ name: "frameworks-base-api-current.txt",
+ srcs: [
+ "api/current.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/Android.mk b/Android.mk
index 9a91dd1..c58f7af 100644
--- a/Android.mk
+++ b/Android.mk
@@ -77,8 +77,6 @@
# Run this for checkbuild
checkbuild: doc-comment-check-docs
-# Check comment when you are updating the API
-update-api: doc-comment-check-docs
# ==== hiddenapi lists =======================================
ifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true)
diff --git a/api/current.txt b/api/current.txt
index 4238ca6..d11b6a4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -27445,6 +27445,7 @@
method public void seekTo(long);
method public void sendCustomAction(@NonNull android.media.session.PlaybackState.CustomAction, @Nullable android.os.Bundle);
method public void sendCustomAction(@NonNull String, @Nullable android.os.Bundle);
+ method public void setPlaybackSpeed(float);
method public void setRating(android.media.Rating);
method public void skipToNext();
method public void skipToPrevious();
@@ -27495,6 +27496,7 @@
method public void onPrepareFromUri(android.net.Uri, android.os.Bundle);
method public void onRewind();
method public void onSeekTo(long);
+ method public void onSetPlaybackSpeed(float);
method public void onSetRating(@NonNull android.media.Rating);
method public void onSkipToNext();
method public void onSkipToPrevious();
@@ -44395,6 +44397,7 @@
method public String getMccString();
method public String getMncString();
method @Nullable public String getMobileNetworkOperator();
+ method public int getUarfcn();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityTdscdma> CREATOR;
}
@@ -45069,12 +45072,12 @@
method public boolean canChangeDtmfToneLength();
method @Nullable public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
method public android.telephony.TelephonyManager createForSubscriptionId(int);
- method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo();
method public int getCallState();
method public int getCardIdForDefaultEuicc();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @WorkerThread public android.os.PersistableBundle getCarrierConfig();
method public int getCarrierIdFromSimMccMnc();
- method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.CellLocation getCellLocation();
+ method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public android.telephony.CellLocation getCellLocation();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @Nullable public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getCurrentEmergencyNumberList();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @Nullable public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getCurrentEmergencyNumberList(int);
method public int getDataActivity();
@@ -45104,7 +45107,7 @@
method public int getPhoneCount();
method public int getPhoneType();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
- method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.ServiceState getServiceState();
+ method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
method @Nullable public android.telephony.SignalStrength getSignalStrength();
method public int getSimCarrierId();
method @Nullable public CharSequence getSimCarrierIdName();
@@ -45146,8 +45149,8 @@
method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
method public boolean isWorldPhone();
method public void listen(android.telephony.PhoneStateListener, int);
- method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
+ method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback);
method public void sendDialerSpecialCode(String);
method public String sendEnvelopeWithStatus(String);
method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
@@ -48998,6 +49001,7 @@
}
public final class StatsLog {
+ method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public static boolean logBinaryPushStateChanged(@NonNull String, long, int, int, @NonNull long[]);
method public static boolean logEvent(int);
method public static boolean logStart(int);
method public static boolean logStop(int);
@@ -53472,6 +53476,7 @@
method public void close();
method @NonNull public final android.view.contentcapture.ContentCaptureSession createContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext);
method public final void destroy();
+ method @Nullable public final android.view.contentcapture.ContentCaptureContext getContentCaptureContext();
method public final android.view.contentcapture.ContentCaptureSessionId getContentCaptureSessionId();
method @NonNull public android.view.autofill.AutofillId newAutofillId(@NonNull android.view.autofill.AutofillId, long);
method @NonNull public final android.view.ViewStructure newVirtualViewStructure(@NonNull android.view.autofill.AutofillId, long);
@@ -53479,6 +53484,7 @@
method public final void notifyViewDisappeared(@NonNull android.view.autofill.AutofillId);
method public final void notifyViewTextChanged(@NonNull android.view.autofill.AutofillId, @Nullable CharSequence);
method public final void notifyViewsDisappeared(@NonNull android.view.autofill.AutofillId, @NonNull long[]);
+ method public final void setContentCaptureContext(@Nullable android.view.contentcapture.ContentCaptureContext);
}
public final class ContentCaptureSessionId implements android.os.Parcelable {
diff --git a/api/removed.txt b/api/removed.txt
index f5bd434..c4ed871 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -558,7 +558,7 @@
public class TelephonyManager {
method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
- method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback);
+ method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback);
}
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 48a450a..4eb285b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1580,7 +1580,7 @@
method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method public void sendDeviceCustomizationReadyBroadcast();
method @RequiresPermission(allOf={android.Manifest.permission.SET_PREFERRED_APPLICATIONS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public abstract boolean setDefaultBrowserPackageNameAsUser(String, int);
- method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setDistractingPackageRestrictions(@NonNull String[], @android.content.pm.PackageManager.DistractionRestriction int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setDistractingPackageRestrictions(@NonNull String[], int);
method @RequiresPermission(android.Manifest.permission.SET_HARMFUL_APP_WARNINGS) public void setHarmfulAppWarning(@NonNull String, @Nullable CharSequence);
method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable String);
method @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo);
@@ -1658,9 +1658,6 @@
method public abstract void onDexModuleRegistered(String, boolean, String);
}
- @IntDef(flag=true, prefix={"RESTRICTION_"}, value={android.content.pm.PackageManager.RESTRICTION_NONE, android.content.pm.PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, android.content.pm.PackageManager.RESTRICTION_HIDE_NOTIFICATIONS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.DistractionRestriction {
- }
-
public static interface PackageManager.OnPermissionsChangedListener {
method public void onPermissionsChanged(int);
}
@@ -5783,8 +5780,10 @@
}
public static interface DeviceConfig.Rollback {
+ field public static final String BOOT_NAMESPACE = "rollback_boot";
field public static final String ENABLE_ROLLBACK_TIMEOUT = "enable_rollback_timeout";
field public static final String NAMESPACE = "rollback";
+ field public static final String ROLLBACK_LIFETIME_IN_MILLIS = "rollback_lifetime_in_millis";
}
public static interface DeviceConfig.Runtime {
@@ -5946,6 +5945,7 @@
public final class Settings {
field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
field public static final String ACTION_LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS = "android.settings.LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS";
+ field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE";
field public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS";
}
@@ -6301,20 +6301,11 @@
}
public abstract class PresentationParams {
- method public int getFlags();
- method @Nullable public android.service.autofill.augmented.PresentationParams.Area getFullArea();
method @Nullable public android.service.autofill.augmented.PresentationParams.Area getSuggestionArea();
- field public static final int FLAG_HINT_GRAVITY_BOTTOM = 2; // 0x2
- field public static final int FLAG_HINT_GRAVITY_LEFT = 4; // 0x4
- field public static final int FLAG_HINT_GRAVITY_RIGHT = 8; // 0x8
- field public static final int FLAG_HINT_GRAVITY_TOP = 1; // 0x1
- field public static final int FLAG_HOST_IME = 16; // 0x10
- field public static final int FLAG_HOST_SYSTEM = 32; // 0x20
}
public abstract static class PresentationParams.Area {
method @NonNull public android.graphics.Rect getBounds();
- method @Nullable public android.service.autofill.augmented.PresentationParams.Area getSubArea(@NonNull android.graphics.Rect);
}
}
@@ -6974,6 +6965,7 @@
field public static final String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
field public static final String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
field public static final String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
+ field public static final String EXTRA_IS_USER_INTENT_EMERGENCY_CALL = "android.telecom.extra.IS_USER_INTENT_EMERGENCY_CALL";
field public static final int TTY_MODE_FULL = 1; // 0x1
field public static final int TTY_MODE_HCO = 2; // 0x2
field public static final int TTY_MODE_OFF = 0; // 0x0
@@ -7860,7 +7852,7 @@
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
method public boolean needsOtaServiceProvisioning();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
- method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
+ method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestNumberVerification(@NonNull android.telephony.PhoneNumberRange, long, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.NumberVerificationCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig();
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
@@ -8248,6 +8240,7 @@
method public int getServiceType();
method public static int getVideoStateFromCallType(int);
method public static int getVideoStateFromImsCallProfile(android.telephony.ims.ImsCallProfile);
+ method public boolean hasKnownUserIntentEmergency();
method public boolean isEmergencyCallTesting();
method public boolean isVideoCall();
method public boolean isVideoPaused();
@@ -8260,6 +8253,7 @@
method public void setEmergencyCallTesting(boolean);
method public void setEmergencyServiceCategories(int);
method public void setEmergencyUrns(java.util.List<java.lang.String>);
+ method public void setHasKnownUserIntentEmergency(boolean);
method public void updateCallExtras(android.telephony.ims.ImsCallProfile);
method public void updateCallType(android.telephony.ims.ImsCallProfile);
method public void updateMediaProfile(android.telephony.ims.ImsCallProfile);
@@ -9324,6 +9318,7 @@
public final class ContentCaptureEvent implements android.os.Parcelable {
method public int describeContents();
+ method @Nullable public android.view.contentcapture.ContentCaptureContext getContentCaptureContext();
method public long getEventTime();
method @Nullable public android.view.autofill.AutofillId getId();
method @Nullable public java.util.List<android.view.autofill.AutofillId> getIds();
@@ -9332,6 +9327,7 @@
method @Nullable public android.view.contentcapture.ViewNode getViewNode();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR;
+ field public static final int TYPE_CONTEXT_UPDATED = 6; // 0x6
field public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; // 0x5
field public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; // 0x4
field public static final int TYPE_VIEW_APPEARED = 1; // 0x1
diff --git a/api/test-current.txt b/api/test-current.txt
index 1a7e4cb..8194785 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1815,6 +1815,7 @@
public final class Settings {
field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
+ field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE";
field public static final int RESET_MODE_PACKAGE_DEFAULTS = 1; // 0x1
}
@@ -2073,20 +2074,11 @@
}
public abstract class PresentationParams {
- method public int getFlags();
- method @Nullable public android.service.autofill.augmented.PresentationParams.Area getFullArea();
method @Nullable public android.service.autofill.augmented.PresentationParams.Area getSuggestionArea();
- field public static final int FLAG_HINT_GRAVITY_BOTTOM = 2; // 0x2
- field public static final int FLAG_HINT_GRAVITY_LEFT = 4; // 0x4
- field public static final int FLAG_HINT_GRAVITY_RIGHT = 8; // 0x8
- field public static final int FLAG_HINT_GRAVITY_TOP = 1; // 0x1
- field public static final int FLAG_HOST_IME = 16; // 0x10
- field public static final int FLAG_HOST_SYSTEM = 32; // 0x20
}
public abstract static class PresentationParams.Area {
method @NonNull public android.graphics.Rect getBounds();
- method @Nullable public android.service.autofill.augmented.PresentationParams.Area getSubArea(@NonNull android.graphics.Rect);
}
}
@@ -2725,6 +2717,7 @@
public final class ContentCaptureEvent implements android.os.Parcelable {
method public int describeContents();
+ method @Nullable public android.view.contentcapture.ContentCaptureContext getContentCaptureContext();
method public long getEventTime();
method @Nullable public android.view.autofill.AutofillId getId();
method @Nullable public java.util.List<android.view.autofill.AutofillId> getIds();
@@ -2733,6 +2726,7 @@
method @Nullable public android.view.contentcapture.ViewNode getViewNode();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR;
+ field public static final int TYPE_CONTEXT_UPDATED = 6; // 0x6
field public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; // 0x5
field public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; // 0x4
field public static final int TYPE_VIEW_APPEARED = 1; // 0x1
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index faf2053..f408655 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -76,6 +76,7 @@
"src/external/SubsystemSleepStatePuller.cpp",
"src/external/PowerStatsPuller.cpp",
"src/external/ResourceHealthManagerPuller.cpp",
+ "src/external/TrainInfoPuller.cpp",
"src/external/StatsPullerManager.cpp",
"src/external/puller_util.cpp",
"src/logd/LogEvent.cpp",
@@ -238,6 +239,7 @@
"tests/guardrail/StatsdStats_test.cpp",
"tests/metrics/metrics_test_helper.cpp",
"tests/statsd_test_util.cpp",
+ "tests/storage/StorageManager_test.cpp",
"tests/e2e/WakelockDuration_e2e_test.cpp",
"tests/e2e/MetricActivation_e2e_test.cpp",
"tests/e2e/MetricConditionLink_e2e_test.cpp",
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index b478fed..fb603b9 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -31,6 +31,7 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionController.h>
+#include <cutils/multiuser.h>
#include <dirent.h>
#include <frameworks/base/cmds/statsd/src/statsd_config.pb.h>
#include <private/android_filesystem_config.h>
@@ -47,6 +48,7 @@
using android::base::StringPrintf;
using android::util::FIELD_COUNT_REPEATED;
+using android::util::FIELD_TYPE_INT64;
using android::util::FIELD_TYPE_MESSAGE;
namespace android {
@@ -62,6 +64,8 @@
// for StatsDataDumpProto
const int FIELD_ID_REPORTS_LIST = 1;
+// for TrainInfo experiment id serialization
+const int FIELD_ID_EXPERIMENT_ID = 1;
static binder::Status ok() {
return binder::Status::ok();
@@ -1167,6 +1171,56 @@
return Status::ok();
}
+Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& trainName,
+ int64_t trainVersionCode, int options,
+ int32_t state,
+ const std::vector<int64_t>& experimentIds) {
+ uid_t uid = IPCThreadState::self()->getCallingUid();
+ // For testing
+ if (uid == AID_ROOT || uid == AID_SYSTEM || uid == AID_SHELL) {
+ return ok();
+ }
+
+ // Caller must be granted these permissions
+ if (!checkCallingPermission(String16(kPermissionDump))) {
+ return exception(binder::Status::EX_SECURITY,
+ StringPrintf("UID %d lacks permission %s", uid, kPermissionDump));
+ }
+ if (!checkCallingPermission(String16(kPermissionUsage))) {
+ return exception(binder::Status::EX_SECURITY,
+ StringPrintf("UID %d lacks permission %s", uid, kPermissionUsage));
+ }
+ // TODO: add verifier permission
+
+ userid_t userId = multiuser_get_user_id(uid);
+
+ bool requiresStaging = options | IStatsManager::FLAG_REQUIRE_STAGING;
+ bool rollbackEnabled = options | IStatsManager::FLAG_ROLLBACK_ENABLED;
+ bool requiresLowLatencyMonitor = options | IStatsManager::FLAG_REQUIRE_LOW_LATENCY_MONITOR;
+
+ ProtoOutputStream proto;
+ for (const auto& expId : experimentIds) {
+ proto.write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_EXPERIMENT_ID,
+ (long long)expId);
+ }
+
+ vector<uint8_t> buffer;
+ buffer.resize(proto.size());
+ size_t pos = 0;
+ auto iter = proto.data();
+ while (iter.readBuffer() != NULL) {
+ size_t toRead = iter.currentToRead();
+ std::memcpy(&(buffer[pos]), iter.readBuffer(), toRead);
+ pos += toRead;
+ iter.rp()->move(toRead);
+ }
+ LogEvent event(std::string(String8(trainName).string()), trainVersionCode, requiresStaging,
+ rollbackEnabled, requiresLowLatencyMonitor, state, buffer, userId);
+ mProcessor->OnLogEvent(&event);
+ StorageManager::writeTrainInfo(trainVersionCode, buffer);
+ return Status::ok();
+}
+
hardware::Return<void> StatsService::reportSpeakerImpedance(
const SpeakerImpedance& speakerImpedance) {
LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), speakerImpedance);
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 7f10d74..d24565a 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -31,6 +31,7 @@
#include <android/frameworks/stats/1.0/types.h>
#include <android/os/BnStatsManager.h>
#include <android/os/IStatsCompanionService.h>
+#include <android/os/IStatsManager.h>
#include <binder/IResultReceiver.h>
#include <utils/Looper.h>
@@ -186,6 +187,13 @@
virtual Status unregisterPullerCallback(int32_t atomTag, const String16& packageName) override;
/**
+ * Binder call to log BinaryPushStateChanged atom.
+ */
+ virtual Status sendBinaryPushStateChangedAtom(
+ const android::String16& trainName, int64_t trainVersionCode, int options,
+ int32_t state, const std::vector<int64_t>& experimentIds) override;
+
+ /**
* Binder call to get SpeakerImpedance atom.
*/
virtual Return<void> reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) override;
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 29f67c7..873b772 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -3156,6 +3156,13 @@
optional int64 order_id = 2;
}
+/**
+ * Potential experiment ids that goes with a train install.
+ */
+message TrainExperimentIds {
+ repeated int64 experiment_id = 1;
+}
+
/*
* Logs when a binary push state changes.
* Logged by the installer via public api.
@@ -3182,8 +3189,14 @@
INSTALL_FAILURE = 6;
INSTALL_CANCELLED = 7;
INSTALLER_ROLLBACK_REQUESTED = 8;
+ INSTALLER_ROLLBACK_SUCCESS = 9;
+ INSTALLER_ROLLBACK_FAILURE = 10;
}
optional State state = 6;
+ // Possible experiment ids for monitoring this push.
+ optional TrainExperimentIds experiment_ids = 7 [(log_mode) = MODE_BYTES];
+ // user id
+ optional int32 user_id = 8;
}
/** Represents USB port overheat event. */
@@ -5511,13 +5524,6 @@
}
/**
- * Potential experiment ids that goes with a train install.
- */
-message TrainExperimentIds {
- repeated int64 experiment_id = 1;
-}
-
-/**
* Pulls the ongoing mainline install train version code.
* Pulled from StatsCompanionService
*/
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index ed72b29..98f810f 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -29,10 +29,11 @@
#include "../statscompanion_util.h"
#include "PowerStatsPuller.h"
#include "ResourceHealthManagerPuller.h"
-#include "StatsCompanionServicePuller.h"
#include "StatsCallbackPuller.h"
+#include "StatsCompanionServicePuller.h"
#include "StatsPullerManager.h"
#include "SubsystemSleepStatePuller.h"
+#include "TrainInfoPuller.h"
#include "statslog.h"
#include <iostream>
@@ -152,7 +153,7 @@
new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
// temperature
{android::util::TEMPERATURE,
- {.puller = new StatsCompanionServicePuller(android::util::TEMPERATURE)}},
+ {.puller = new StatsCompanionServicePuller(android::util::TEMPERATURE)}},
// binder_calls
{android::util::BINDER_CALLS,
{.additiveFields = {4, 5, 6, 8, 12},
@@ -231,6 +232,8 @@
// PermissionState.
{android::util::DANGEROUS_PERMISSION_STATE,
{.puller = new StatsCompanionServicePuller(android::util::DANGEROUS_PERMISSION_STATE)}},
+ // TrainInfo.
+ {android::util::TRAIN_INFO, {.puller = new TrainInfoPuller()}},
};
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/src/external/TrainInfoPuller.cpp b/cmds/statsd/src/external/TrainInfoPuller.cpp
new file mode 100644
index 0000000..9d09242
--- /dev/null
+++ b/cmds/statsd/src/external/TrainInfoPuller.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#define DEBUG false // STOPSHIP if true
+#include "Log.h"
+
+#include "external/StatsPuller.h"
+
+#include "TrainInfoPuller.h"
+#include "logd/LogEvent.h"
+#include "stats_log_util.h"
+#include "statslog.h"
+#include "storage/StorageManager.h"
+
+using std::make_shared;
+using std::shared_ptr;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+TrainInfoPuller::TrainInfoPuller() :
+ StatsPuller(android::util::TRAIN_INFO) {
+}
+
+bool TrainInfoPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
+ InstallTrainInfo trainInfo;
+ bool ret = StorageManager::readTrainInfo(trainInfo);
+ if (!ret) {
+ ALOGW("Failed to read train info.");
+ return false;
+ }
+ auto event = make_shared<LogEvent>(getWallClockNs(), getElapsedRealtimeNs(), trainInfo);
+ data->push_back(event);
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/TrainInfoPuller.h b/cmds/statsd/src/external/TrainInfoPuller.h
new file mode 100644
index 0000000..615d023
--- /dev/null
+++ b/cmds/statsd/src/external/TrainInfoPuller.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Reads train info from disk.
+ */
+class TrainInfoPuller : public StatsPuller {
+ public:
+ TrainInfoPuller();
+
+ private:
+ bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index b433c41..d661ee8 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -478,6 +478,11 @@
getAtomMetricStats(metricId).bucketDropped++;
}
+void StatsdStats::noteBucketUnknownCondition(int64_t metricId) {
+ lock_guard<std::mutex> lock(mLock);
+ getAtomMetricStats(metricId).bucketUnknownCondition++;
+}
+
void StatsdStats::noteConditionChangeInNextBucket(int64_t metricId) {
lock_guard<std::mutex> lock(mLock);
getAtomMetricStats(metricId).conditionChangeInNextBucket++;
@@ -497,7 +502,7 @@
std::min(pullStats.minBucketBoundaryDelayNs, timeDelayNs);
}
-StatsdStats::AtomMetricStats& StatsdStats::getAtomMetricStats(int metricId) {
+StatsdStats::AtomMetricStats& StatsdStats::getAtomMetricStats(int64_t metricId) {
auto atomMetricStatsIter = mAtomMetricStats.find(metricId);
if (atomMetricStatsIter != mAtomMetricStats.end()) {
return atomMetricStatsIter->second;
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 5275c8f..e039be2 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -409,6 +409,11 @@
void noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayNs);
/**
+ * Number of buckets with unknown condition.
+ */
+ void noteBucketUnknownCondition(int64_t metricId);
+
+ /**
* Reset the historical stats. Including all stats in icebox, and the tracked stats about
* metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue
* to collect stats after reset() has been called.
@@ -458,6 +463,7 @@
long bucketDropped = 0;
int64_t minBucketBoundaryDelayNs = 0;
int64_t maxBucketBoundaryDelayNs = 0;
+ long bucketUnknownCondition = 0;
} AtomMetricStats;
private:
@@ -488,7 +494,7 @@
std::map<int, PulledAtomStats> mPulledAtomStats;
// Maps metric ID to its stats. The size is capped by the number of metrics.
- std::map<int, AtomMetricStats> mAtomMetricStats;
+ std::map<int64_t, AtomMetricStats> mAtomMetricStats;
struct LogLossStats {
LogLossStats(int32_t sec, int32_t count, int32_t error)
@@ -532,7 +538,7 @@
* Get a reference to AtomMetricStats for a metric. If none exists, create it. The reference
* will live as long as `this`.
*/
- StatsdStats::AtomMetricStats& getAtomMetricStats(int metricId);
+ StatsdStats::AtomMetricStats& getAtomMetricStats(int64_t metricId);
FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd);
FRIEND_TEST(StatsdStatsTest, TestInvalidConfigAdd);
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 40a4070..dec36b5 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -20,6 +20,8 @@
#include "stats_log_util.h"
#include "statslog.h"
+#include <binder/IPCThreadState.h>
+
namespace android {
namespace os {
namespace statsd {
@@ -180,6 +182,25 @@
}
}
+LogEvent::LogEvent(const string& trainName, int64_t trainVersionCode, bool requiresStaging,
+ bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state,
+ const std::vector<uint8_t>& experimentIds, int32_t userId) {
+ mLogdTimestampNs = getWallClockNs();
+ mElapsedTimestampNs = getElapsedRealtimeNs();
+ mTagId = android::util::BINARY_PUSH_STATE_CHANGED;
+ mLogUid = android::IPCThreadState::self()->getCallingUid();
+
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)), Value(trainName)));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainVersionCode)));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)), Value((int)requiresStaging)));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value((int)rollbackEnabled)));
+ mValues.push_back(
+ FieldValue(Field(mTagId, getSimpleField(5)), Value((int)requiresLowLatencyMonitor)));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(6)), Value(state)));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(7)), Value(experimentIds)));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(8)), Value(userId)));
+}
+
LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
const SpeakerImpedance& speakerImpedance) {
mLogdTimestampNs = wallClockTimestampNs;
@@ -340,6 +361,16 @@
}
}
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const InstallTrainInfo& trainInfo) {
+ mLogdTimestampNs = wallClockTimestampNs;
+ mElapsedTimestampNs = elapsedTimestampNs;
+ mTagId = android::util::TRAIN_INFO;
+ mValues.push_back(
+ FieldValue(Field(mTagId, getSimpleField(1)), Value(trainInfo.trainVersionCode)));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainInfo.experimentIds)));
+}
+
LogEvent::LogEvent(int32_t tagId, int64_t timestampNs) : LogEvent(tagId, timestampNs, 0) {}
LogEvent::LogEvent(int32_t tagId, int64_t timestampNs, int32_t uid) {
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 784376a..111a619 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -55,6 +55,11 @@
int32_t mUid;
std::string mTag;
};
+
+struct InstallTrainInfo {
+ int64_t trainVersionCode;
+ std::vector<uint8_t> experimentIds;
+};
/**
* Wrapper for the log_msg structure.
*/
@@ -97,6 +102,11 @@
const std::map<int32_t, std::string>& string_map,
const std::map<int32_t, float>& float_map);
+ // Constructs a BinaryPushStateChanged LogEvent from API call.
+ explicit LogEvent(const std::string& trainName, int64_t trainVersionCode, bool requiresStaging,
+ bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state,
+ const std::vector<uint8_t>& experimentIds, int32_t userId);
+
explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
const SpeakerImpedance& speakerImpedance);
@@ -127,6 +137,9 @@
explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
const VendorAtom& vendorAtom);
+ explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const InstallTrainInfo& installTrainInfo);
+
~LogEvent();
/**
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 350745b..5707de5 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -250,7 +250,7 @@
void CountMetricProducer::onConditionChangedLocked(const bool conditionMet,
const int64_t eventTime) {
VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
- mCondition = conditionMet;
+ mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
}
bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 6c1c47b..da6b97c 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -433,7 +433,7 @@
void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet,
const int64_t eventTime) {
VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
- mCondition = conditionMet;
+ mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
flushIfNeededLocked(eventTime);
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
for (auto& pair : whatIt.second) {
@@ -767,12 +767,13 @@
!mSameConditionDimensionsInTracker,
!mHasLinksToAllConditionDimensionsInTracker,
&dimensionKeysInCondition);
- condition = (conditionState == ConditionState::kTrue);
+ condition = conditionState == ConditionState::kTrue;
if (mDimensionsInCondition.empty() && condition) {
dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
}
} else {
- condition = mCondition;
+ // TODO: The unknown condition state is not handled here, we should fix it.
+ condition = mCondition == ConditionState::kTrue;
if (condition) {
dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
}
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 1b830a3..ec561b5 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -137,6 +137,7 @@
FRIEND_TEST(DurationMetricTrackerTest, TestNoCondition);
FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedCondition);
+ FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState);
FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade);
FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket);
FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade);
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 7e695a6..3b4af65 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -132,7 +132,7 @@
void EventMetricProducer::onConditionChangedLocked(const bool conditionMet,
const int64_t eventTime) {
VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
- mCondition = conditionMet;
+ mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
}
void EventMetricProducer::onMatchedLogEventInternalLocked(
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index d56a355..837d532 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -320,15 +320,15 @@
// When the metric wants to do random sampling and there is already one gauge atom for the
// current bucket, do not do it again.
case GaugeMetric::RANDOM_ONE_SAMPLE: {
- triggerPuller = mCondition && mCurrentSlicedBucket->empty();
+ triggerPuller = mCondition == ConditionState::kTrue && mCurrentSlicedBucket->empty();
break;
}
case GaugeMetric::CONDITION_CHANGE_TO_TRUE: {
- triggerPuller = mCondition;
+ triggerPuller = mCondition == ConditionState::kTrue;
break;
}
case GaugeMetric::FIRST_N_SAMPLES: {
- triggerPuller = mCondition;
+ triggerPuller = mCondition == ConditionState::kTrue;
break;
}
default:
@@ -364,7 +364,7 @@
const int64_t eventTimeNs) {
VLOG("GaugeMetric %lld onConditionChanged", (long long)mMetricId);
flushIfNeededLocked(eventTimeNs);
- mCondition = conditionMet;
+ mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
if (mIsPulled && mTriggerAtomId == -1) {
pullAndMatchEventsLocked(eventTimeNs);
} // else: Push mode. No need to proactively pull the gauge data.
@@ -377,7 +377,7 @@
flushIfNeededLocked(eventTimeNs);
// If the condition is sliced, mCondition is true if any of the dimensions is true. And we will
// pull for every dimension.
- mCondition = overallCondition;
+ mCondition = overallCondition ? ConditionState::kTrue : ConditionState::kFalse;
if (mIsPulled && mTriggerAtomId == -1) {
pullAndMatchEventsLocked(eventTimeNs);
} // else: Push mode. No need to proactively pull the gauge data.
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 1107568..4cf5333 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -45,7 +45,8 @@
&dimensionKeysInCondition);
condition = (conditionState == ConditionState::kTrue);
} else {
- condition = mCondition;
+ // TODO: The unknown condition state is not handled here, we should fix it.
+ condition = mCondition == ConditionState::kTrue;
}
if (mDimensionsInCondition.empty() && condition) {
@@ -107,7 +108,8 @@
if (it == mEventActivationMap.end()) {
return;
}
- if (mActivationType == MetricActivation::ACTIVATE_ON_BOOT) {
+ if (mActivationType == MetricActivation::ACTIVATE_ON_BOOT &&
+ it->second.state == ActivationState::kNotActive) {
it->second.state = ActivationState::kActiveOnBoot;
return;
}
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 849cb76..8ab3b06 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -59,7 +59,7 @@
mTimeBaseNs(timeBaseNs),
mCurrentBucketStartTimeNs(timeBaseNs),
mCurrentBucketNum(0),
- mCondition(conditionIndex >= 0 ? false : true),
+ mCondition(conditionIndex >= 0 ? ConditionState::kUnknown : ConditionState::kTrue),
mConditionSliced(false),
mWizard(wizard),
mConditionTrackerIndex(conditionIndex),
@@ -315,7 +315,7 @@
int64_t mBucketSizeNs;
- bool mCondition;
+ ConditionState mCondition;
bool mConditionSliced;
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 851ae99..bc7c872 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -154,7 +154,7 @@
// Adjust start for partial bucket
mCurrentBucketStartTimeNs = startTimeNs;
// Kicks off the puller immediately if condition is true and diff based.
- if (mIsPulled && mCondition && mUseDiff) {
+ if (mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) {
pullAndMatchEventsLocked(startTimeNs);
}
VLOG("value metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
@@ -341,17 +341,21 @@
flushIfNeededLocked(eventTimeNs);
// Pull on condition changes.
- if (mIsPulled && (mCondition != condition)) {
+ bool conditionChanged = mCondition != condition;
+ bool unknownToFalse = mCondition == ConditionState::kUnknown
+ && condition == ConditionState::kFalse;
+ // We do not need to pull when we go from unknown to false.
+ if (mIsPulled && conditionChanged && !unknownToFalse) {
pullAndMatchEventsLocked(eventTimeNs);
}
// when condition change from true to false, clear diff base but don't
// reset other counters as we may accumulate more value in the bucket.
- if (mUseDiff && mCondition && !condition) {
+ if (mUseDiff && mCondition == ConditionState::kTrue && condition == ConditionState::kFalse) {
resetBase();
}
- mCondition = condition;
+ mCondition = condition ? ConditionState::kTrue : ConditionState::kFalse;
}
void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
@@ -372,7 +376,7 @@
void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData,
bool pullSuccess, int64_t originalPullTimeNs) {
std::lock_guard<std::mutex> lock(mMutex);
- if (mCondition) {
+ if (mCondition == ConditionState::kTrue) {
if (!pullSuccess) {
// If the pull failed, we won't be able to compute a diff.
invalidateCurrentBucket();
@@ -725,6 +729,10 @@
}
void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs) {
+ if (mCondition == ConditionState::kUnknown) {
+ StatsdStats::getInstance().noteBucketUnknownCondition(mMetricId);
+ }
+
VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs,
(int)mCurrentSlicedBucket.size());
int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index f7428a5..09b8fed 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -425,6 +425,7 @@
optional int64 bucket_dropped = 8;
optional int64 min_bucket_boundary_delay_ns = 9;
optional int64 max_bucket_boundary_delay_ns = 10;
+ optional int64 bucket_unknown_condition = 11;
}
repeated AtomMetricStats atom_metric_stats = 17;
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 01043a2..ca645e1 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -84,6 +84,7 @@
const int FIELD_ID_BUCKET_DROPPED = 8;
const int FIELD_ID_MIN_BUCKET_BOUNDARY_DELAY_NS = 9;
const int FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS = 10;
+const int FIELD_ID_BUCKET_UNKNOWN_CONDITION = 11;
namespace {
@@ -489,11 +490,11 @@
protoOutput->end(token);
}
-void writeAtomMetricStatsToStream(const std::pair<int, StatsdStats::AtomMetricStats> &pair,
+void writeAtomMetricStatsToStream(const std::pair<int64_t, StatsdStats::AtomMetricStats> &pair,
util::ProtoOutputStream *protoOutput) {
uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_METRIC_STATS |
FIELD_COUNT_REPEATED);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID, (int32_t)pair.first);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID, (long long)pair.first);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_HARD_DIMENSION_LIMIT_REACHED,
(long long)pair.second.hardDimensionLimitReached);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_LATE_LOG_EVENT_SKIPPED,
@@ -512,6 +513,8 @@
(long long)pair.second.minBucketBoundaryDelayNs);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS,
(long long)pair.second.maxBucketBoundaryDelayNs);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_UNKNOWN_CONDITION,
+ (long long)pair.second.bucketUnknownCondition);
protoOutput->end(token);
}
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
index dcea0e6..59d4865 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -74,7 +74,7 @@
util::ProtoOutputStream* protoOutput);
// Helper function to write AtomMetricStats to ProtoOutputStream
-void writeAtomMetricStatsToStream(const std::pair<int, StatsdStats::AtomMetricStats> &pair,
+void writeAtomMetricStatsToStream(const std::pair<int64_t, StatsdStats::AtomMetricStats> &pair,
util::ProtoOutputStream *protoOutput);
template<class T>
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 90f641a..165e57c 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -38,10 +38,13 @@
#define STATS_DATA_DIR "/data/misc/stats-data"
#define STATS_SERVICE_DIR "/data/misc/stats-service"
+#define TRAIN_INFO_DIR "/data/misc/train-info"
// for ConfigMetricsReportList
const int FIELD_ID_REPORTS = 2;
+std::mutex StorageManager::sTrainInfoMutex;
+
using android::base::StringPrintf;
using std::unique_ptr;
@@ -92,6 +95,71 @@
close(fd);
}
+bool StorageManager::writeTrainInfo(int64_t trainVersionCode,
+ const std::vector<uint8_t>& experimentIds) {
+ std::lock_guard<std::mutex> lock(sTrainInfoMutex);
+
+ deleteAllFiles(TRAIN_INFO_DIR);
+
+ string file_name = StringPrintf("%s/%lld", TRAIN_INFO_DIR, (long long)trainVersionCode);
+
+ int fd = open(file_name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
+ if (fd == -1) {
+ VLOG("Attempt to access %s but failed", file_name.c_str());
+ return false;
+ }
+
+ size_t result = write(fd, experimentIds.data(), experimentIds.size());
+ if (result == experimentIds.size()) {
+ VLOG("Successfully wrote %s", file_name.c_str());
+ } else {
+ VLOG("Failed to write %s", file_name.c_str());
+ return false;
+ }
+
+ result = fchown(fd, AID_STATSD, AID_STATSD);
+ if (result) {
+ VLOG("Failed to chown %s to statsd", file_name.c_str());
+ return false;
+ }
+
+ close(fd);
+ return true;
+}
+
+bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) {
+ std::lock_guard<std::mutex> lock(sTrainInfoMutex);
+
+ unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);
+
+ if (dir == NULL) {
+ VLOG("Directory does not exist: %s", TRAIN_INFO_DIR);
+ return false;
+ }
+
+ dirent* de;
+ while ((de = readdir(dir.get()))) {
+ char* name = de->d_name;
+ if (name[0] == '.') {
+ continue;
+ }
+ trainInfo.trainVersionCode = StrToInt64(name);
+ string fullPath = StringPrintf("%s/%s", TRAIN_INFO_DIR, name);
+ int fd = open(fullPath.c_str(), O_RDONLY | O_CLOEXEC);
+ if (fd != -1) {
+ string str;
+ if (android::base::ReadFdToString(fd, &str)) {
+ close(fd);
+ std::copy(str.begin(), str.end(), std::back_inserter(trainInfo.experimentIds));
+ VLOG("Read train info file successful: %s", fullPath.c_str());
+ return true;
+ }
+ }
+ close(fd);
+ }
+ return false;
+}
+
void StorageManager::deleteFile(const char* file) {
if (remove(file) != 0) {
VLOG("Attempt to delete %s but is not found", file);
diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h
index dcf3bb6..d6df867 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -29,6 +29,11 @@
using android::util::ProtoOutputStream;
+struct TrainInfo {
+ int64_t trainVersionCode;
+ std::vector<uint8_t> experimentIds;
+};
+
class StorageManager : public virtual RefBase {
public:
/**
@@ -37,6 +42,16 @@
static void writeFile(const char* file, const void* buffer, int numBytes);
/**
+ * Writes train info.
+ */
+ static bool writeTrainInfo(int64_t trainVersionCode, const std::vector<uint8_t>& experimentIds);
+
+ /**
+ * Reads train info.
+ */
+ static bool readTrainInfo(InstallTrainInfo& trainInfo);
+
+ /**
* Reads the file content to the buffer.
*/
static bool readFileToString(const char* file, string* content);
@@ -109,6 +124,8 @@
* Prints disk usage statistics about a directory related to statsd.
*/
static void printDirStats(int out, const char* path);
+
+ static std::mutex sTrainInfoMutex;
};
} // namespace statsd
diff --git a/cmds/statsd/statsd.rc b/cmds/statsd/statsd.rc
index e0cbd5d..a98ecd5 100644
--- a/cmds/statsd/statsd.rc
+++ b/cmds/statsd/statsd.rc
@@ -27,3 +27,4 @@
mkdir /data/misc/stats-data/ 0770 statsd system
mkdir /data/misc/stats-service/ 0770 statsd system
mkdir /data/misc/stats-active-metric/ 0770 statsd system
+ mkdir /data/misc/train-info/ 0770 statsd system
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index b540964..b294cad 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -117,6 +117,7 @@
DurationMetricProducer durationProducer(
kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */,
3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
+ durationProducer.mCondition = ConditionState::kFalse;
EXPECT_FALSE(durationProducer.mCondition);
EXPECT_FALSE(durationProducer.isConditionSliced());
@@ -140,6 +141,51 @@
EXPECT_EQ(1LL, buckets2[0].mDuration);
}
+TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+
+ DurationMetric metric;
+ metric.set_id(1);
+ metric.set_bucket(ONE_MINUTE);
+ metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
+
+ int tagId = 1;
+ LogEvent event1(tagId, bucketStartTimeNs + 1);
+ event1.init();
+ LogEvent event2(tagId, bucketStartTimeNs + 2);
+ event2.init();
+ LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1);
+ event3.init();
+ LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3);
+ event4.init();
+
+ FieldMatcher dimensions;
+ DurationMetricProducer durationProducer(
+ kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */,
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
+
+ EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition);
+ EXPECT_FALSE(durationProducer.isConditionSliced());
+
+ durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
+ durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
+ durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
+ EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
+
+ durationProducer.onMatchedLogEvent(1 /* start index*/, event3);
+ durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2);
+ durationProducer.onMatchedLogEvent(2 /* stop index*/, event4);
+ durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
+ EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
+ const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+ EXPECT_EQ(1UL, buckets2.size());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs);
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs);
+ EXPECT_EQ(1LL, buckets2[0].mDuration);
+}
+
TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) {
/**
* The duration starts from the first bucket, through the two partial buckets (10-70sec),
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 572b199..7e7ffed 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -2176,7 +2176,7 @@
eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.mCondition = true;
+ valueProducer.mCondition = ConditionState::kTrue;
vector<shared_ptr<LogEvent>> allData;
valueProducer.onDataPulled(allData, /** succeed */ false, bucketStartTimeNs);
@@ -2226,7 +2226,7 @@
eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.mCondition = false;
+ valueProducer.mCondition = ConditionState::kFalse;
// Max delay is set to 0 so pull will exceed max delay.
valueProducer.onConditionChanged(true, bucketStartTimeNs + 1);
@@ -2257,7 +2257,7 @@
eventMatcherWizard, tagId, bucket2StartTimeNs,
bucket2StartTimeNs, pullerManager);
- valueProducer.mCondition = false;
+ valueProducer.mCondition = ConditionState::kFalse;
// Event should be skipped since it is from previous bucket.
// Pull should not be called.
@@ -2299,7 +2299,7 @@
eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.mCondition = false;
+ valueProducer.mCondition = ConditionState::kFalse;
valueProducer.mHasGlobalBase = false;
valueProducer.onConditionChanged(true, bucketStartTimeNs + 1);
@@ -2351,7 +2351,7 @@
eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.mCondition = true;
+ valueProducer.mCondition = ConditionState::kTrue;
// Bucket start.
vector<shared_ptr<LogEvent>> allData;
@@ -2429,7 +2429,7 @@
eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.mCondition = false;
+ valueProducer.mCondition = ConditionState::kFalse;
valueProducer.onConditionChanged(true, bucket2StartTimeNs + 2);
EXPECT_EQ(true, valueProducer.mCurrentBucketIsInvalid);
EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
@@ -2481,7 +2481,7 @@
eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.mCondition = true;
+ valueProducer.mCondition = ConditionState::kTrue;
// Bucket start.
vector<shared_ptr<LogEvent>> allData;
@@ -2564,7 +2564,7 @@
eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.mCondition = true;
+ valueProducer.mCondition = ConditionState::kTrue;
// Bucket start.
vector<shared_ptr<LogEvent>> allData;
diff --git a/cmds/statsd/tests/storage/StorageManager_test.cpp b/cmds/statsd/tests/storage/StorageManager_test.cpp
new file mode 100644
index 0000000..f66de05
--- /dev/null
+++ b/cmds/statsd/tests/storage/StorageManager_test.cpp
@@ -0,0 +1,52 @@
+// 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.
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include "src/storage/StorageManager.h"
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using namespace testing;
+using std::make_shared;
+using std::shared_ptr;
+using std::vector;
+using testing::Contains;
+
+TEST(StorageManagerTest, TrainInfoReadWriteTest) {
+ InstallTrainInfo trainInfo;
+ trainInfo.trainVersionCode = 12345;
+ const char* expIds = "test_ids";
+ trainInfo.experimentIds.assign(expIds, expIds + strlen(expIds));
+
+ StorageManager::writeTrainInfo(trainInfo.trainVersionCode, trainInfo.experimentIds);
+
+ InstallTrainInfo result;
+ StorageManager::readTrainInfo(result);
+ EXPECT_EQ(trainInfo.trainVersionCode, result.trainVersionCode);
+ EXPECT_EQ(trainInfo.experimentIds.size(), result.experimentIds.size());
+ EXPECT_EQ(trainInfo.experimentIds, result.experimentIds);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index b72ce89..dc2ed4c 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -542,8 +542,6 @@
Landroid/net/IConnectivityManager;->getTetheredIfaces()[Ljava/lang/String;
Landroid/net/IConnectivityManager;->getTetheringErroredIfaces()[Ljava/lang/String;
Landroid/net/IConnectivityManager;->startLegacyVpn(Lcom/android/internal/net/VpnProfile;)V
-Landroid/net/INetd$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetd;
-Landroid/net/INetd;->interfaceAddAddress(Ljava/lang/String;Ljava/lang/String;I)V
Landroid/net/INetworkManagementEventObserver$Stub;-><init>()V
Landroid/net/INetworkPolicyListener$Stub;-><init>()V
Landroid/net/INetworkPolicyManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkPolicyManager;
@@ -2825,7 +2823,6 @@
Lcom/android/internal/telephony/GsmAlphabet$TextEncodingDetails;-><init>()V
Lcom/android/internal/telephony/GsmCdmaCall;->attachFake(Lcom/android/internal/telephony/Connection;Lcom/android/internal/telephony/Call$State;)V
Lcom/android/internal/telephony/GsmCdmaCallTracker;->clearDisconnected()V
-Lcom/android/internal/telephony/GsmCdmaCallTracker;->dialThreeWay(Ljava/lang/String;)Lcom/android/internal/telephony/Connection;
Lcom/android/internal/telephony/GsmCdmaCallTracker;->disableDataCallInEmergencyCall(Ljava/lang/String;)V
Lcom/android/internal/telephony/GsmCdmaCallTracker;->fakeHoldForegroundBeforeDial()V
Lcom/android/internal/telephony/GsmCdmaCallTracker;->getPhone()Lcom/android/internal/telephony/GsmCdmaPhone;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 1a728c1..6908ca2 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2101,7 +2101,8 @@
}
private static Resources createResources(IBinder activityToken, LoadedApk pi, String splitName,
- int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) {
+ int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo,
+ String[] overlayDirs) {
final String[] splitResDirs;
final ClassLoader classLoader;
try {
@@ -2113,7 +2114,7 @@
return ResourcesManager.getInstance().getResources(activityToken,
pi.getResDir(),
splitResDirs,
- pi.getOverlayDirs(),
+ overlayDirs,
pi.getApplicationInfo().sharedLibraryFiles,
displayId,
overrideConfig,
@@ -2131,9 +2132,11 @@
new UserHandle(UserHandle.getUserId(application.uid)), flags, null, null);
final int displayId = getDisplayId();
-
+ // overlayDirs is retrieved directly from ApplicationInfo since ActivityThread may have
+ // a LoadedApk containing Resources with stale overlays for a remote application.
+ final String[] overlayDirs = application.resourceDirs;
c.setResources(createResources(mActivityToken, pi, null, displayId, null,
- getDisplayAdjustments(displayId).getCompatibilityInfo()));
+ getDisplayAdjustments(displayId).getCompatibilityInfo(), overlayDirs));
if (c.mResources != null) {
return c;
}
@@ -2168,7 +2171,7 @@
final int displayId = getDisplayId();
c.setResources(createResources(mActivityToken, pi, null, displayId, null,
- getDisplayAdjustments(displayId).getCompatibilityInfo()));
+ getDisplayAdjustments(displayId).getCompatibilityInfo(), pi.getOverlayDirs()));
if (c.mResources != null) {
return c;
}
@@ -2218,7 +2221,8 @@
final int displayId = getDisplayId();
context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
- overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo()));
+ overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(),
+ mPackageInfo.getOverlayDirs()));
return context;
}
@@ -2233,7 +2237,8 @@
final int displayId = display.getDisplayId();
context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
- null, getDisplayAdjustments(displayId).getCompatibilityInfo()));
+ null, getDisplayAdjustments(displayId).getCompatibilityInfo(),
+ mPackageInfo.getOverlayDirs()));
context.mDisplay = display;
return context;
}
@@ -2416,7 +2421,7 @@
ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo, null,
null, null, 0, null, null);
context.setResources(createResources(null, packageInfo, null, displayId, null,
- packageInfo.getCompatibilityInfo()));
+ packageInfo.getCompatibilityInfo(), packageInfo.getOverlayDirs()));
context.updateDisplay(displayId);
return context;
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 807b7f2..cca8bd0 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -101,7 +101,6 @@
import android.net.IEthernetManager;
import android.net.IIpMemoryStore;
import android.net.IIpSecService;
-import android.net.INetd;
import android.net.INetworkPolicyManager;
import android.net.IpMemoryStore;
import android.net.IpSecManager;
@@ -330,11 +329,10 @@
return new ConnectivityManager(context, service);
}});
- registerService(Context.NETD_SERVICE, INetd.class, new StaticServiceFetcher<INetd>() {
+ registerService(Context.NETD_SERVICE, IBinder.class, new StaticServiceFetcher<IBinder>() {
@Override
- public INetd createService() throws ServiceNotFoundException {
- return INetd.Stub.asInterface(
- ServiceManager.getServiceOrThrow(Context.NETD_SERVICE));
+ public IBinder createService() throws ServiceNotFoundException {
+ return ServiceManager.getServiceOrThrow(Context.NETD_SERVICE);
}
});
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index eaf6c5a..3ea78df 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5910,24 +5910,24 @@
/**
* Flag to denote no restrictions. This should be used to clear any restrictions that may have
* been previously set for the package.
- * @see PackageManager.DistractionRestriction
* @hide
+ * @see #setDistractingPackageRestrictions(String[], int)
*/
@SystemApi
public static final int RESTRICTION_NONE = 0x0;
/**
* Flag to denote that a package should be hidden from any suggestions to the user.
- * @see PackageManager.DistractionRestriction
* @hide
+ * @see #setDistractingPackageRestrictions(String[], int)
*/
@SystemApi
public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 0x00000001;
/**
* Flag to denote that a package's notifications should be hidden.
- * @see PackageManager.DistractionRestriction
* @hide
+ * @see #setDistractingPackageRestrictions(String[], int)
*/
@SystemApi
public static final int RESTRICTION_HIDE_NOTIFICATIONS = 0x00000002;
@@ -5939,7 +5939,6 @@
* @see #setDistractingPackageRestrictions(String[], int)
* @hide
*/
- @SystemApi
@IntDef(flag = true, prefix = {"RESTRICTION_"}, value = {
RESTRICTION_NONE,
RESTRICTION_HIDE_FROM_SUGGESTIONS,
@@ -5957,14 +5956,16 @@
* <p>The caller must hold {@link android.Manifest.permission#SUSPEND_APPS} to use this API.
*
* @param packages Packages to mark as distracting.
- * @param restrictionFlags Any combination of {@link DistractionRestriction restrictions} to
- * impose on the given packages. {@link #RESTRICTION_NONE} can be used
- * to clear any existing restrictions.
+ * @param restrictionFlags Any combination of restrictions to impose on the given packages.
+ * {@link #RESTRICTION_NONE} can be used to clear any existing
+ * restrictions.
* @return A list of packages that could not have the {@code restrictionFlags} set. The system
* may prevent restricting critical packages to preserve normal device function.
*
- * @see DistractionRestriction
* @hide
+ * @see #RESTRICTION_NONE
+ * @see #RESTRICTION_HIDE_FROM_SUGGESTIONS
+ * @see #RESTRICTION_HIDE_NOTIFICATIONS
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.SUSPEND_APPS)
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 0abd5ea..0f67262 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -4848,7 +4848,7 @@
}
/**
- * Sets every the max aspect ratio of every child activity that doesn't already have an aspect
+ * Sets every the min aspect ratio of every child activity that doesn't already have an aspect
* ratio set.
*/
private void setMinAspectRatio(Package owner) {
diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java
index 62cf7d7..b9d49c1 100644
--- a/core/java/android/net/InterfaceConfiguration.java
+++ b/core/java/android/net/InterfaceConfiguration.java
@@ -36,8 +36,9 @@
private LinkAddress mAddr;
private HashSet<String> mFlags = Sets.newHashSet();
- private static final String FLAG_UP = INetd.IF_STATE_UP;
- private static final String FLAG_DOWN = INetd.IF_STATE_DOWN;
+ // Must be kept in sync with constant in INetd.aidl
+ private static final String FLAG_UP = "up";
+ private static final String FLAG_DOWN = "down";
private static final String[] EMPTY_STRING_ARRAY = new String[0];
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 5ab34e9..bf27262 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -92,16 +92,6 @@
public static final int MASK_ALL_NETWORKS = 0b11110000;
public static final int FIREWALL_RULE_DEFAULT = 0;
- public static final int FIREWALL_RULE_ALLOW = INetd.FIREWALL_RULE_ALLOW;
- public static final int FIREWALL_RULE_DENY = INetd.FIREWALL_RULE_DENY;
-
- public static final int FIREWALL_TYPE_WHITELIST = INetd.FIREWALL_WHITELIST;
- public static final int FIREWALL_TYPE_BLACKLIST = INetd.FIREWALL_BLACKLIST;
-
- public static final int FIREWALL_CHAIN_NONE = INetd.FIREWALL_CHAIN_NONE;
- public static final int FIREWALL_CHAIN_DOZABLE = INetd.FIREWALL_CHAIN_DOZABLE;
- public static final int FIREWALL_CHAIN_STANDBY = INetd.FIREWALL_CHAIN_STANDBY;
- public static final int FIREWALL_CHAIN_POWERSAVE = INetd.FIREWALL_CHAIN_POWERSAVE;
public static final String FIREWALL_CHAIN_NAME_NONE = "none";
public static final String FIREWALL_CHAIN_NAME_DOZABLE = "dozable";
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 8813e40..cc241b3 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -25,16 +25,11 @@
import android.content.pm.ResolveInfo;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
-import android.gamedriver.GameDriverProto.Blacklist;
-import android.gamedriver.GameDriverProto.Blacklists;
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;
-
import dalvik.system.VMRuntime;
import java.io.File;
@@ -69,8 +64,7 @@
private static final String ANGLE_RULES_FILE = "a4a_rules.json";
private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID";
- private static final String GAME_DRIVER_BLACKLIST_FLAG = "blacklist";
- private static final int BASE64_FLAGS = Base64.NO_PADDING | Base64.NO_WRAP;
+ private static final String GAME_DRIVER_WHITELIST_ALL = "*";
private ClassLoader mClassLoader;
private String mLayerPath;
@@ -630,43 +624,23 @@
final boolean isOptIn =
getGlobalSettingsString(null, coreSettings,
Settings.Global.GAME_DRIVER_OPT_IN_APPS).contains(packageName);
- if (!isOptIn
- && !getGlobalSettingsString(null, coreSettings,
- Settings.Global.GAME_DRIVER_WHITELIST).contains(packageName)) {
+ final List<String> whitelist = getGlobalSettingsString(null, coreSettings,
+ Settings.Global.GAME_DRIVER_WHITELIST);
+ if (!isOptIn && whitelist.indexOf(GAME_DRIVER_WHITELIST_ALL) != 0
+ && !whitelist.contains(packageName)) {
if (DEBUG) {
Log.w(TAG, packageName + " is not on the whitelist.");
}
return false;
}
- if (!isOptIn) {
- // At this point, the application is on the whitelist only, check whether it's
- // on the blacklist, terminate early when it's on the blacklist.
- try {
- // TODO(b/121350991) Switch to DeviceConfig with property listener.
- final String base64String =
- coreSettings.getString(Settings.Global.GAME_DRIVER_BLACKLIST);
- if (base64String != null && !base64String.isEmpty()) {
- final Blacklists blacklistsProto =
- Blacklists.parseFrom(Base64.decode(base64String, BASE64_FLAGS));
- final List<Blacklist> blacklists = blacklistsProto.getBlacklistsList();
- final long driverVersionCode = driverAppInfo.longVersionCode;
- for (Blacklist blacklist : blacklists) {
- if (blacklist.getVersionCode() == driverVersionCode) {
- for (String pkgName : blacklist.getPackageNamesList()) {
- if (pkgName == packageName) {
- return false;
- }
- }
- break;
- }
- }
- }
- } catch (InvalidProtocolBufferException e) {
- if (DEBUG) {
- Log.w(TAG, "Can't parse blacklist, skip and continue...");
- }
- }
+ // If the application is not opted-in and check whether it's on the blacklist,
+ // terminate early if it's on the blacklist and fallback to system driver.
+ if (!isOptIn
+ && getGlobalSettingsString(null, coreSettings,
+ Settings.Global.GAME_DRIVER_BLACKLIST)
+ .contains(ai.packageName)) {
+ return false;
}
}
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index f1bba1a..6d4c5a0 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -196,4 +196,25 @@
* Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS
*/
oneway void unregisterPullerCallback(int atomTag, String packageName);
+
+ /**
+ * The install requires staging.
+ */
+ const int FLAG_REQUIRE_STAGING = 0x01;
+
+ /**
+ * Rollback is enabled with this install.
+ */
+ const int FLAG_ROLLBACK_ENABLED = 0x02;
+
+ /**
+ * Requires low latency monitoring.
+ */
+ const int FLAG_REQUIRE_LOW_LATENCY_MONITOR = 0x04;
+
+ /**
+ * Logs an event for binary push for module updates.
+ */
+ oneway void sendBinaryPushStateChangedAtom(in String trainName, in long trainVersionCode,
+ in int options, in int state, in long[] experimentId);
}
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 8492b0c..3ee54ce 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -32,6 +32,7 @@
import android.provider.Settings;
import android.telephony.euicc.EuiccManager;
import android.text.TextUtils;
+import android.text.format.DateFormat;
import android.util.Log;
import android.view.Display;
import android.view.WindowManager;
@@ -762,7 +763,8 @@
String reasonArg = null;
if (!TextUtils.isEmpty(reason)) {
- reasonArg = "--reason=" + sanitizeArg(reason);
+ String timeStamp = DateFormat.format("yyyy-MM-ddTHH:mm:ssZ", System.currentTimeMillis()).toString();
+ reasonArg = "--reason=" + sanitizeArg(reason + "," + timeStamp);
}
final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ;
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 104b61d..7eb0300 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -316,10 +316,17 @@
public interface Rollback {
String NAMESPACE = "rollback";
+ String BOOT_NAMESPACE = "rollback_boot";
+
/**
* Timeout in milliseconds for enabling package rollback.
*/
String ENABLE_ROLLBACK_TIMEOUT = "enable_rollback_timeout";
+
+ /**
+ * The lifetime duration of rollback packages in millis
+ */
+ String ROLLBACK_LIFETIME_IN_MILLIS = "rollback_lifetime_in_millis";
}
/**
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 5f1c560..5708342 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -853,10 +853,8 @@
private static final String PATH_DOCUMENT = "document";
private static final String PATH_CHILDREN = "children";
private static final String PATH_SEARCH = "search";
- // TODO(b/72055774): make private again once ScopedAccessProvider is refactored
- /** {@hide} */
@UnsupportedAppUsage
- public static final String PATH_TREE = "tree";
+ private static final String PATH_TREE = "tree";
private static final String PARAM_QUERY = "query";
private static final String PARAM_MANAGE = "manage";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d75c5e2..d925d7e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1657,6 +1657,30 @@
public static final String ACTION_STORAGE_VOLUME_ACCESS_SETTINGS =
"android.settings.STORAGE_VOLUME_ACCESS_SETTINGS";
+
+ /**
+ * Activity Action: Show screen that let user select enable (or disable) Content Capture.
+ * <p>
+ * Input: Nothing.
+ *
+ * <p>
+ * Output: {@link android.app.Activity#RESULT_OK} if user enabled Content Capture,
+ * {@link android.app.Activity#RESULT_CANCELED} if user disabled it, cancelled, or if the caller
+ * is not the Content Capture service associated with the user.
+ *
+ * <p>
+ * <b>NOTE: </b> Caller should call
+ * {@link android.view.contentcapture.ContentCaptureManager#isContentCaptureFeatureEnabled()}
+ * first to check whether the feature is already enabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE =
+ "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE";
+
// End of Intent actions for Settings
/**
@@ -6103,7 +6127,7 @@
* Indicates which clock face to show on lock screen and AOD while docked.
* @hide
*/
- private static final String DOCKED_CLOCK_FACE = "docked_clock_face";
+ public static final String DOCKED_CLOCK_FACE = "docked_clock_face";
/**
* Set by the system to track if the user needs to see the call to action for
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 81d066d..8695da2 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -61,8 +61,6 @@
*/
@SystemApi
@TestApi
-// TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-// in the same package as the test, and that module is compiled with SDK=test_current
public abstract class AugmentedAutofillService extends Service {
private static final String TAG = AugmentedAutofillService.class.getSimpleName();
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index f2a7a35..b989dd9 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -31,8 +31,6 @@
*/
@SystemApi
@TestApi
-//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-//in the same package as the test, and that module is compiled with SDK=test_current
public final class FillCallback {
private static final String TAG = FillCallback.class.getSimpleName();
diff --git a/core/java/android/service/autofill/augmented/FillController.java b/core/java/android/service/autofill/augmented/FillController.java
index d7bc893..67f23d5 100644
--- a/core/java/android/service/autofill/augmented/FillController.java
+++ b/core/java/android/service/autofill/augmented/FillController.java
@@ -38,10 +38,8 @@
*/
@SystemApi
@TestApi
-//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-//in the same package as the test, and that module is compiled with SDK=test_current
public final class FillController {
- private static final String TAG = "FillController";
+ private static final String TAG = FillController.class.getSimpleName();
private final AutofillProxy mProxy;
diff --git a/core/java/android/service/autofill/augmented/FillRequest.java b/core/java/android/service/autofill/augmented/FillRequest.java
index af9905f..9a97bb2 100644
--- a/core/java/android/service/autofill/augmented/FillRequest.java
+++ b/core/java/android/service/autofill/augmented/FillRequest.java
@@ -31,8 +31,6 @@
@SystemApi
// TODO(b/123100811): pass a requestId and/or sessionId?
@TestApi
-// TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-// in the same package as the test, and that module is compiled with SDK=test_current
public final class FillRequest {
final AutofillProxy mProxy;
diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java
index f1e904a7..2ac406c 100644
--- a/core/java/android/service/autofill/augmented/FillResponse.java
+++ b/core/java/android/service/autofill/augmented/FillResponse.java
@@ -30,8 +30,6 @@
*/
@SystemApi
@TestApi
-//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-//in the same package as the test, and that module is compiled with SDK=test_current
public final class FillResponse {
private final FillWindow mFillWindow;
@@ -53,8 +51,6 @@
*/
@SystemApi
@TestApi
- //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
- //in the same package as the test, and that module is compiled with SDK=test_current
public static final class Builder {
private FillWindow mFillWindow;
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index 40e3a12..6e06754 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -63,10 +63,8 @@
*/
@SystemApi
@TestApi
-//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-//in the same package as the test, and that module is compiled with SDK=test_current
public final class FillWindow implements AutoCloseable {
- private static final String TAG = "FillWindow";
+ private static final String TAG = FillWindow.class.getSimpleName();
private final Object mLock = new Object();
private final CloseGuard mCloseGuard = CloseGuard.get();
diff --git a/core/java/android/service/autofill/augmented/PresentationParams.java b/core/java/android/service/autofill/augmented/PresentationParams.java
index 1fb9032..334487d 100644
--- a/core/java/android/service/autofill/augmented/PresentationParams.java
+++ b/core/java/android/service/autofill/augmented/PresentationParams.java
@@ -15,32 +15,19 @@
*/
package android.service.autofill.augmented;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.graphics.Rect;
import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
-import android.util.DebugUtils;
import android.view.View;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
/**
* Abstraction of a "Smart Suggestion" component responsible to embed the autofill UI provided by
- * the intelligence service.
- *
- * <p>The Smart Suggestion can embed the autofill UI in 3 distinct places:
- *
- * <ul>
- * <li>A small area associated with suggestions (like a small strip in the top of the IME),
- * returned by {@link #getSuggestionArea()}
- * <li>The full area (like the full IME window), returned by {@link #getFullArea()}
- * <li>A subset of the aforementioned areas, returned by {@link Area#getSubArea(Rect)}
- * </ul>
+ * the augmented autofill service.
*
* <p>The Smart Suggestion is represented by a {@link Area} object that contains the
* dimensions the smart suggestion window, so the service can use it to calculate the size of the
@@ -50,54 +37,8 @@
*/
@SystemApi
@TestApi
-//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-//in the same package as the test, and that module is compiled with SDK=test_current
public abstract class PresentationParams {
- /**
- * Flag indicating the Smart Suggestion is hosted in the top of its container.
- */
- public static final int FLAG_HINT_GRAVITY_TOP = 0x1;
-
- /**
- * Flag indicating the Smart Suggestion is hosted in the bottom of its container.
- */
- public static final int FLAG_HINT_GRAVITY_BOTTOM = 0x2;
-
- /**
- * Flag indicating the Smart Suggestion is hosted in the left of its container.
- */
- public static final int FLAG_HINT_GRAVITY_LEFT = 0x4;
-
- /**
- * Flag indicating the Smart Suggestion is hosted in the right of its container.
- */
- public static final int FLAG_HINT_GRAVITY_RIGHT = 0x8;
-
- /**
- * Flag indicating the Smart Suggestion is hosted by the IME.
- */
- public static final int FLAG_HOST_IME = 0x10;
-
- /**
- * Flag indicating the Smart Suggestion is hosted by the Android System as a floating popup
- * window.
- */
- public static final int FLAG_HOST_SYSTEM = 0x20;
-
- /** @hide */
- @IntDef(flag = true, prefix = { "FLAG_" }, value = {
- FLAG_HINT_GRAVITY_TOP,
- FLAG_HINT_GRAVITY_BOTTOM,
- FLAG_HINT_GRAVITY_LEFT,
- FLAG_HINT_GRAVITY_RIGHT,
- FLAG_HOST_IME,
- FLAG_HOST_SYSTEM
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface Flags {}
-
-
// /** @hide */
PresentationParams() {}
@@ -112,40 +53,7 @@
return null;
}
- /**
- * Gets the full area for the of the Smart Suggestion provider.
- *
- * @return full dimensions, or {@code null} if the Smart Suggestion provider does not support
- * embeding the UI on its full area.
- */
- @Nullable
- public Area getFullArea() {
- return null;
- }
-
- /**
- * Gets flags associated with the Smart Suggestion.
- *
- * @return any combination of {@link #FLAG_HINT_GRAVITY_TOP},
- * {@link #FLAG_HINT_GRAVITY_BOTTOM}, {@link #FLAG_HINT_GRAVITY_LEFT},
- * {@link #FLAG_HINT_GRAVITY_RIGHT}, {@link #FLAG_HOST_IME}, or
- * {@link #FLAG_HOST_SYSTEM},
- */
- public @Flags int getFlags() {
- return 0;
- }
-
- /** @hide */
- void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
- final int flags = getFlags();
- if (flags > 0) {
- pw.print(prefix); pw.print("flags: "); pw.println(flagsToString(flags));
- }
- }
-
- private static String flagsToString(int flags) {
- return DebugUtils.flagsToString(PresentationParams.class, "FLAG_", flags);
- }
+ abstract void dump(String prefix, PrintWriter pw);
/**
* Area associated with a {@link PresentationParams Smart Suggestions} provider.
@@ -154,8 +62,6 @@
*/
@SystemApi
@TestApi
- //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
- //in the same package as the test, and that module is compiled with SDK=test_current
public abstract static class Area {
/** @hide */
@@ -176,24 +82,6 @@
return mBounds;
}
- /**
- * Gets a subarea limited by given boundaries.
- *
- * @param bounds boundaries relative to this Area.
- *
- * @return new subarea, or {@code null} if the Smart Suggestion host does not support such
- * subaarea.
- *
- * @throws IllegalArgumentException if the {@code bounds} is not fully-contained inside this
- * full Area.
- *
- */
- @Nullable
- public Area getSubArea(@NonNull Rect bounds) {
- // TODO(b/123100712): implement / check boundaries / throw IAE / add unit test
- return null;
- }
-
@Override
public String toString() {
return mBounds.toString();
@@ -220,13 +108,7 @@
}
@Override
- public int getFlags() {
- return FLAG_HOST_SYSTEM | FLAG_HINT_GRAVITY_BOTTOM;
- }
-
- @Override
void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
- super.dump(prefix, pw);
pw.print(prefix); pw.print("area: "); pw.println(mSuggestionArea);
}
}
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index c98f09e..d2f9859 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -339,7 +339,7 @@
}
switch (event.getType()) {
case ContentCaptureEvent.TYPE_SESSION_STARTED:
- final ContentCaptureContext clientContext = event.getClientContext();
+ final ContentCaptureContext clientContext = event.getContentCaptureContext();
clientContext.setParentSessionId(event.getParentSessionId());
mSessionUids.put(sessionIdString, uid);
onCreateContentCaptureSession(clientContext, sessionId);
@@ -383,8 +383,8 @@
}
final Integer rightUid = mSessionUids.get(sessionId);
if (rightUid == null) {
- if (DEBUG) {
- Log.d(TAG, "handleIsRightCallerFor(" + event + "): no session for " + sessionId
+ if (VERBOSE) {
+ Log.v(TAG, "handleIsRightCallerFor(" + event + "): no session for " + sessionId
+ ": " + mSessionUids);
}
// Just ignore, as the session could have been finished already
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 551bb8a..954dc39 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -372,6 +372,13 @@
/**
* @hide
*/
+ public void clearPackageContext() {
+ mContext = null;
+ }
+
+ /**
+ * @hide
+ */
@UnsupportedAppUsage
public Context getPackageContext(Context context) {
if (mContext == null) {
diff --git a/core/java/android/text/style/DynamicDrawableSpan.java b/core/java/android/text/style/DynamicDrawableSpan.java
index be772af..5754014 100644
--- a/core/java/android/text/style/DynamicDrawableSpan.java
+++ b/core/java/android/text/style/DynamicDrawableSpan.java
@@ -77,6 +77,13 @@
*/
public static final int ALIGN_BASELINE = 1;
+ /**
+ * A constant indicating that this span should be vertically centered between
+ * the top and the lowest descender.
+ * @hide
+ */
+ public static final int ALIGN_CENTER = 2;
+
protected final int mVerticalAlignment;
@UnsupportedAppUsage
@@ -142,6 +149,8 @@
int transY = bottom - b.getBounds().bottom;
if (mVerticalAlignment == ALIGN_BASELINE) {
transY -= paint.getFontMetricsInt().descent;
+ } else if (mVerticalAlignment == ALIGN_CENTER) {
+ transY = (bottom - top) / 2 - b.getBounds().height() / 2;
}
canvas.translate(x, transY);
diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java
index ace4bf4..29ced3e 100644
--- a/core/java/android/util/StatsLog.java
+++ b/core/java/android/util/StatsLog.java
@@ -16,7 +16,14 @@
package android.util;
+import static android.Manifest.permission.DUMP;
+import static android.Manifest.permission.PACKAGE_USAGE_STATS;
+
+import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.app.IActivityManager;
+import android.content.Context;
import android.os.IStatsManager;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -31,7 +38,10 @@
private static IStatsManager sService;
- private StatsLog() {}
+ private static Object sLogLock = new Object();
+
+ private StatsLog() {
+ }
/**
* Logs a start event.
@@ -40,11 +50,13 @@
* @return True if the log request was sent to statsd.
*/
public static boolean logStart(int label) {
- synchronized (StatsLog.class) {
+ synchronized (sLogLock) {
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
- if (DEBUG) Slog.d(TAG, "Failed to find statsd when logging start");
+ if (DEBUG) {
+ Slog.d(TAG, "Failed to find statsd when logging start");
+ }
return false;
}
service.sendAppBreadcrumbAtom(label,
@@ -52,7 +64,9 @@
return true;
} catch (RemoteException e) {
sService = null;
- if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when logging start");
+ if (DEBUG) {
+ Slog.d(TAG, "Failed to connect to statsd when logging start");
+ }
return false;
}
}
@@ -65,18 +79,22 @@
* @return True if the log request was sent to statsd.
*/
public static boolean logStop(int label) {
- synchronized (StatsLog.class) {
+ synchronized (sLogLock) {
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
- if (DEBUG) Slog.d(TAG, "Failed to find statsd when logging stop");
+ if (DEBUG) {
+ Slog.d(TAG, "Failed to find statsd when logging stop");
+ }
return false;
}
service.sendAppBreadcrumbAtom(label, StatsLog.APP_BREADCRUMB_REPORTED__STATE__STOP);
return true;
} catch (RemoteException e) {
sService = null;
- if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when logging stop");
+ if (DEBUG) {
+ Slog.d(TAG, "Failed to connect to statsd when logging stop");
+ }
return false;
}
}
@@ -89,11 +107,13 @@
* @return True if the log request was sent to statsd.
*/
public static boolean logEvent(int label) {
- synchronized (StatsLog.class) {
+ synchronized (sLogLock) {
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
- if (DEBUG) Slog.d(TAG, "Failed to find statsd when logging event");
+ if (DEBUG) {
+ Slog.d(TAG, "Failed to find statsd when logging event");
+ }
return false;
}
service.sendAppBreadcrumbAtom(
@@ -101,7 +121,51 @@
return true;
} catch (RemoteException e) {
sService = null;
- if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when logging event");
+ if (DEBUG) {
+ Slog.d(TAG, "Failed to connect to statsd when logging event");
+ }
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Logs an event for binary push for module updates.
+ *
+ * @param trainName name of install train.
+ * @param trainVersionCode version code of the train.
+ * @param options optional flags about this install.
+ * @param state current install state.
+ * @param experimentIds experiment ids.
+ * @return True if the log request was sent to statsd.
+ */
+ @RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS})
+ public static boolean logBinaryPushStateChanged(@NonNull String trainName,
+ long trainVersionCode, int options, int state,
+ @NonNull long[] experimentIds) {
+ synchronized (sLogLock) {
+ try {
+ IStatsManager service = getIStatsManagerLocked();
+ if (service == null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Failed to find statsd when logging event");
+ }
+ return false;
+ }
+ int userId = IActivityManager.Stub.asInterface(
+ ServiceManager.getService("activity"))
+ .getCurrentUser()
+ .id;
+ service.sendBinaryPushStateChangedAtom(
+ trainName, trainVersionCode, options, state, experimentIds);
+ return true;
+ } catch (RemoteException e) {
+ sService = null;
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Failed to connect to StatsCompanionService when logging "
+ + "BinaryPushStateChanged");
+ }
return false;
}
}
@@ -118,7 +182,7 @@
/**
* Add a log to the stats log.
*
- * @param id The id of the atom
+ * @param id The id of the atom
* @param params The parameters of the atom's message.
*/
public static void write(int id, @NonNull Object... params) {
@@ -128,4 +192,13 @@
(boolean) params[4], (int) params[5]);
}
}
+
+ private static void enforceDumpCallingPermission(Context context) {
+ context.enforceCallingPermission(android.Manifest.permission.DUMP, "Need DUMP permission.");
+ }
+
+ private static void enforcesageStatsCallingPermission(Context context) {
+ context.enforceCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS,
+ "Need PACKAGE_USAGE_STATS permission.");
+ }
}
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 62ed901..f37c916 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -336,7 +336,7 @@
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
final View root = findViewByAccessibilityId(accessibilityViewId);
- if (root != null) {
+ if (root != null && isShown(root)) {
mPrefetcher.prefetchAccessibilityNodeInfos(
root, virtualDescendantId, flags, infos, arguments);
}
@@ -448,7 +448,7 @@
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
final View root = findViewByAccessibilityId(accessibilityViewId);
- if (root != null) {
+ if (root != null && isShown(root)) {
AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
if (provider != null) {
infos = provider.findAccessibilityNodeInfosByText(text,
@@ -531,7 +531,7 @@
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
final View root = findViewByAccessibilityId(accessibilityViewId);
- if (root != null) {
+ if (root != null && isShown(root)) {
switch (focusType) {
case AccessibilityNodeInfo.FOCUS_ACCESSIBILITY: {
View host = mViewRootImpl.mAccessibilityFocusedHost;
@@ -621,7 +621,7 @@
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
final View root = findViewByAccessibilityId(accessibilityViewId);
- if (root != null) {
+ if (root != null && isShown(root)) {
View nextView = root.focusSearch(direction);
if (nextView != null) {
next = nextView.createAccessibilityNodeInfo();
@@ -676,7 +676,7 @@
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
final View target = findViewByAccessibilityId(accessibilityViewId);
- if (target != null) {
+ if (target != null && isShown(target)) {
if (action == R.id.accessibilityActionClickOnClickableSpan) {
// Handle this hidden action separately
succeeded = handleClickableSpanActionUiThread(
@@ -759,9 +759,7 @@
if (accessibilityId == AccessibilityNodeInfo.ROOT_ITEM_ID) {
return mViewRootImpl.mView;
} else {
- final View foundView =
- AccessibilityNodeIdManager.getInstance().findView(accessibilityId);
- return isShown(foundView) ? foundView : null;
+ return AccessibilityNodeIdManager.getInstance().findView(accessibilityId);
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ba1b517..2b440dc 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9487,7 +9487,7 @@
* Gets the session used to notify Content Capture events.
*
* @return session explicitly set by {@link #setContentCaptureSession(ContentCaptureSession)},
- * inherited by ancestore, default session or {@code null} if content capture is disabled for
+ * inherited by ancestors, default session or {@code null} if content capture is disabled for
* this view.
*/
@Nullable
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 5e5c826..f93ac4c 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1782,10 +1782,6 @@
/**
* Explicitly limits augmented autofill to the given packages and activities.
*
- * <p>When the whitelist is set, it overrides the values passed to
- * {@link #setActivityAugmentedAutofillEnabled(ComponentName, boolean)}
- * and {@link #setPackageAugmentedAutofillEnabled(String, boolean)}.
- *
* <p>To reset the whitelist, call it passing {@code null} to both arguments.
*
* <p>Useful when the service wants to restrict augmented autofill to a category of apps, like
@@ -1803,8 +1799,6 @@
*/
@SystemApi
@TestApi
- //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
- //in the same package as the test, and that module is compiled with SDK=test_current
public void setAugmentedAutofillWhitelist(@Nullable List<String> packages,
@Nullable List<ComponentName> activities) {
// TODO(b/123100824): implement
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
index acb81e0..13e8a65 100644
--- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -20,10 +20,6 @@
import android.view.autofill.AutofillId;
import android.view.contentcapture.ViewNode.ViewStructureImpl;
-import com.android.internal.util.Preconditions;
-
-import java.io.PrintWriter;
-
/**
* A session that is explicitly created by the app (and hence is a descendant of
* {@link MainContentCaptureSession}).
@@ -35,21 +31,11 @@
@NonNull
private final ContentCaptureSession mParent;
- /**
- * {@link ContentCaptureContext} set by client, or {@code null} when it's the
- * {@link ContentCaptureManager#getMainContentCaptureSession() default session} for the
- * context.
- *
- * @hide
- */
- @NonNull
- private final ContentCaptureContext mClientContext;
-
/** @hide */
protected ChildContentCaptureSession(@NonNull ContentCaptureSession parent,
@NonNull ContentCaptureContext clientContext) {
+ super(clientContext);
mParent = parent;
- mClientContext = Preconditions.checkNotNull(clientContext);
}
@Override
@@ -73,6 +59,11 @@
}
@Override
+ public void updateContentCaptureContext(@Nullable ContentCaptureContext context) {
+ getMainCaptureSession().notifyContextUpdated(mId, context);
+ }
+
+ @Override
void onDestroy() {
getMainCaptureSession().notifyChildSessionFinished(mParent.mId, mId);
}
@@ -101,13 +92,4 @@
boolean isContentCaptureEnabled() {
return getMainCaptureSession().isContentCaptureEnabled();
}
-
- @Override
- void dump(String prefix, PrintWriter pw) {
- if (mClientContext != null) {
- // NOTE: we don't dump clientContent because it could have PII
- pw.print(prefix); pw.println("hasClientContext");
- }
- super.dump(prefix, pw);
- }
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index 22254cd..9cdbefa 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -91,13 +91,22 @@
*/
public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5;
+ /**
+ * Called after a call to
+ * {@link ContentCaptureSession#setContentCaptureContext(ContentCaptureContext)}.
+ *
+ * <p>The passed context is available through {@link #getContentCaptureContext()}.
+ */
+ public static final int TYPE_CONTEXT_UPDATED = 6;
+
/** @hide */
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_VIEW_APPEARED,
TYPE_VIEW_DISAPPEARED,
TYPE_VIEW_TEXT_CHANGED,
TYPE_INITIAL_VIEW_TREE_APPEARING,
- TYPE_INITIAL_VIEW_TREE_APPEARED
+ TYPE_INITIAL_VIEW_TREE_APPEARED,
+ TYPE_CONTEXT_UPDATED
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventType{}
@@ -193,12 +202,13 @@
}
/**
- * Used by {@link #TYPE_SESSION_STARTED}.
+ * Gets the {@link ContentCaptureContext} set calls to
+ * {@link ContentCaptureSession#setContentCaptureContext(ContentCaptureContext)}.
*
- * @hide
+ * <p>Only set on {@link #TYPE_CONTEXT_UPDATED} events.
*/
@Nullable
- public ContentCaptureContext getClientContext() {
+ public ContentCaptureContext getContentCaptureContext() {
return mClientContext;
}
@@ -220,8 +230,8 @@
* Gets the type of the event.
*
* @return one of {@link #TYPE_VIEW_APPEARED}, {@link #TYPE_VIEW_DISAPPEARED},
- * {@link #TYPE_VIEW_TEXT_CHANGED}, {@link #TYPE_INITIAL_VIEW_TREE_APPEARING}, or
- * {@link #TYPE_INITIAL_VIEW_TREE_APPEARED}.
+ * {@link #TYPE_VIEW_TEXT_CHANGED}, {@link #TYPE_INITIAL_VIEW_TREE_APPEARING},
+ * {@link #TYPE_INITIAL_VIEW_TREE_APPEARED}, or {@link #TYPE_CONTEXT_UPDATED}.
*/
public @EventType int getType() {
return mType;
@@ -299,6 +309,10 @@
if (mText != null) {
pw.print(", text="); pw.println(getSanitizedString(mText));
}
+ if (mClientContext != null) {
+ pw.print(", context="); mClientContext.dump(pw); pw.println();
+
+ }
}
@Override
@@ -325,6 +339,9 @@
if (mText != null) {
string.append(", text=").append(getSanitizedString(mText));
}
+ if (mClientContext != null) {
+ string.append(", context=").append(mClientContext);
+ }
return string.append(']').toString();
}
@@ -345,7 +362,7 @@
if (mType == TYPE_SESSION_STARTED || mType == TYPE_SESSION_FINISHED) {
parcel.writeString(mParentSessionId);
}
- if (mType == TYPE_SESSION_STARTED) {
+ if (mType == TYPE_SESSION_STARTED || mType == TYPE_CONTEXT_UPDATED) {
parcel.writeParcelable(mClientContext, flags);
}
}
@@ -375,7 +392,7 @@
if (type == TYPE_SESSION_STARTED || type == TYPE_SESSION_FINISHED) {
event.setParentSessionId(parcel.readString());
}
- if (type == TYPE_SESSION_STARTED) {
+ if (type == TYPE_SESSION_STARTED || type == TYPE_CONTEXT_UPDATED) {
event.setClientContext(parcel.readParcelable(null));
}
return event;
@@ -404,6 +421,8 @@
return "INITIAL_VIEW_HIERARCHY_STARTED";
case TYPE_INITIAL_VIEW_TREE_APPEARED:
return "INITIAL_VIEW_HIERARCHY_FINISHED";
+ case TYPE_CONTEXT_UPDATED:
+ return "CONTEXT_UPDATED";
default:
return "UKNOWN_TYPE: " + type;
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index e028961..b8d3fa6 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -166,6 +166,14 @@
private ContentCaptureSessionId mContentCaptureSessionId;
/**
+ * {@link ContentCaptureContext} set by client, or {@code null} when it's the
+ * {@link ContentCaptureManager#getMainContentCaptureSession() default session} for the
+ * context.
+ */
+ @Nullable
+ private ContentCaptureContext mClientContext;
+
+ /**
* List of children session.
*/
@Nullable
@@ -183,6 +191,12 @@
mId = Preconditions.checkNotNull(id);
}
+ // Used by ChildCOntentCaptureSession
+ ContentCaptureSession(@NonNull ContentCaptureContext initialContext) {
+ this();
+ mClientContext = Preconditions.checkNotNull(initialContext);
+ }
+
/** @hide */
@NonNull
abstract MainContentCaptureSession getMainCaptureSession();
@@ -240,6 +254,30 @@
abstract void flush(@FlushReason int reason);
/**
+ * Sets the {@link ContentCaptureContext} associated with the session.
+ *
+ * <p>Typically used to change the context associated with the default session from an activity.
+ */
+ public final void setContentCaptureContext(@Nullable ContentCaptureContext context) {
+ mClientContext = context;
+ updateContentCaptureContext(context);
+ }
+
+ abstract void updateContentCaptureContext(@Nullable ContentCaptureContext context);
+
+ /**
+ * Gets the {@link ContentCaptureContext} associated with the session.
+ *
+ * @return context set on constructor or by
+ * {@link #setContentCaptureContext(ContentCaptureContext)}, or {@code null} if never
+ * explicitly set.
+ */
+ @Nullable
+ public final ContentCaptureContext getContentCaptureContext() {
+ return mClientContext;
+ }
+
+ /**
* Destroys this session, flushing out all pending notifications to the service.
*
* <p>Once destroyed, any new notification will be dropped.
@@ -424,6 +462,9 @@
@CallSuper
void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
pw.print(prefix); pw.print("id: "); pw.println(mId);
+ if (mClientContext != null) {
+ pw.print(prefix); mClientContext.dump(pw); pw.println();
+ }
synchronized (mLock) {
pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
if (mChildren != null && !mChildren.isEmpty()) {
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 810c967..d949f45 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -15,6 +15,7 @@
*/
package android.view.contentcapture;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_CONTEXT_UPDATED;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARED;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARING;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED;
@@ -269,11 +270,12 @@
private void sendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
final int eventType = event.getType();
if (VERBOSE) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event);
- if (!hasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED) {
+ if (!hasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED
+ && eventType != ContentCaptureEvent.TYPE_CONTEXT_UPDATED) {
// TODO(b/120494182): comment when this could happen (dialogs?)
Log.v(TAG, "handleSendEvent(" + getDebugState() + ", "
+ ContentCaptureEvent.getTypeAsString(eventType)
- + "): session not started yet");
+ + "): dropping because session not started yet");
return;
}
if (mDisabled.get()) {
@@ -476,6 +478,11 @@
}
}
+ @Override
+ public void updateContentCaptureContext(@Nullable ContentCaptureContext context) {
+ notifyContextUpdated(mId, context);
+ }
+
/**
* Resets the buffer and return a {@link ParceledListSlice} with the previous events.
*/
@@ -613,6 +620,12 @@
}
}
+ void notifyContextUpdated(@NonNull String sessionId,
+ @Nullable ContentCaptureContext context) {
+ sendEvent(new ContentCaptureEvent(sessionId, TYPE_CONTEXT_UPDATED)
+ .setClientContext(context));
+ }
+
@Override
void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
pw.print(prefix); pw.print("mContext: "); pw.println(mContext);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 2171fc5..3555822 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -760,7 +760,7 @@
* <p>
* The {@code encoding} parameter specifies whether the data is base64 or URL
* encoded. If the data is base64 encoded, the value of the encoding
- * parameter must be 'base64'. HTML can be encoded with {@link
+ * parameter must be {@code "base64"}. HTML can be encoded with {@link
* android.util.Base64#encodeToString(byte[],int)} like so:
* <pre>
* String unencodedHtml =
@@ -768,11 +768,15 @@
* String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(), Base64.NO_PADDING);
* webView.loadData(encodedHtml, "text/html", "base64");
* </pre>
- * <p>
+ * <p class="note">
* For all other values of {@code encoding} (including {@code null}) it is assumed that the
* data uses ASCII encoding for octets inside the range of safe URL characters and use the
* standard %xx hex encoding of URLs for octets outside that range. See <a
* href="https://tools.ietf.org/html/rfc3986#section-2.2">RFC 3986</a> for more information.
+ * Applications targeting {@link android.os.Build.VERSION_CODES#Q} or later must either use
+ * base64 or encode any {@code #} characters in the content as {@code %23}, otherwise they
+ * will be treated as the end of the content and the remaining text used as a document
+ * fragment identifier.
* <p>
* The {@code mimeType} parameter specifies the format of the data.
* If WebView can't handle the specified MIME type, it will download the data.
@@ -820,7 +824,8 @@
* <p>
* If the base URL uses the data scheme, this method is equivalent to
* calling {@link #loadData(String,String,String) loadData()} and the
- * historyUrl is ignored, and the data will be treated as part of a data: URL.
+ * historyUrl is ignored, and the data will be treated as part of a data: URL,
+ * including the requirement that the content be URL-encoded or base64 encoded.
* If the base URL uses any other scheme, then the data will be loaded into
* the WebView as a plain string (i.e. not part of a data URL) and any URL-encoded
* entities in the string will not be decoded.
diff --git a/core/proto/android/server/connectivity/Android.bp b/core/proto/android/server/connectivity/Android.bp
new file mode 100644
index 0000000..c0ac2cb
--- /dev/null
+++ b/core/proto/android/server/connectivity/Android.bp
@@ -0,0 +1,25 @@
+// 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.
+
+java_library_static {
+ name: "datastallprotosnano",
+ proto: {
+ type: "nano",
+ },
+ srcs: [
+ "data_stall_event.proto",
+ ],
+ sdk_version: "system_current",
+ no_framework_libs: true,
+}
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_qs_night_display_on.xml b/core/res/res/drawable/ic_qs_night_display_on.xml
index 35907cc..a4755ee 100644
--- a/core/res/res/drawable/ic_qs_night_display_on.xml
+++ b/core/res/res/drawable/ic_qs_night_display_on.xml
@@ -1,5 +1,5 @@
<!--
- Copyright (C) 2017 The Android Open Source Project
+ 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.
@@ -14,14 +14,13 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="64dp"
- android:height="64dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <group
- android:translateX="-1.0">
- <path
- android:pathData="M13,12c0,-3.57 2.2,-6.62 5.31,-7.87 0.89,-0.36 0.75,-1.69 -0.19,-1.9 -1.1,-0.24 -2.27,-0.3 -3.48,-0.14 -4.51,0.6 -8.12,4.31 -8.59,8.83C5.43,16.93 10.12,22 16,22c0.73,0 1.43,-0.08 2.12,-0.23 0.95,-0.21 1.1,-1.53 0.2,-1.9A8.471,8.471 0,0 1,13 12z"
- android:fillColor="#FFF"/>
- </group>
-</vector>
\ No newline at end of file
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M6.28,4.81c0,0.16,0.01,0.32,0.02,0.48c0.38,6.58,5.83,12.03,12.41,12.41c0.16,0.01,0.32,0.02,0.47,0.02 c-1.58,1.2-3.53,1.88-5.56,1.88c-0.46,0-0.93-0.03-1.4-0.1c-3.96-0.58-7.13-3.75-7.71-7.71C4.13,9.24,4.8,6.75,6.28,4.81 M8.27,0.6 c-0.08,0-0.17,0.02-0.25,0.07c-3.8,2.2-6.2,6.56-5.49,11.4c0.7,4.82,4.59,8.7,9.4,9.4c0.57,0.08,1.13,0.12,1.69,0.12 c4.15,0,7.78-2.26,9.72-5.62c0.2-0.35-0.07-0.76-0.44-0.76c-0.05,0-0.1,0.01-0.15,0.02c-1.03,0.31-2.12,0.48-3.25,0.48 c-0.22,0-0.44-0.01-0.67-0.02C13.23,15.38,8.62,10.77,8.29,5.17C8.21,3.81,8.38,2.49,8.75,1.26C8.86,0.91,8.59,0.6,8.27,0.6 L8.27,0.6z" />
+</vector>
+
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d14164f..dae2692 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3252,7 +3252,7 @@
skinny aspect ratio that is not expected to be widely used. -->
<item name="config_pictureInPictureMinAspectRatio" format="float" type="dimen">0.41841004184</item>
- <!-- The minimum aspect ratio (width/height) that is supported for picture-in-picture. Any
+ <!-- The maximum aspect ratio (width/height) that is supported for picture-in-picture. Any
ratio larger than this is considered to wide and short to be usable. Currently 2.39:1. -->
<item name="config_pictureInPictureMaxAspectRatio" format="float" type="dimen">2.39</item>
@@ -3273,6 +3273,11 @@
-->
<integer name="config_dockedStackDividerSnapMode">0</integer>
+ <!-- The maximum aspect ratio (longerSide/shorterSide) that is treated as close-to-square. If
+ config_forceDefaultOrientation is set to true, the rotation on a close-to-square display
+ will be fixed. -->
+ <item name="config_closeToSquareDisplayMaxAspectRatio" format="float" type="dimen">1.333</item>
+
<!-- List of comma separated package names for which we the system will not show crash, ANR,
etc. dialogs. -->
<string translatable="false" name="config_appsNotReportingCrashes"></string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a1bafbf..01abffe 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -403,6 +403,7 @@
<java-symbol type="integer" name="config_defaultPictureInPictureGravity" />
<java-symbol type="dimen" name="config_pictureInPictureMinAspectRatio" />
<java-symbol type="dimen" name="config_pictureInPictureMaxAspectRatio" />
+ <java-symbol type="dimen" name="config_closeToSquareDisplayMaxAspectRatio" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_threshold" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_factor" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_penalty_threshold" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 4d2f005..c57b609 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -621,6 +621,7 @@
Settings.Secure.DISABLED_PRINT_SERVICES,
Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS,
Settings.Secure.DISPLAY_DENSITY_FORCED,
+ Settings.Secure.DOCKED_CLOCK_FACE,
Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION,
Settings.Secure.ENABLED_INPUT_METHODS, // Intentionally removed in P
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
index f325d89..a97c3fa 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
@@ -15,6 +15,7 @@
*/
package android.view.contentcapture;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_CONTEXT_UPDATED;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
@@ -174,7 +175,8 @@
assertThat(event.getIds()).isNull();
assertThat(event.getText()).isNull();
assertThat(event.getViewNode()).isNull();
- final ContentCaptureContext clientContext = event.getClientContext();
+ final ContentCaptureContext clientContext = event.getContentCaptureContext();
+ assertThat(clientContext).isNotNull();
assertThat(clientContext.getAction()).isEqualTo("WHATEVER");
}
@@ -205,9 +207,44 @@
assertThat(event.getIds()).isNull();
assertThat(event.getText()).isNull();
assertThat(event.getViewNode()).isNull();
- assertThat(event.getClientContext()).isNull();
+ assertThat(event.getContentCaptureContext()).isNull();
}
+
+ @Test
+ public void testContextUpdated_directly() {
+ final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_CONTEXT_UPDATED)
+ .setClientContext(mClientContext);
+ assertThat(event).isNotNull();
+ assertContextUpdatedEvent(event);
+ }
+
+ @Test
+ public void testContextUpdated_throughParcel() {
+ final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_CONTEXT_UPDATED)
+ .setClientContext(mClientContext);
+ assertThat(event).isNotNull();
+ final ContentCaptureEvent clone = cloneThroughParcel(event);
+ assertContextUpdatedEvent(clone);
+ }
+
+ private void assertContextUpdatedEvent(ContentCaptureEvent event) {
+ assertThat(event.getType()).isEqualTo(TYPE_CONTEXT_UPDATED);
+ assertThat(event.getEventTime()).isAtLeast(MY_EPOCH);
+ assertThat(event.getSessionId()).isEqualTo("42");
+ assertThat(event.getParentSessionId()).isNull();
+ assertThat(event.getId()).isNull();
+ assertThat(event.getIds()).isNull();
+ assertThat(event.getText()).isNull();
+ assertThat(event.getViewNode()).isNull();
+ final ContentCaptureContext clientContext = event.getContentCaptureContext();
+ assertThat(clientContext).isNotNull();
+ assertThat(clientContext.getAction()).isEqualTo("WHATEVER");
+ }
+
+ // TODO(b/123036895): add test for all events type (right now we're just testing the 3 types
+ // that use logic to write to parcel
+
private ContentCaptureEvent cloneThroughParcel(ContentCaptureEvent event) {
Parcel parcel = Parcel.obtain();
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index 34fdebf..b6717e1 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -160,5 +160,10 @@
public void internalNotifyViewHierarchyEvent(boolean started) {
throw new UnsupportedOperationException("should not have been called");
}
+
+ @Override
+ public void updateContentCaptureContext(ContentCaptureContext context) {
+ throw new UnsupportedOperationException("should not have been called");
+ }
}
}
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 76c56609..2ffda83 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -107,7 +107,7 @@
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
mRenderThread.requireGlContext();
} else {
- mRenderThread.vulkanManager().initialize();
+ mRenderThread.requireVkContext();
}
if (!image.get()) {
return CopyResult::UnknownError;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 15f53f2..87cffb5 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -120,7 +120,7 @@
}
DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() {
- mVkManager.initialize();
+ mRenderThread.requireVkContext();
return new DeferredLayerUpdater(mRenderThread.renderState());
}
@@ -136,8 +136,9 @@
setSurfaceColorProperties(colorMode);
if (surface) {
+ mRenderThread.requireVkContext();
mVkSurface = mVkManager.createSurface(surface, colorMode, mSurfaceColorSpace,
- mSurfaceColorType);
+ mSurfaceColorType, mRenderThread.getGrContext());
}
return mVkSurface != nullptr;
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 3904ed2..fc63819 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -173,10 +173,10 @@
initializeDisplayEventReceiver();
mEglManager = new EglManager();
mRenderState = new RenderState(*this);
- mVkManager = new VulkanManager(*this);
+ mVkManager = new VulkanManager();
mCacheManager = new CacheManager(mDisplayInfo);
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
- mVkManager->initialize();
+ requireVkContext();
}
}
@@ -195,8 +195,7 @@
LOG_ALWAYS_FATAL_IF(!glInterface.get());
GrContextOptions options;
- options.fPreferExternalImagesOverES3 = true;
- options.fDisableDistanceFieldPaths = true;
+ initGrContextOptions(options);
auto glesVersion = reinterpret_cast<const char*>(glGetString(GL_VERSION));
auto size = glesVersion ? strlen(glesVersion) : -1;
cacheManager().configureContext(&options, glesVersion, size);
@@ -205,6 +204,25 @@
setGrContext(grContext);
}
+void RenderThread::requireVkContext() {
+ if (mVkManager->hasVkContext()) {
+ return;
+ }
+ mVkManager->initialize();
+ GrContextOptions options;
+ initGrContextOptions(options);
+ // TODO: get a string describing the SPIR-V compiler version and use it here
+ cacheManager().configureContext(&options, nullptr, 0);
+ sk_sp<GrContext> grContext = mVkManager->createContext(options);
+ LOG_ALWAYS_FATAL_IF(!grContext.get());
+ setGrContext(grContext);
+}
+
+void RenderThread::initGrContextOptions(GrContextOptions& options) {
+ options.fPreferExternalImagesOverES3 = true;
+ options.fDisableDistanceFieldPaths = true;
+}
+
void RenderThread::destroyRenderingContext() {
mFunctorManager.onContextDestroyed();
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index b182928..419e7c7 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -112,6 +112,7 @@
void dumpGraphicsMemory(int fd);
void requireGlContext();
+ void requireVkContext();
void destroyRenderingContext();
/**
@@ -122,6 +123,8 @@
*/
static bool isCurrent();
+ static void initGrContextOptions(GrContextOptions& options);
+
protected:
virtual bool threadLoop() override;
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 90397fd..1e685ab 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -55,11 +55,7 @@
#define GET_INST_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(mInstance, "vk" #F)
#define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(mDevice, "vk" #F)
-VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {}
-
void VulkanManager::destroy() {
- mRenderThread.setGrContext(nullptr);
-
// We don't need to explicitly free the command buffer since it automatically gets freed when we
// delete the VkCommandPool below.
mDummyCB = VK_NULL_HANDLE;
@@ -333,29 +329,10 @@
LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&instanceVersion));
LOG_ALWAYS_FATAL_IF(instanceVersion < VK_MAKE_VERSION(1, 1, 0));
- GrVkExtensions extensions;
- this->setupDevice(extensions, mPhysicalDeviceFeatures2);
+ this->setupDevice(mExtensions, mPhysicalDeviceFeatures2);
mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue);
- auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) {
- if (device != VK_NULL_HANDLE) {
- return vkGetDeviceProcAddr(device, proc_name);
- }
- return vkGetInstanceProcAddr(instance, proc_name);
- };
-
- GrVkBackendContext backendContext;
- backendContext.fInstance = mInstance;
- backendContext.fPhysicalDevice = mPhysicalDevice;
- backendContext.fDevice = mDevice;
- backendContext.fQueue = mGraphicsQueue;
- backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex;
- backendContext.fMaxAPIVersion = mAPIVersion;
- backendContext.fVkExtensions = &extensions;
- backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2;
- backendContext.fGetProc = std::move(getProc);
-
// create the command pool for the command buffers
if (VK_NULL_HANDLE == mCommandPool) {
VkCommandPoolCreateInfo commandPoolInfo;
@@ -376,22 +353,35 @@
}
LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE);
-
mGetDeviceQueue(mDevice, mPresentQueueIndex, 0, &mPresentQueue);
- GrContextOptions options;
- options.fDisableDistanceFieldPaths = true;
- // TODO: get a string describing the SPIR-V compiler version and use it here
- mRenderThread.cacheManager().configureContext(&options, nullptr, 0);
- sk_sp<GrContext> grContext(GrContext::MakeVulkan(backendContext, options));
- LOG_ALWAYS_FATAL_IF(!grContext.get());
- mRenderThread.setGrContext(grContext);
-
if (Properties::enablePartialUpdates && Properties::useBufferAge) {
mSwapBehavior = SwapBehavior::BufferAge;
}
}
+sk_sp<GrContext> VulkanManager::createContext(GrContextOptions options) {
+ auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) {
+ if (device != VK_NULL_HANDLE) {
+ return vkGetDeviceProcAddr(device, proc_name);
+ }
+ return vkGetInstanceProcAddr(instance, proc_name);
+ };
+
+ GrVkBackendContext backendContext;
+ backendContext.fInstance = mInstance;
+ backendContext.fPhysicalDevice = mPhysicalDevice;
+ backendContext.fDevice = mDevice;
+ backendContext.fQueue = mGraphicsQueue;
+ backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex;
+ backendContext.fMaxAPIVersion = mAPIVersion;
+ backendContext.fVkExtensions = &mExtensions;
+ backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2;
+ backendContext.fGetProc = std::move(getProc);
+
+ return GrContext::MakeVulkan(backendContext, options);
+}
+
VkFunctorInitParams VulkanManager::getVkFunctorInitParams() const {
return VkFunctorInitParams{
.instance = mInstance,
@@ -470,8 +460,9 @@
ColorMode colorMode = surface->mColorMode;
sk_sp<SkColorSpace> colorSpace = surface->mColorSpace;
SkColorType colorType = surface->mColorType;
+ GrContext* grContext = surface->mGrContext;
destroySurface(surface);
- *surfaceOut = createSurface(window, colorMode, colorSpace, colorType);
+ *surfaceOut = createSurface(window, colorMode, colorSpace, colorType, grContext);
surface = *surfaceOut;
if (!surface) {
return nullptr;
@@ -650,7 +641,7 @@
VulkanSurface::ImageInfo& imageInfo = surface->mImageInfos[i];
imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget(
- mRenderThread.getGrContext(), backendRT, kTopLeft_GrSurfaceOrigin,
+ surface->mGrContext, backendRT, kTopLeft_GrSurfaceOrigin,
surface->mColorType, surface->mColorSpace, &props);
}
@@ -880,15 +871,15 @@
VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode,
sk_sp<SkColorSpace> surfaceColorSpace,
- SkColorType surfaceColorType) {
- initialize();
-
+ SkColorType surfaceColorType,
+ GrContext* grContext) {
+ LOG_ALWAYS_FATAL_IF(!hasVkContext(), "Not initialized");
if (!window) {
return nullptr;
}
VulkanSurface* surface = new VulkanSurface(colorMode, window, surfaceColorSpace,
- surfaceColorType);
+ surfaceColorType, grContext);
VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR));
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 1fe6c65..9763686 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -22,6 +22,8 @@
#endif
#include <vulkan/vulkan.h>
+#include <GrContextOptions.h>
+#include <vk/GrVkExtensions.h>
#include <SkSurface.h>
#include <ui/Fence.h>
#include <utils/StrongPointer.h>
@@ -39,9 +41,9 @@
class VulkanSurface {
public:
VulkanSurface(ColorMode colorMode, ANativeWindow* window, sk_sp<SkColorSpace> colorSpace,
- SkColorType colorType)
+ SkColorType colorType, GrContext* grContext)
: mColorMode(colorMode), mNativeWindow(window), mColorSpace(colorSpace),
- mColorType(colorType) {}
+ mColorType(colorType), mGrContext(grContext) {}
sk_sp<SkSurface> getBackBufferSurface() { return mBackbuffer; }
@@ -93,6 +95,7 @@
SkColorType mColorType;
VkSurfaceTransformFlagBitsKHR mTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
SkMatrix mPreTransform;
+ GrContext* mGrContext;
};
// This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue,
@@ -100,6 +103,9 @@
// windowing contexts. The VulkanManager must be initialized before use.
class VulkanManager {
public:
+ explicit VulkanManager() {}
+ ~VulkanManager() { destroy(); }
+
// Sets up the vulkan context that is shared amonst all clients of the VulkanManager. This must
// be call once before use of the VulkanManager. Multiple calls after the first will simiply
// return.
@@ -112,7 +118,8 @@
// VulkanSurface object which is returned.
VulkanSurface* createSurface(ANativeWindow* window, ColorMode colorMode,
sk_sp<SkColorSpace> surfaceColorSpace,
- SkColorType surfaceColorType);
+ SkColorType surfaceColorType,
+ GrContext* grContext);
// Destroy the VulkanSurface and all associated vulkan objects.
void destroySurface(VulkanSurface* surface);
@@ -143,12 +150,9 @@
// Returned pointers are owned by VulkanManager.
VkFunctorInitParams getVkFunctorInitParams() const;
+ sk_sp<GrContext> createContext(GrContextOptions options);
+
private:
- friend class RenderThread;
-
- explicit VulkanManager(RenderThread& thread);
- ~VulkanManager() { destroy(); }
-
// Sets up the VkInstance and VkDevice objects. Also fills out the passed in
// VkPhysicalDeviceFeatures struct.
void setupDevice(GrVkExtensions&, VkPhysicalDeviceFeatures2&);
@@ -231,8 +235,6 @@
VkPtr<PFN_vkWaitForFences> mWaitForFences;
VkPtr<PFN_vkResetFences> mResetFences;
- RenderThread& mRenderThread;
-
VkInstance mInstance = VK_NULL_HANDLE;
VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
VkDevice mDevice = VK_NULL_HANDLE;
@@ -256,6 +258,7 @@
BufferAge,
};
SwapBehavior mSwapBehavior = SwapBehavior::Discard;
+ GrVkExtensions mExtensions;
};
} /* namespace renderthread */
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index a9f651d..e8ba15f 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -100,7 +100,7 @@
// RenderState only valid once RenderThread is running, so queried here
renderthread::RenderThread& renderThread = renderthread::RenderThread::getInstance();
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
- renderThread.vulkanManager().initialize();
+ renderThread.requireVkContext();
} else {
renderThread.requireGlContext();
}
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index d742cc3..733b866 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -115,10 +115,14 @@
mLocked.pointerSprite.clear();
- for (size_t i = 0; i < mLocked.spots.size(); i++) {
- delete mLocked.spots.itemAt(i);
+ for (auto& it : mLocked.spotsByDisplay) {
+ const std::vector<Spot*>& spots = it.second;
+ size_t numSpots = spots.size();
+ for (size_t i = 0; i < numSpots; i++) {
+ delete spots[i];
+ }
}
- mLocked.spots.clear();
+ mLocked.spotsByDisplay.clear();
mLocked.recycledSprites.clear();
}
@@ -271,22 +275,30 @@
}
void PointerController::setSpots(const PointerCoords* spotCoords,
- const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
+ const uint32_t* spotIdToIndex, BitSet32 spotIdBits, int32_t displayId) {
#if DEBUG_POINTER_UPDATES
ALOGD("setSpots: idBits=%08x", spotIdBits.value);
for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
uint32_t id = idBits.firstMarkedBit();
idBits.clearBit(id);
const PointerCoords& c = spotCoords[spotIdToIndex[id]];
- ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id,
+ ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f, displayId=%" PRId32 ".", id,
c.getAxisValue(AMOTION_EVENT_AXIS_X),
c.getAxisValue(AMOTION_EVENT_AXIS_Y),
- c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+ c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+ displayId);
}
#endif
AutoMutex _l(mLock);
+ std::vector<Spot*> newSpots;
+ std::map<int32_t, std::vector<Spot*>>::const_iterator iter =
+ mLocked.spotsByDisplay.find(displayId);
+ if (iter != mLocked.spotsByDisplay.end()) {
+ newSpots = iter->second;
+ }
+
mSpriteController->openTransaction();
// Add or move spots for fingers that are down.
@@ -298,17 +310,17 @@
float x = c.getAxisValue(AMOTION_EVENT_AXIS_X);
float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
- Spot* spot = getSpotLocked(id);
+ Spot* spot = getSpot(id, newSpots);
if (!spot) {
- spot = createAndAddSpotLocked(id);
+ spot = createAndAddSpotLocked(id, newSpots);
}
- spot->updateSprite(&icon, x, y);
+ spot->updateSprite(&icon, x, y, displayId);
}
// Remove spots for fingers that went up.
- for (size_t i = 0; i < mLocked.spots.size(); i++) {
- Spot* spot = mLocked.spots.itemAt(i);
+ for (size_t i = 0; i < newSpots.size(); i++) {
+ Spot* spot = newSpots[i];
if (spot->id != Spot::INVALID_ID
&& !spotIdBits.hasBit(spot->id)) {
fadeOutAndReleaseSpotLocked(spot);
@@ -316,6 +328,7 @@
}
mSpriteController->closeTransaction();
+ mLocked.spotsByDisplay[displayId] = newSpots;
}
void PointerController::clearSpots() {
@@ -539,21 +552,33 @@
}
// Animate spots that are fading out and being removed.
- for (size_t i = 0; i < mLocked.spots.size();) {
- Spot* spot = mLocked.spots.itemAt(i);
- if (spot->id == Spot::INVALID_ID) {
- spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION;
- if (spot->alpha <= 0) {
- mLocked.spots.removeAt(i);
- releaseSpotLocked(spot);
- continue;
- } else {
- spot->sprite->setAlpha(spot->alpha);
- keepAnimating = true;
+ for(auto it = mLocked.spotsByDisplay.begin(); it != mLocked.spotsByDisplay.end();) {
+ std::vector<Spot*>& spots = it->second;
+ size_t numSpots = spots.size();
+ for (size_t i = 0; i < numSpots;) {
+ Spot* spot = spots[i];
+ if (spot->id == Spot::INVALID_ID) {
+ spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION;
+ if (spot->alpha <= 0) {
+ spots.erase(spots.begin() + i);
+ releaseSpotLocked(spot);
+ numSpots--;
+ continue;
+ } else {
+ spot->sprite->setAlpha(spot->alpha);
+ keepAnimating = true;
+ }
}
+ ++i;
}
- ++i;
+
+ if (spots.size() == 0) {
+ it = mLocked.spotsByDisplay.erase(it);
+ } else {
+ ++it;
+ }
}
+
return keepAnimating;
}
@@ -655,47 +680,49 @@
mSpriteController->closeTransaction();
}
-PointerController::Spot* PointerController::getSpotLocked(uint32_t id) {
- for (size_t i = 0; i < mLocked.spots.size(); i++) {
- Spot* spot = mLocked.spots.itemAt(i);
+PointerController::Spot* PointerController::getSpot(uint32_t id, const std::vector<Spot*>& spots) {
+ for (size_t i = 0; i < spots.size(); i++) {
+ Spot* spot = spots[i];
if (spot->id == id) {
return spot;
}
}
- return NULL;
+
+ return nullptr;
}
-PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id) {
+PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id,
+ std::vector<Spot*>& spots) {
// Remove spots until we have fewer than MAX_SPOTS remaining.
- while (mLocked.spots.size() >= MAX_SPOTS) {
- Spot* spot = removeFirstFadingSpotLocked();
+ while (spots.size() >= MAX_SPOTS) {
+ Spot* spot = removeFirstFadingSpotLocked(spots);
if (!spot) {
- spot = mLocked.spots.itemAt(0);
- mLocked.spots.removeAt(0);
+ spot = spots[0];
+ spots.erase(spots.begin());
}
releaseSpotLocked(spot);
}
// Obtain a sprite from the recycled pool.
sp<Sprite> sprite;
- if (! mLocked.recycledSprites.isEmpty()) {
- sprite = mLocked.recycledSprites.top();
- mLocked.recycledSprites.pop();
+ if (! mLocked.recycledSprites.empty()) {
+ sprite = mLocked.recycledSprites.back();
+ mLocked.recycledSprites.pop_back();
} else {
sprite = mSpriteController->createSprite();
}
// Return the new spot.
Spot* spot = new Spot(id, sprite);
- mLocked.spots.push(spot);
+ spots.push_back(spot);
return spot;
}
-PointerController::Spot* PointerController::removeFirstFadingSpotLocked() {
- for (size_t i = 0; i < mLocked.spots.size(); i++) {
- Spot* spot = mLocked.spots.itemAt(i);
+PointerController::Spot* PointerController::removeFirstFadingSpotLocked(std::vector<Spot*>& spots) {
+ for (size_t i = 0; i < spots.size(); i++) {
+ Spot* spot = spots[i];
if (spot->id == Spot::INVALID_ID) {
- mLocked.spots.removeAt(i);
+ spots.erase(spots.begin() + i);
return spot;
}
}
@@ -706,7 +733,7 @@
spot->sprite->clearIcon();
if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) {
- mLocked.recycledSprites.push(spot->sprite);
+ mLocked.recycledSprites.push_back(spot->sprite);
}
delete spot;
@@ -720,9 +747,13 @@
}
void PointerController::fadeOutAndReleaseAllSpotsLocked() {
- for (size_t i = 0; i < mLocked.spots.size(); i++) {
- Spot* spot = mLocked.spots.itemAt(i);
- fadeOutAndReleaseSpotLocked(spot);
+ for (auto& it : mLocked.spotsByDisplay) {
+ const std::vector<Spot*>& spots = it.second;
+ size_t numSpots = spots.size();
+ for (size_t i = 0; i < numSpots; i++) {
+ Spot* spot = spots[i];
+ fadeOutAndReleaseSpotLocked(spot);
+ }
}
}
@@ -743,12 +774,13 @@
// --- PointerController::Spot ---
-void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y) {
+void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y,
+ int32_t displayId) {
sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
sprite->setAlpha(alpha);
sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale));
sprite->setPosition(x, y);
-
+ sprite->setDisplayId(displayId);
this->x = x;
this->y = y;
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index be05786..52305b8 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -103,7 +103,7 @@
virtual void setPresentation(Presentation presentation);
virtual void setSpots(const PointerCoords* spotCoords,
- const uint32_t* spotIdToIndex, BitSet32 spotIdBits);
+ const uint32_t* spotIdToIndex, BitSet32 spotIdBits, int32_t displayId);
virtual void clearSpots();
void updatePointerIcon(int32_t iconId);
@@ -133,7 +133,7 @@
: id(id), sprite(sprite), alpha(1.0f), scale(1.0f),
x(0.0f), y(0.0f), lastIcon(NULL) { }
- void updateSprite(const SpriteIcon* icon, float x, float y);
+ void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId);
private:
const SpriteIcon* lastIcon;
@@ -180,8 +180,8 @@
int32_t buttonState;
- Vector<Spot*> spots;
- Vector<sp<Sprite> > recycledSprites;
+ std::map<int32_t /* displayId */, std::vector<Spot*>> spotsByDisplay;
+ std::vector<sp<Sprite> > recycledSprites;
} mLocked GUARDED_BY(mLock);
bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
@@ -200,9 +200,9 @@
void removeInactivityTimeoutLocked();
void updatePointerLocked();
- Spot* getSpotLocked(uint32_t id);
- Spot* createAndAddSpotLocked(uint32_t id);
- Spot* removeFirstFadingSpotLocked();
+ Spot* getSpot(uint32_t id, const std::vector<Spot*>& spots);
+ Spot* createAndAddSpotLocked(uint32_t id, std::vector<Spot*>& spots);
+ Spot* removeFirstFadingSpotLocked(std::vector<Spot*>& spots);
void releaseSpotLocked(Spot* spot);
void fadeOutAndReleaseSpotLocked(Spot* spot);
void fadeOutAndReleaseAllSpotsLocked();
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 0caa0c5..b3953fd 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -184,8 +184,7 @@
* @return a new location request
*/
public static LocationRequest create() {
- LocationRequest request = new LocationRequest();
- return request;
+ return new LocationRequest();
}
/** @hide */
@@ -230,12 +229,10 @@
quality = ACCURACY_FINE;
break;
default: {
- switch (criteria.getPowerRequirement()) {
- case Criteria.POWER_HIGH:
- quality = POWER_HIGH;
- break;
- default:
- quality = POWER_LOW;
+ if (criteria.getPowerRequirement() == Criteria.POWER_HIGH) {
+ quality = POWER_HIGH;
+ } else {
+ quality = POWER_LOW;
}
}
}
@@ -288,7 +285,7 @@
*
* @param quality an accuracy or power constant
* @return the same object, so that setters can be chained
- * @throws InvalidArgumentException if the quality constant is not valid
+ * @throws IllegalArgumentException if the quality constant is not valid
*/
public LocationRequest setQuality(int quality) {
checkQuality(quality);
@@ -331,7 +328,7 @@
*
* @param millis desired interval in millisecond, inexact
* @return the same object, so that setters can be chained
- * @throws InvalidArgumentException if the interval is less than zero
+ * @throws IllegalArgumentException if the interval is less than zero
*/
public LocationRequest setInterval(long millis) {
checkInterval(millis);
@@ -433,7 +430,7 @@
*
* @param millis fastest interval for updates in milliseconds, exact
* @return the same object, so that setters can be chained
- * @throws InvalidArgumentException if the interval is less than zero
+ * @throws IllegalArgumentException if the interval is less than zero
*/
public LocationRequest setFastestInterval(long millis) {
checkInterval(millis);
@@ -528,7 +525,7 @@
*
* @param numUpdates the number of location updates requested
* @return the same object, so that setters can be chained
- * @throws InvalidArgumentException if numUpdates is 0 or less
+ * @throws IllegalArgumentException if numUpdates is 0 or less
*/
public LocationRequest setNumUpdates(int numUpdates) {
if (numUpdates <= 0) {
@@ -668,7 +665,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private static void checkProvider(String name) {
if (name == null) {
- throw new IllegalArgumentException("invalid provider: " + name);
+ throw new IllegalArgumentException("invalid provider: null");
}
}
@@ -758,9 +755,11 @@
if (mNumUpdates != Integer.MAX_VALUE) {
s.append(" num=").append(mNumUpdates);
}
- s.append(" lowPowerMode=").append(mLowPowerMode);
+ if (mLowPowerMode) {
+ s.append(" lowPowerMode");
+ }
if (mLocationSettingsIgnored) {
- s.append(" ignoreSettings");
+ s.append(" locationSettingsIgnored");
}
s.append(']');
return s.toString();
diff --git a/location/java/com/android/internal/location/ProviderRequest.java b/location/java/com/android/internal/location/ProviderRequest.java
index a45c20d..af8123a 100644
--- a/location/java/com/android/internal/location/ProviderRequest.java
+++ b/location/java/com/android/internal/location/ProviderRequest.java
@@ -40,7 +40,7 @@
* restrictions or any other restricting factors and always satisfy this request to the best of
* their ability. This flag should only be used in event of an emergency.
*/
- public boolean forceLocation = false;
+ public boolean locationSettingsIgnored = false;
/**
* Whether provider shall make stronger than normal tradeoffs to substantially restrict power
@@ -70,6 +70,7 @@
request.reportLocation = in.readInt() == 1;
request.interval = in.readLong();
request.lowPowerMode = in.readBoolean();
+ request.locationSettingsIgnored = in.readBoolean();
int count = in.readInt();
for (int i = 0; i < count; i++) {
request.locationRequests.add(LocationRequest.CREATOR.createFromParcel(in));
@@ -93,6 +94,7 @@
parcel.writeInt(reportLocation ? 1 : 0);
parcel.writeLong(interval);
parcel.writeBoolean(lowPowerMode);
+ parcel.writeBoolean(locationSettingsIgnored);
parcel.writeInt(locationRequests.size());
for (LocationRequest request : locationRequests) {
request.writeToParcel(parcel, flags);
@@ -107,7 +109,12 @@
s.append("ON");
s.append(" interval=");
TimeUtils.formatDuration(interval, s);
- s.append(" lowPowerMode=" + lowPowerMode);
+ if (lowPowerMode) {
+ s.append(" lowPowerMode");
+ }
+ if (locationSettingsIgnored) {
+ s.append(" locationSettingsIgnored");
+ }
} else {
s.append("OFF");
}
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index 67d6496..dbb581f 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -31,6 +31,7 @@
method public long getInterval();
method public int getQuality();
method public float getSmallestDisplacement();
+ method public boolean isLocationSettingsIgnored();
field public static final int ACCURACY_BLOCK = 102; // 0x66
field public static final int ACCURACY_CITY = 104; // 0x68
field public static final int ACCURACY_FINE = 100; // 0x64
@@ -44,10 +45,10 @@
}
public final class ProviderRequestUnbundled {
- method public boolean getForceLocation();
method public long getInterval();
method public java.util.List<com.android.location.provider.LocationRequestUnbundled> getLocationRequests();
method public boolean getReportLocation();
+ method public boolean isLocationSettingsIgnored();
}
}
diff --git a/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java b/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java
index 41fd769..2511c39 100644
--- a/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java
+++ b/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java
@@ -121,6 +121,15 @@
return delegate.getSmallestDisplacement();
}
+ /**
+ * Returns true if location settings will be ignored in order to satisfy this request.
+ *
+ * @return true if location settings will be ignored in order to satisfy this request
+ */
+ public boolean isLocationSettingsIgnored() {
+ return delegate.isLocationSettingsIgnored();
+ }
+
@Override
public String toString() {
return delegate.toString();
diff --git a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
index b825b58..febbf1b 100644
--- a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
+++ b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
@@ -46,15 +46,15 @@
return mRequest.interval;
}
- public boolean getForceLocation() {
- return mRequest.forceLocation;
+ public boolean isLocationSettingsIgnored() {
+ return mRequest.locationSettingsIgnored;
}
/**
* Never null.
*/
public List<LocationRequestUnbundled> getLocationRequests() {
- List<LocationRequestUnbundled> result = new ArrayList<LocationRequestUnbundled>(
+ List<LocationRequestUnbundled> result = new ArrayList<>(
mRequest.locationRequests.size());
for (LocationRequest r : mRequest.locationRequests) {
result.add(new LocationRequestUnbundled(r));
diff --git a/media/apex/java/android/media/MediaController2.java b/media/apex/java/android/media/MediaController2.java
index 887b447..e85d997 100644
--- a/media/apex/java/android/media/MediaController2.java
+++ b/media/apex/java/android/media/MediaController2.java
@@ -425,7 +425,7 @@
public void onDisconnected(@NonNull MediaController2 controller) {}
/**
- * Called when the playback of the session's playback activeness is changed.
+ * Called when the session's playback activeness is changed.
*
* @param controller the controller for this event
* @param playbackActive {@code true} if the session's playback is active.
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index fb18c3b6..e4d356b 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1180,13 +1180,8 @@
}
final File file = new File(path);
- if (file.exists()) {
- FileInputStream is = new FileInputStream(file);
- FileDescriptor fd = is.getFD();
- setDataSource(fd);
- is.close();
- } else {
- throw new IOException("setDataSource failed.");
+ try (FileInputStream is = new FileInputStream(file)) {
+ setDataSource(is.getFD());
}
}
@@ -2868,15 +2863,9 @@
throw new IllegalArgumentException(msg);
}
- File file = new File(path);
- if (file.exists()) {
- FileInputStream is = new FileInputStream(file);
- FileDescriptor fd = is.getFD();
- addTimedTextSource(fd, mimeType);
- is.close();
- } else {
- // We do not support the case where the path is not a file.
- throw new IOException(path);
+ final File file = new File(path);
+ try (FileInputStream is = new FileInputStream(file)) {
+ addTimedTextSource(is.getFD(), mimeType);
}
}
diff --git a/media/java/android/media/session/ControllerLink.java b/media/java/android/media/session/ControllerLink.java
index 64d283f..40c7166 100644
--- a/media/java/android/media/session/ControllerLink.java
+++ b/media/java/android/media/session/ControllerLink.java
@@ -493,6 +493,22 @@
}
/**
+ * Tell system that a controller requests changing the playback speed.
+ *
+ * @param packageName the package name of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ * @param speed the playback speed
+ */
+ void setPlaybackSpeed(@NonNull String packageName, @NonNull ControllerCallbackLink caller,
+ float speed) {
+ try {
+ mISessionController.setPlaybackSpeed(packageName, caller, speed);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
* Tell system that a controller sends a custom action.
*
* @param packageName the package name of the controller
@@ -759,6 +775,11 @@
@NonNull Rating rating) {
}
+ /** Stub method for ISessionController.setPlaybackSpeed */
+ public void setPlaybackSpeed(@NonNull String packageName,
+ @NonNull ControllerCallbackLink caller, float speed) {
+ }
+
/** Stub method for ISessionController.sendCustomAction */
public void sendCustomAction(@NonNull String packageName,
@NonNull ControllerCallbackLink caller, @NonNull String action,
@@ -953,6 +974,12 @@
}
@Override
+ public void setPlaybackSpeed(String packageName, ControllerCallbackLink caller,
+ float speed) {
+ mControllerStub.setPlaybackSpeed(packageName, caller, speed);
+ }
+
+ @Override
public void sendCustomAction(String packageName, ControllerCallbackLink caller,
String action, Bundle args) {
mControllerStub.sendCustomAction(packageName, caller, action, args);
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index 9b86bfc..cd33c04 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -60,6 +60,8 @@
long pos);
void notifyRate(String packageName, int pid, int uid, in ControllerCallbackLink caller,
in Rating rating);
+ void notifySetPlaybackSpeed(String packageName, int pid, int uid,
+ in ControllerCallbackLink caller, float speed);
void notifyCustomAction(String packageName, int pid, int uid, in ControllerCallbackLink caller,
String action, in Bundle args);
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index e697c65..3e7b4fb 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -76,6 +76,7 @@
void rewind(String packageName, in ControllerCallbackLink caller);
void seekTo(String packageName, in ControllerCallbackLink caller, long pos);
void rate(String packageName, in ControllerCallbackLink caller, in Rating rating);
+ void setPlaybackSpeed(String packageName, in ControllerCallbackLink caller, float speed);
void sendCustomAction(String packageName, in ControllerCallbackLink caller,
String action, in Bundle args);
MediaMetadata getMetadata();
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 6e2c8c5..9e4199c 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -865,6 +865,19 @@
}
/**
+ * Set the playback speed.
+ *
+ * @param speed The playback speed
+ */
+ public void setPlaybackSpeed(float speed) {
+ try {
+ mSessionBinder.setPlaybackSpeed(mContext.getPackageName(), mCbStub, speed);
+ } catch (RuntimeException e) {
+ Log.wtf(TAG, "Error calling setPlaybackSpeed.", e);
+ }
+ }
+
+ /**
* Send a custom action back for the {@link MediaSession} to perform.
*
* @param customAction The action to perform.
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 1b9ebda..8ab893b 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -681,6 +681,19 @@
}
/**
+ * Override to handle the playback speed change.
+ * To update the new playback speed, create a new {@link PlaybackState} by using {@link
+ * PlaybackState.Builder#setState(int, long, float)}, and set it with
+ * {@link #setPlaybackState(PlaybackState)}.
+ *
+ * @param speed the playback speed
+ * @see #setPlaybackState(PlaybackState)
+ * @see PlaybackState.Builder#setState(int, long, float)
+ */
+ public void onSetPlaybackSpeed(float speed) {
+ }
+
+ /**
* Called when a {@link MediaController} wants a {@link PlaybackState.CustomAction} to be
* performed.
*
diff --git a/media/java/android/media/session/MediaSessionEngine.java b/media/java/android/media/session/MediaSessionEngine.java
index e19bdbc..266bf32 100644
--- a/media/java/android/media/session/MediaSessionEngine.java
+++ b/media/java/android/media/session/MediaSessionEngine.java
@@ -538,6 +538,10 @@
postToCallback(caller, CallbackMessageHandler.MSG_RATE, rating, null);
}
+ void dispatchSetPlaybackSpeed(RemoteUserInfo caller, float speed) {
+ postToCallback(caller, CallbackMessageHandler.MSG_SET_PLAYBACK_SPEED, speed, null);
+ }
+
void dispatchCustomAction(RemoteUserInfo caller, String action, Bundle args) {
postToCallback(caller, CallbackMessageHandler.MSG_CUSTOM_ACTION, action, args);
}
@@ -871,6 +875,17 @@
}
/**
+ * Override to handle the playback speed change.
+ *
+ * @param speed the playback speed
+ */
+ public void onSetPlaybackSpeed(float speed) {
+ if (mCallback != null) {
+ mCallback.onSetPlaybackSpeed(speed);
+ }
+ }
+
+ /**
* Called when a {@link MediaController} wants a {@link PlaybackState.CustomAction} to be
* performed.
*
@@ -1092,10 +1107,11 @@
private static final int MSG_REWIND = 17;
private static final int MSG_SEEK_TO = 18;
private static final int MSG_RATE = 19;
- private static final int MSG_CUSTOM_ACTION = 20;
- private static final int MSG_ADJUST_VOLUME = 21;
- private static final int MSG_SET_VOLUME = 22;
- private static final int MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 23;
+ private static final int MSG_SET_PLAYBACK_SPEED = 20;
+ private static final int MSG_CUSTOM_ACTION = 21;
+ private static final int MSG_ADJUST_VOLUME = 22;
+ private static final int MSG_SET_VOLUME = 23;
+ private static final int MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 24;
@SuppressWarnings("WeakerAccess") /* synthetic access */
CallbackWrapper mCallbackWrapper;
@@ -1186,6 +1202,9 @@
case MSG_RATE:
mCallbackWrapper.onSetRating((Rating) obj);
break;
+ case MSG_SET_PLAYBACK_SPEED:
+ mCallbackWrapper.onSetPlaybackSpeed((Float) obj);
+ break;
case MSG_CUSTOM_ACTION:
mCallbackWrapper.onCustomAction((String) obj, msg.getData());
break;
diff --git a/media/java/android/media/session/SessionCallbackLink.java b/media/java/android/media/session/SessionCallbackLink.java
index f59a69d..f9fa45a 100644
--- a/media/java/android/media/session/SessionCallbackLink.java
+++ b/media/java/android/media/session/SessionCallbackLink.java
@@ -462,6 +462,25 @@
}
/**
+ * Notify session that a controller requests changing playback speed.
+ *
+ * @param packageName the package name of the controller
+ * @param pid the pid of the controller
+ * @param uid the uid of the controller
+ * @param caller the {@link ControllerCallbackLink} of the controller
+ * @param speed the playback speed
+ */
+ @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+ public void notifySetPlaybackSpeed(@NonNull String packageName, int pid, int uid,
+ @NonNull ControllerCallbackLink caller, float speed) {
+ try {
+ mISessionCallback.notifySetPlaybackSpeed(packageName, pid, uid, caller, speed);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
* Notify session that a controller sends a custom action.
*
* @param packageName the package name of the controller
@@ -871,6 +890,23 @@
}
}
+ @Override
+ public void notifySetPlaybackSpeed(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, float speed) {
+ ensureMediaControlPermission();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchSetPlaybackSpeed(
+ createRemoteUserInfo(packageName, pid, uid), speed);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void notifyCustomAction(String packageName, int pid, int uid,
ControllerCallbackLink caller, String action, Bundle args) {
ensureMediaControlPermission();
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index ce627ce..a288d010 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -175,6 +175,7 @@
webSettings.setSupportZoom(true);
webSettings.setBuiltInZoomControls(true);
webSettings.setDisplayZoomControls(false);
+ webSettings.setDomStorageEnabled(true);
mWebViewClient = new MyWebViewClient();
webview.setWebViewClient(mWebViewClient);
webview.setWebChromeClient(new MyWebChromeClient());
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index f36b4aa..55c9361 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -106,6 +106,7 @@
webSettings.setLoadWithOverviewMode(true);
webSettings.setSupportZoom(true);
webSettings.setBuiltInZoomControls(true);
+ webSettings.setDomStorageEnabled(true);
mWebViewClient = new MyWebViewClient();
mWebView.setWebViewClient(mWebViewClient);
mWebView.setWebChromeClient(new MyWebChromeClient());
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
index e4d3591..860ebfb 100644
--- a/packages/NetworkStack/AndroidManifest.xml
+++ b/packages/NetworkStack/AndroidManifest.xml
@@ -31,6 +31,7 @@
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.NETWORK_STACK" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<application
android:label="NetworkStack"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java
index 9e59912..b1f6d24 100644
--- a/packages/NetworkStack/src/android/net/ip/IpClient.java
+++ b/packages/NetworkStack/src/android/net/ip/IpClient.java
@@ -46,6 +46,7 @@
import android.net.util.InterfaceParams;
import android.net.util.SharedLog;
import android.os.ConditionVariable;
+import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -380,6 +381,13 @@
public InterfaceParams getInterfaceParams(String ifname) {
return InterfaceParams.getByName(ifname);
}
+
+ /**
+ * Get a INetd connector.
+ */
+ public INetd getNetd(Context context) {
+ return INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE));
+ }
}
public IpClient(Context context, String ifName, IIpClientCallbacks callback,
@@ -413,7 +421,7 @@
// TODO: Consider creating, constructing, and passing in some kind of
// InterfaceController.Dependencies class.
- mNetd = mContext.getSystemService(INetd.class);
+ mNetd = deps.getNetd(mContext);
mInterfaceCtrl = new InterfaceController(mInterfaceName, mNetd, mLog);
mLinkObserver = new IpClientLinkObserver(
diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
index cedcb84..c6a207f 100644
--- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java
+++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
@@ -114,7 +114,8 @@
NetworkStackConnector(Context context) {
mContext = context;
- mNetd = (INetd) context.getSystemService(Context.NETD_SERVICE);
+ mNetd = INetd.Stub.asInterface(
+ (IBinder) context.getSystemService(Context.NETD_SERVICE));
mObserverRegistry = new NetworkObserverRegistry();
mCm = context.getSystemService(ConnectivityManager.class);
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index 2e72d82..b9e901b 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -67,15 +67,9 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
-import android.telephony.CellIdentityCdma;
-import android.telephony.CellIdentityGsm;
-import android.telephony.CellIdentityLte;
-import android.telephony.CellIdentityWcdma;
-import android.telephony.CellInfo;
-import android.telephony.CellInfoCdma;
-import android.telephony.CellInfoGsm;
-import android.telephony.CellInfoLte;
-import android.telephony.CellInfoWcdma;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.NetworkRegistrationState;
+import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
@@ -1312,6 +1306,7 @@
urlConnection.setInstanceFollowRedirects(probeType == ValidationProbeEvent.PROBE_PAC);
urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
+ urlConnection.setRequestProperty("Connection", "close");
urlConnection.setUseCaches(false);
if (mCaptivePortalUserAgent != null) {
urlConnection.setRequestProperty("User-Agent", mCaptivePortalUserAgent);
@@ -1485,10 +1480,6 @@
*/
private void sendNetworkConditionsBroadcast(boolean responseReceived, boolean isCaptivePortal,
long requestTimestampMs, long responseTimestampMs) {
- if (!mWifiManager.isScanAlwaysAvailable()) {
- return;
- }
-
if (!mSystemReady) {
return;
}
@@ -1496,6 +1487,10 @@
Intent latencyBroadcast =
new Intent(NetworkMonitorUtils.ACTION_NETWORK_CONDITIONS_MEASURED);
if (mNetworkCapabilities.hasTransport(TRANSPORT_WIFI)) {
+ if (!mWifiManager.isScanAlwaysAvailable()) {
+ return;
+ }
+
WifiInfo currentWifiInfo = mWifiManager.getConnectionInfo();
if (currentWifiInfo != null) {
// NOTE: getSSID()'s behavior changed in API 17; before that, SSIDs were not
@@ -1515,39 +1510,21 @@
}
latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_WIFI);
} else if (mNetworkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+ // TODO(b/123893112): Support multi-sim.
latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_NETWORK_TYPE,
mTelephonyManager.getNetworkType());
- List<CellInfo> info = mTelephonyManager.getAllCellInfo();
- if (info == null) return;
- int numRegisteredCellInfo = 0;
- for (CellInfo cellInfo : info) {
- if (cellInfo.isRegistered()) {
- numRegisteredCellInfo++;
- if (numRegisteredCellInfo > 1) {
- if (VDBG) {
- logw("more than one registered CellInfo."
- + " Can't tell which is active. Bailing.");
- }
- return;
- }
- if (cellInfo instanceof CellInfoCdma) {
- CellIdentityCdma cellId = ((CellInfoCdma) cellInfo).getCellIdentity();
- latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
- } else if (cellInfo instanceof CellInfoGsm) {
- CellIdentityGsm cellId = ((CellInfoGsm) cellInfo).getCellIdentity();
- latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
- } else if (cellInfo instanceof CellInfoLte) {
- CellIdentityLte cellId = ((CellInfoLte) cellInfo).getCellIdentity();
- latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
- } else if (cellInfo instanceof CellInfoWcdma) {
- CellIdentityWcdma cellId = ((CellInfoWcdma) cellInfo).getCellIdentity();
- latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
- } else {
- if (VDBG) logw("Registered cellinfo is unrecognized");
- return;
- }
- }
+ final ServiceState dataSs = mTelephonyManager.getServiceState();
+ if (dataSs == null) {
+ logw("failed to retrieve ServiceState");
+ return;
}
+ // See if the data sub is registered for PS services on cell.
+ final NetworkRegistrationState nrs = dataSs.getNetworkRegistrationState(
+ NetworkRegistrationState.DOMAIN_PS,
+ AccessNetworkConstants.TransportType.WWAN);
+ latencyBroadcast.putExtra(
+ NetworkMonitorUtils.EXTRA_CELL_ID,
+ nrs == null ? null : nrs.getCellIdentity());
latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_MOBILE);
} else {
return;
diff --git a/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java b/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java
index 7e57d1e..aaaff02 100644
--- a/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java
+++ b/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java
@@ -104,8 +104,8 @@
when(mContext.getSystemService(eq(Context.ALARM_SERVICE))).thenReturn(mAlarm);
when(mContext.getSystemService(eq(ConnectivityManager.class))).thenReturn(mCm);
- when(mContext.getSystemService(INetd.class)).thenReturn(mNetd);
when(mContext.getResources()).thenReturn(mResources);
+ when(mDependencies.getNetd(any())).thenReturn(mNetd);
when(mResources.getInteger(R.integer.config_networkAvoidBadWifi))
.thenReturn(DEFAULT_AVOIDBADWIFI_CONFIG_VALUE);
diff --git a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
index 6d35550..b198f5a 100644
--- a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
+++ b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
@@ -52,7 +52,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
- android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+ android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee"
android:fadingEdge="horizontal"/>
@@ -65,7 +65,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:textAppearance="@android:style/TextAppearance.Material.Small"
+ android:textAppearance="?android:attr/textAppearanceSmall"
android:textAlignment="viewStart"
android:textColor="?android:attr/textColorSecondary"/>
@@ -73,7 +73,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:textAppearance="@android:style/TextAppearance.Material.Small"
+ android:textAppearance="?android:attr/textAppearanceSmall"
android:textAlignment="viewEnd"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="1"
diff --git a/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml
index 9604512..013d2d0 100644
--- a/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml
+++ b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml
@@ -23,7 +23,7 @@
android:layout_marginEnd="16dp"
android:gravity="center"
android:clickable="true"
- android:background="?android:attr/selectableItemBackground"
+ android:background="@*android:drawable/btn_borderless_material"
android:orientation="vertical">
<ImageView
diff --git a/packages/SettingsLib/res/drawable/ic_info_outline_24.xml b/packages/SettingsLib/res/drawable/ic_info_outline_24.xml
new file mode 100644
index 0000000..317e43b
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_info_outline_24.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<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="#FF000000"
+ android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
+</vector>
diff --git a/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java
index a106846..2a12810 100644
--- a/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java
@@ -56,7 +56,7 @@
}
private void init() {
- setIcon(com.android.internal.R.drawable.ic_info_outline_24);
+ setIcon(R.drawable.ic_info_outline_24);
setKey(KEY_FOOTER);
setOrder(ORDER_FOOTER);
setSelectable(false);
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
index 51f6a4b..dc45b4b 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
@@ -28,11 +28,20 @@
android:clipToPadding="false"
android:orientation="vertical"
android:layout_centerHorizontal="true">
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="64dp"
+ android:paddingEnd="64dp"
+ android:visibility="gone"
+ android:textColor="?attr/wallpaperTextColor"
+ android:theme="@style/TextAppearance.Keyguard"
+ />
<view class="com.android.keyguard.KeyguardSliceView$Row"
android:id="@+id/row"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/subtitle_clock_padding"
android:orientation="horizontal"
android:gravity="center"
/>
diff --git a/packages/SystemUI/res-keyguard/layout/text_clock.xml b/packages/SystemUI/res-keyguard/layout/text_clock.xml
index b61ad9c..9f7ea0d 100644
--- a/packages/SystemUI/res-keyguard/layout/text_clock.xml
+++ b/packages/SystemUI/res-keyguard/layout/text_clock.xml
@@ -16,9 +16,10 @@
-->
<TextClock
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
android:letterSpacing="0.03"
android:textColor="?attr/wallpaperTextColor"
android:singleLine="true"
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index e2ba23e..b6a41c1 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -42,18 +42,18 @@
<dimen name="eca_overlap">-10dip</dimen>
<!-- Slice header -->
- <dimen name="widget_title_font_size">24dp</dimen>
- <dimen name="widget_title_bottom_margin">14dp</dimen>
- <dimen name="bottom_text_spacing_digital">0dp</dimen>
+ <dimen name="widget_title_font_size">22dp</dimen>
+ <dimen name="header_subtitle_padding">4dp</dimen>
+ <dimen name="header_icon_size">20dp</dimen>
<!-- Slice subtitle -->
<dimen name="widget_label_font_size">16dp</dimen>
<!-- Clock without header -->
<dimen name="widget_big_font_size">64dp</dimen>
+ <dimen name="bottom_text_spacing_digital">0dp</dimen>
<!-- Clock with header -->
- <dimen name="widget_small_clock_padding">-25dp</dimen>
- <dimen name="widget_small_font_size">24dp</dimen>
- <dimen name="widget_small_font_stroke">0.6dp</dimen>
+ <dimen name="widget_small_font_size">22dp</dimen>
<dimen name="widget_vertical_padding">32dp</dimen>
+ <dimen name="widget_vertical_padding_clock">30dp</dimen>
<!-- Subtitle paddings -->
<dimen name="widget_horizontal_padding">8dp</dimen>
<dimen name="widget_icon_size">16dp</dimen>
diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml
index b673e4f..dd124b7 100644
--- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml
@@ -14,12 +14,19 @@
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" >
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorControlNormal">
+
<path
- android:pathData="M13.51,12l3.75,-3.74c0.41,-0.41 0.41,-1.07 0,-1.48l-4.47,-4.47 -0.03,-0.03a1.046,1.046 0,0 0,-1.76 0.76v6.44L6.95,5.43c-0.41,-0.41 -1.06,-0.41 -1.47,0s-0.41,1.06 0,1.47l5.09,5.1 -5.09,5.09c-0.41,0.41 -0.41,1.06 0,1.47s1.06,0.41 1.47,0L11,14.51v6.45a1.04,1.04 0,0 0,1.75 0.76l0.05,-0.05 4.46,-4.46c0.41,-0.41 0.41,-1.07 0,-1.48L13.51,12zM12.99,9.67v-4.3l2.15,2.15 -2.15,2.15zM12.99,18.62v-4.3l2.15,2.15 -2.15,2.15zM6.06,13.06c-0.59,0.59 -1.54,0.59 -2.12,0a1.49,1.49 0,0 1,0 -2.12,1.49 1.49,0 0,1 2.12,0c0.59,0.59 0.59,1.53 0,2.12zM20.06,10.94c0.59,0.59 0.59,1.54 0,2.12 -0.59,0.59 -1.54,0.59 -2.12,0a1.49,1.49 0,0 1,0 -2.12,1.49 1.49,0 0,1 2.12,0z"
- android:fillColor="#FFFFFFFF" />
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L11,14.41V22h1l5.71-5.71L13.41,12L17.71,7.71z M13,5.83 l1.88,1.88L13,9.59V5.83z M14.88,16.29L13,18.17v-3.76L14.88,16.29z" />
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M 5 10.5 C 5.82842712475 10.5 6.5 11.1715728753 6.5 12 C 6.5 12.8284271247 5.82842712475 13.5 5 13.5 C 4.17157287525 13.5 3.5 12.8284271247 3.5 12 C 3.5 11.1715728753 4.17157287525 10.5 5 10.5 Z" />
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M 19 10.5 C 19.8284271247 10.5 20.5 11.1715728753 20.5 12 C 20.5 12.8284271247 19.8284271247 13.5 19 13.5 C 18.1715728753 13.5 17.5 12.8284271247 17.5 12 C 17.5 11.1715728753 18.1715728753 10.5 19 10.5 Z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml
index 8cc6caa..220c63c 100644
--- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml
@@ -14,12 +14,13 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="64dp"
- android:height="64dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"
- android:tint="?android:attr/colorControlNormal">
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorControlNormal">
+
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M13.5,12l3.8,-3.7c0.4,-0.4 0.4,-1.1 0,-1.5l-4.5,-4.5c-0.4,-0.4 -1.1,-0.4 -1.5,0.1C11.1,2.5 11,2.8 11,3v6.4L6.9,5.4C6.5,5 5.9,5 5.5,5.4s-0.4,1.1 0,1.5l5.1,5.1l-5.1,5.1c-0.4,0.4 -0.4,1.1 0,1.5s1.1,0.4 1.5,0l4.1,-4V21c0,0.6 0.5,1 1,1c0.3,0 0.5,-0.1 0.7,-0.3l0.1,0l4.5,-4.5c0.4,-0.4 0.4,-1.1 0,-1.5L13.5,12zM13,9.7V5.4l2.1,2.2L13,9.7zM13,18.6v-4.3l2.1,2.2L13,18.6z"/>
+ android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L11,14.41V22h1l5.71-5.71L13.41,12L17.71,7.71z M13,5.83 l1.88,1.88L13,9.59V5.83z M14.88,16.29L13,18.17v-3.76L14.88,16.29z" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_airplane.xml b/packages/SystemUI/res/drawable/ic_signal_airplane.xml
index 0a4d752..f708ed9 100644
--- a/packages/SystemUI/res/drawable/ic_signal_airplane.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_airplane.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2017 The Android Open Source Project
+ 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.
@@ -15,16 +15,12 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="20.5"
- android:viewportHeight="20.5">
- <group
- android:translateX="1.75"
- android:translateY="1.4">
- <path
- android:pathData="M16.01,9.87l-6.24,-3.9v-4.7C9.77,0.57 9.21,0 8.5,0S7.23,0.57 7.23,1.28v4.7L0.99,9.88c-0.37,0.23 -0.6,0.64 -0.6,1.08v0.41c0,0.29 0.29,0.5 0.55,0.41l6.27,-1.97v4.7l-1.37,1.02c-0.21,0.16 -0.34,0.41 -0.34,0.68v0.57c0,0.15 0.12,0.23 0.27,0.2 1.67,-0.47 1.12,-0.31 2.73,-0.78 1.03,0.3 1.7,0.49 2.72,0.78 0.15,0.03 0.27,-0.06 0.27,-0.2v-0.57c0,-0.27 -0.13,-0.52 -0.34,-0.68l-1.37,-1.02v-4.7l6.27,1.97c0.28,0.09 0.55,-0.12 0.55,-0.41v-0.41c0.01,-0.45 -0.23,-0.87 -0.59,-1.09z"
- android:fillColor="#FFF"/>
- </group>
-</vector>
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+<path
+ android:fillColor="#FFFFFF"
+ android:pathData="M21,16v-2l-8-5V3.5C13,2.67,12.33,2,11.5,2S10,2.67,10,3.5V9l-8,5v2l8-2.5V19l-2,1.5V22l3.5-1l3.5,1v-1.5L13,19v-5.5L21,16z" />
+</vector>
diff --git a/packages/SystemUI/res/layout-land/global_actions_grid.xml b/packages/SystemUI/res/layout-land/global_actions_grid.xml
new file mode 100644
index 0000000..911b661
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/global_actions_grid.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.android.systemui.globalactions.GlobalActionsGridLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@id/global_actions_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:theme="@style/qs_theme"
+ android:gravity="top|right"
+ android:clipChildren="false"
+>
+
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:gravity="top|right"
+ android:padding="0dp"
+ android:orientation="vertical"
+ android:layoutDirection="ltr"
+ android:layout_marginRight="@dimen/global_actions_grid_container_bottom_margin"
+ >
+ <!-- Grid of action items -->
+ <com.android.systemui.globalactions.ListGridLayout
+ android:id="@android:id/list"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layoutDirection="ltr"
+ android:layout_marginTop="@dimen/global_actions_grid_side_margin"
+ android:translationZ="@dimen/global_actions_translate"
+ android:paddingLeft="@dimen/global_actions_grid_top_padding"
+ android:paddingRight="@dimen/global_actions_grid_bottom_padding"
+ android:paddingTop="@dimen/global_actions_grid_left_padding"
+ android:paddingBottom="@dimen/global_actions_grid_right_padding"
+ android:background="?android:attr/colorBackgroundFloating"
+ >
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:layoutDirection="ltr"
+ android:orientation="horizontal"
+ />
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:layoutDirection="ltr"
+ android:orientation="horizontal"
+ />
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:layoutDirection="ltr"
+ android:orientation="horizontal"
+ />
+ </com.android.systemui.globalactions.ListGridLayout>
+
+ <!-- For separated items-->
+ <LinearLayout
+ android:id="@+id/separated_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/global_actions_grid_side_margin"
+ android:layout_marginBottom="@dimen/global_actions_grid_side_margin"
+ android:paddingTop="@dimen/global_actions_grid_left_padding"
+ android:paddingLeft="@dimen/global_actions_grid_top_padding"
+ android:paddingBottom="@dimen/global_actions_grid_right_padding"
+ android:paddingRight="@dimen/global_actions_grid_bottom_padding"
+ android:orientation="horizontal"
+ android:layoutDirection="ltr"
+ android:background="?android:attr/colorBackgroundFloating"
+ android:translationZ="@dimen/global_actions_translate"
+ />
+
+ </LinearLayout>
+
+</com.android.systemui.globalactions.GlobalActionsGridLayout>
diff --git a/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml b/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml
new file mode 100644
index 0000000..669be1b
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.android.systemui.globalactions.GlobalActionsGridLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@id/global_actions_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:theme="@style/qs_theme"
+ android:gravity="top|left"
+ android:clipChildren="false"
+>
+
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:gravity="bottom|left"
+ android:padding="0dp"
+ android:orientation="vertical"
+ android:layout_marginLeft="@dimen/global_actions_grid_container_bottom_margin"
+ >
+ <!-- For separated items-->
+ <LinearLayout
+ android:id="@+id/separated_button"
+ android:layout_gravity="top|left"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/global_actions_grid_side_margin"
+ android:layout_marginBottom="@dimen/global_actions_grid_side_margin"
+ android:paddingTop="@dimen/global_actions_grid_left_padding"
+ android:paddingLeft="@dimen/global_actions_grid_top_padding"
+ android:paddingBottom="@dimen/global_actions_grid_right_padding"
+ android:paddingRight="@dimen/global_actions_grid_bottom_padding"
+ android:orientation="horizontal"
+ android:layoutDirection="rtl"
+ android:background="?android:attr/colorBackgroundFloating"
+ android:translationZ="@dimen/global_actions_translate"
+ />
+
+ <!-- Grid of action items -->
+ <com.android.systemui.globalactions.ListGridLayout
+ android:id="@android:id/list"
+ android:layout_gravity="bottom|left"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginTop="@dimen/global_actions_grid_side_margin"
+ android:translationZ="@dimen/global_actions_translate"
+ android:paddingLeft="@dimen/global_actions_grid_top_padding"
+ android:paddingRight="@dimen/global_actions_grid_bottom_padding"
+ android:paddingTop="@dimen/global_actions_grid_left_padding"
+ android:paddingBottom="@dimen/global_actions_grid_right_padding"
+ android:background="?android:attr/colorBackgroundFloating"
+ >
+ <LinearLayout
+ android:layout_gravity="bottom"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:layoutDirection="rtl"
+ android:orientation="horizontal"
+ />
+ <LinearLayout
+ android:layout_gravity="bottom"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:layoutDirection="rtl"
+ android:orientation="horizontal"
+ />
+ <LinearLayout
+ android:layout_gravity="bottom"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:layoutDirection="rtl"
+ android:orientation="horizontal"
+ />
+ </com.android.systemui.globalactions.ListGridLayout>
+ </LinearLayout>
+
+</com.android.systemui.globalactions.GlobalActionsGridLayout>
diff --git a/packages/SystemUI/res/layout/global_actions_grid.xml b/packages/SystemUI/res/layout/global_actions_grid.xml
index e6f2376..1b56fa0 100644
--- a/packages/SystemUI/res/layout/global_actions_grid.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid.xml
@@ -12,10 +12,11 @@
>
<LinearLayout
- android:layout_height="290dp"
- android:layout_width="412dp"
- android:gravity="bottom"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:gravity="bottom | right"
android:padding="0dp"
+ android:layoutDirection="ltr"
android:layout_marginBottom="@dimen/global_actions_grid_container_bottom_margin"
>
<!-- For separated items-->
@@ -34,15 +35,11 @@
android:translationZ="@dimen/global_actions_translate"
/>
- <Space android:layout_width="match_parent" android:layout_height="2dp"
- android:layout_weight="1" />
-
<!-- Grid of action items -->
<com.android.systemui.globalactions.ListGridLayout
android:id="@android:id/list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:gravity="right"
android:orientation="horizontal"
android:layoutDirection="rtl"
android:layout_marginRight="@dimen/global_actions_grid_side_margin"
@@ -56,25 +53,19 @@
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="bottom|right"
android:visibility="gone"
- android:gravity="bottom"
android:orientation="vertical"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="bottom|right"
android:visibility="gone"
- android:gravity="bottom"
android:orientation="vertical"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="bottom|right"
android:visibility="gone"
- android:gravity="bottom"
android:orientation="vertical"
/>
</com.android.systemui.globalactions.ListGridLayout>
diff --git a/packages/SystemUI/res/layout/global_actions_grid_item.xml b/packages/SystemUI/res/layout/global_actions_grid_item.xml
index 0c11cd9..a893839 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_item.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_item.xml
@@ -47,6 +47,7 @@
android:gravity="center"
android:textSize="12sp"
android:textAppearance="?android:attr/textAppearanceSmall"
+ android:singleLine="true"
/>
<TextView
@@ -57,5 +58,6 @@
android:gravity="center"
android:textColor="?android:attr/textColorTertiary"
android:textAppearance="?android:attr/textAppearanceSmall"
+ android:singleLine="true"
/>
</LinearLayout>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 7d009b5..b4131d7 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2323,7 +2323,7 @@
<!-- Action for accepting the Ongoing privacy dialog [CHAR LIMIT=10]-->
<string name="ongoing_privacy_dialog_ok">Got it</string>
- <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=20]-->
+ <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=23]-->
<string name="ongoing_privacy_dialog_open_settings">Privacy settings</string>
<!-- Text for item in Ongoing Privacy Dialog title when only one app is using app ops [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 822920e..8de84bf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -1,10 +1,19 @@
package com.android.keyguard;
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Paint;
import android.graphics.Paint.Style;
+import android.transition.ChangeBounds;
+import android.transition.Transition;
+import android.transition.TransitionManager;
+import android.transition.TransitionValues;
import android.util.AttributeSet;
+import android.util.MathUtils;
+import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -16,6 +25,7 @@
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.clock.ClockManager;
import com.android.systemui.Dependency;
+import com.android.systemui.Interpolators;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -28,6 +38,7 @@
*/
public class KeyguardClockSwitch extends RelativeLayout {
+ private final Transition mTransition;
/**
* Optional/alternative clock injected via plugin.
*/
@@ -53,6 +64,10 @@
* Maintain state so that a newly connected plugin can be initialized.
*/
private float mDarkAmount;
+ /**
+ * If the Keyguard Slice has a header (big center-aligned text.)
+ */
+ private boolean mShowingHeader;
private boolean mSupportsDarkText;
private int[] mColorPalette;
@@ -98,6 +113,7 @@
public KeyguardClockSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
+ mTransition = new ClockBoundsTransition();
}
/**
@@ -286,6 +302,26 @@
}
}
+ /**
+ * Sets if the keyguard slice is showing a center-aligned header. We need a smaller clock
+ * in these cases.
+ */
+ public void setKeyguardShowingHeader(boolean hasHeader) {
+ if (mShowingHeader == hasHeader || hasCustomClock()) {
+ return;
+ }
+ mShowingHeader = hasHeader;
+
+ TransitionManager.beginDelayedTransition((ViewGroup) mClockView.getParent(), mTransition);
+ int fontSize = mContext.getResources().getDimensionPixelSize(mShowingHeader
+ ? R.dimen.widget_small_font_size : R.dimen.widget_big_font_size);
+ int paddingBottom = mContext.getResources().getDimensionPixelSize(mShowingHeader
+ ? R.dimen.widget_vertical_padding_clock : R.dimen.header_subtitle_padding);
+ mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
+ mClockView.setPadding(mClockView.getPaddingLeft(), mClockView.getPaddingTop(),
+ mClockView.getPaddingRight(), paddingBottom);
+ }
+
@VisibleForTesting (otherwise = VisibleForTesting.NONE)
ClockManager.ClockChangedListener getClockChangedListener() {
return mClockChangedListener;
@@ -295,4 +331,54 @@
StatusBarStateController.StateListener getStateListener() {
return mStateListener;
}
+
+ /**
+ * Special layout transition that scales the clock view as its bounds change, to make it look
+ * like the text is shrinking.
+ */
+ private class ClockBoundsTransition extends ChangeBounds {
+
+ ClockBoundsTransition() {
+ setDuration(KeyguardSliceView.DEFAULT_ANIM_DURATION / 2);
+ setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ }
+
+ @Override
+ public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ Animator animator = super.createAnimator(sceneRoot, startValues, endValues);
+ if (animator == null || startValues.view != mClockView) {
+ return animator;
+ }
+
+ ValueAnimator boundsAnimator = null;
+ if (animator instanceof AnimatorSet) {
+ Animator first = ((AnimatorSet) animator).getChildAnimations().get(0);
+ if (first instanceof ValueAnimator) {
+ boundsAnimator = (ValueAnimator) first;
+ }
+ } else if (animator instanceof ValueAnimator) {
+ boundsAnimator = (ValueAnimator) animator;
+ }
+
+ if (boundsAnimator != null) {
+ float bigFontSize = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.widget_big_font_size);
+ float smallFontSize = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.widget_small_font_size);
+ float startScale = mShowingHeader
+ ? bigFontSize / smallFontSize : smallFontSize / bigFontSize;
+ boundsAnimator.addUpdateListener(animation -> {
+ float scale = MathUtils.lerp(startScale, 1f /* stop */,
+ animation.getAnimatedFraction());
+ mClockView.setPivotX(mClockView.getWidth() / 2);
+ mClockView.setPivotY(0);
+ mClockView.setScaleX(scale);
+ mClockView.setScaleY(scale);
+ });
+ }
+
+ return animator;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index bac7844..2040a76 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -37,10 +37,10 @@
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
-import android.view.ViewGroup;
import android.view.animation.Animation;
import android.widget.Button;
import android.widget.LinearLayout;
+import android.widget.TextView;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
@@ -58,6 +58,7 @@
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
+import com.android.systemui.R;
import com.android.systemui.keyguard.KeyguardSliceProvider;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
@@ -78,6 +79,8 @@
private final HashMap<View, PendingIntent> mClickActions;
private Uri mKeyguardSliceUri;
+ @VisibleForTesting
+ TextView mTitle;
private Row mRow;
private int mTextColor;
private float mDarkAmount = 0;
@@ -91,6 +94,8 @@
private Runnable mContentChangeListener;
private Slice mSlice;
private boolean mHasHeader;
+ private final int mRowWithHeaderPadding;
+ private final int mRowPadding;
public KeyguardSliceView(Context context) {
this(context, null, 0);
@@ -107,6 +112,9 @@
tunerService.addTunable(this, Settings.Secure.KEYGUARD_SLICE_URI);
mClickActions = new HashMap<>();
+ mRowPadding = context.getResources().getDimensionPixelSize(R.dimen.subtitle_clock_padding);
+ mRowWithHeaderPadding = context.getResources()
+ .getDimensionPixelSize(R.dimen.header_subtitle_padding);
LayoutTransition transition = new LayoutTransition();
transition.setStagger(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION / 2);
@@ -117,13 +125,13 @@
transition.setInterpolator(LayoutTransition.APPEARING, Interpolators.FAST_OUT_SLOW_IN);
transition.setInterpolator(LayoutTransition.DISAPPEARING, Interpolators.ALPHA_OUT);
transition.setAnimateParentHierarchy(false);
- transition.addTransitionListener(new SliceViewTransitionListener());
setLayoutTransition(transition);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+ mTitle = findViewById(R.id.title);
mRow = findViewById(R.id.row);
mTextColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor);
mIconSize = (int) mContext.getResources().getDimension(R.dimen.widget_icon_size);
@@ -160,6 +168,7 @@
private void showSlice() {
Trace.beginSection("KeyguardSliceView#showSlice");
if (mSlice == null) {
+ mTitle.setVisibility(GONE);
mRow.setVisibility(GONE);
mHasHeader = false;
if (mContentChangeListener != null) {
@@ -170,8 +179,7 @@
ListContent lc = new ListContent(getContext(), mSlice);
SliceContent headerContent = lc.getHeader();
- mHasHeader = headerContent != null
- && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM);
+ mHasHeader = headerContent != null && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM);
List<SliceContent> subItems = new ArrayList<>();
for (int i = 0; i < lc.getRowItems().size(); i++) {
SliceContent subItem = lc.getRowItems().get(i);
@@ -181,12 +189,26 @@
subItems.add(subItem);
}
}
+ if (!mHasHeader) {
+ mTitle.setVisibility(GONE);
+ } else {
+ mTitle.setVisibility(VISIBLE);
+
+ RowContent header = lc.getHeader();
+ SliceItem mainTitle = header.getTitleItem();
+ CharSequence title = mainTitle != null ? mainTitle.getText() : null;
+ mTitle.setText(title);
+ }
mClickActions.clear();
final int subItemsCount = subItems.size();
final int blendedColor = getTextColor();
final int startIndex = mHasHeader ? 1 : 0; // First item is header; skip it
mRow.setVisibility(subItemsCount > 0 ? VISIBLE : GONE);
+ LinearLayout.LayoutParams layoutParams = (LayoutParams) mRow.getLayoutParams();
+ layoutParams.topMargin = mHasHeader ? mRowWithHeaderPadding : mRowPadding;
+ mRow.setLayoutParams(layoutParams);
+
for (int i = startIndex; i < subItemsCount; i++) {
RowContent rc = (RowContent) subItems.get(i);
SliceItem item = rc.getSliceItem();
@@ -250,6 +272,7 @@
private void updateTextColors() {
final int blendedColor = getTextColor();
+ mTitle.setTextColor(blendedColor);
int childCount = mRow.getChildCount();
for (int i = 0; i < childCount; i++) {
View v = mRow.getChildAt(i);
@@ -294,7 +317,10 @@
setupUri(newValue);
}
- private void setupUri(String uriString) {
+ /**
+ * Sets the slice provider Uri.
+ */
+ public void setupUri(String uriString) {
if (uriString == null) {
uriString = KeyguardSliceProvider.KEYGUARD_SLICE_URI;
}
@@ -512,29 +538,4 @@
}
}
}
-
- private class SliceViewTransitionListener implements LayoutTransition.TransitionListener {
- @Override
- public void startTransition(LayoutTransition transition, ViewGroup container, View view,
- int transitionType) {
- switch (transitionType) {
- case LayoutTransition.APPEARING:
- int translation = getResources().getDimensionPixelSize(
- R.dimen.pulsing_notification_appear_translation);
- view.setTranslationY(translation);
- view.animate()
- .translationY(0)
- .setDuration(DEFAULT_ANIM_DURATION)
- .setInterpolator(Interpolators.ALPHA_IN)
- .start();
- break;
- }
- }
-
- @Override
- public void endTransition(LayoutTransition transition, ViewGroup container, View view,
- int transitionType) {
-
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index bb549ad..b0670fd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -16,14 +16,11 @@
package com.android.keyguard;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
-import android.graphics.Paint;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
@@ -37,15 +34,12 @@
import android.util.TypedValue;
import android.view.View;
import android.widget.GridLayout;
-import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.core.graphics.ColorUtils;
import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.ViewClippingUtil;
import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.google.android.collect.Sets;
@@ -54,14 +48,13 @@
import java.util.TimeZone;
public class KeyguardStatusView extends GridLayout implements
- ConfigurationController.ConfigurationListener, View.OnLayoutChangeListener {
+ ConfigurationController.ConfigurationListener {
private static final boolean DEBUG = KeyguardConstants.DEBUG;
private static final String TAG = "KeyguardStatusView";
private static final int MARQUEE_DELAY_MS = 2000;
private final LockPatternUtils mLockPatternUtils;
private final IActivityManager mIActivityManager;
- private final float mSmallClockScale;
private TextView mLogoutView;
private KeyguardClockSwitch mClockView;
@@ -74,8 +67,6 @@
private boolean mPulsing;
private float mDarkAmount = 0;
private int mTextColor;
- private int mLastLayoutHeight;
- private int mSmallClockPadding;
private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
@@ -135,8 +126,6 @@
mIActivityManager = ActivityManager.getService();
mLockPatternUtils = new LockPatternUtils(getContext());
mHandler = new Handler(Looper.myLooper());
- mSmallClockScale = getResources().getDimension(R.dimen.widget_small_font_size)
- / getResources().getDimension(R.dimen.widget_big_font_size);
onDensityOrFontScaleChanged();
}
@@ -189,9 +178,6 @@
mVisibleInDoze = Sets.newArraySet(mClockView, mKeyguardSlice);
mTextColor = mClockView.getCurrentTextColor();
- int clockStroke = getResources().getDimensionPixelSize(R.dimen.widget_small_font_stroke);
- mClockView.getPaint().setStrokeWidth(clockStroke);
- mClockView.addOnLayoutChangeListener(this);
mKeyguardSlice.setContentChangeListener(this::onSliceContentChanged);
onSliceContentChanged();
@@ -207,72 +193,20 @@
* Moves clock, adjusting margins when slice content changes.
*/
private void onSliceContentChanged() {
- LinearLayout.LayoutParams layoutParams =
- (LinearLayout.LayoutParams) mClockView.getLayoutParams();
- layoutParams.bottomMargin = mKeyguardSlice.hasHeader() ? mSmallClockPadding : 0;
- mClockView.setLayoutParams(layoutParams);
- }
-
- /**
- * Animate clock when necessary.
- */
- @Override
- public void onLayoutChange(View view, int left, int top, int right, int bottom,
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
- boolean smallClock = mKeyguardSlice.hasHeader();
- int heightOffset = smallClock ? 0 : getHeight() - mLastLayoutHeight;
- long duration = KeyguardSliceView.DEFAULT_ANIM_DURATION;
- long delay = smallClock ? 0 : duration / 4;
-
- boolean shouldAnimate = mKeyguardSlice.getLayoutTransition() != null
- && mKeyguardSlice.getLayoutTransition().isRunning();
- if (view == mClockView) {
- float clockScale = smallClock ? mSmallClockScale : 1;
- Paint.Style style = smallClock ? Paint.Style.FILL_AND_STROKE : Paint.Style.FILL;
- mClockView.animate().cancel();
- if (shouldAnimate) {
- mClockView.setY(oldTop + heightOffset);
- mClockView.animate()
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .setDuration(duration)
- .setListener(new ClipChildrenAnimationListener())
- .setStartDelay(delay)
- .y(top)
- .scaleX(clockScale)
- .scaleY(clockScale)
- .withEndAction(() -> {
- mClockView.setStyle(style);
- mClockView.invalidate();
- })
- .start();
- } else {
- mClockView.setY(top);
- mClockView.setScaleX(clockScale);
- mClockView.setScaleY(clockScale);
- mClockView.setStyle(style);
- mClockView.invalidate();
- }
- }
+ mClockView.setKeyguardShowingHeader(mKeyguardSlice.hasHeader());
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- mClockView.setPivotX(mClockView.getWidth() / 2);
- mClockView.setPivotY(0);
- mLastLayoutHeight = getHeight();
layoutOwnerInfo();
}
@Override
public void onDensityOrFontScaleChanged() {
- mSmallClockPadding = getResources()
- .getDimensionPixelSize(R.dimen.widget_small_clock_padding);
if (mClockView != null) {
mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
getResources().getDimensionPixelSize(R.dimen.widget_big_font_size));
- mClockView.getPaint().setStrokeWidth(
- getResources().getDimensionPixelSize(R.dimen.widget_small_font_stroke));
}
if (mOwnerInfo != null) {
mOwnerInfo.setTextSize(TypedValue.COMPLEX_UNIT_PX,
@@ -461,24 +395,4 @@
Log.e(TAG, "Failed to logout user", re);
}
}
-
- private class ClipChildrenAnimationListener extends AnimatorListenerAdapter implements
- ViewClippingUtil.ClippingParameters {
-
- ClipChildrenAnimationListener() {
- ViewClippingUtil.setClippingDeactivated(mClockView, true /* deactivated */,
- this /* clippingParams */);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- ViewClippingUtil.setClippingDeactivated(mClockView, false /* deactivated */,
- this /* clippingParams */);
- }
-
- @Override
- public boolean shouldFinish(View view) {
- return view == getParent();
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index 9598142..078108d 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -15,6 +15,7 @@
*/
package com.android.keyguard.clock;
+import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
@@ -25,16 +26,18 @@
import android.provider.Settings;
import android.view.LayoutInflater;
+import androidx.annotation.VisibleForTesting;
+
import com.android.keyguard.R;
+import com.android.systemui.dock.DockManager;
+import com.android.systemui.dock.DockManager.DockEventListener;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.ExtensionController.Extension;
import java.util.ArrayList;
import java.util.List;
-import java.util.Objects;
import java.util.function.Consumer;
-import java.util.function.Supplier;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -45,7 +48,6 @@
@Singleton
public final class ClockManager {
- private final LayoutInflater mLayoutInflater;
private final ContentResolver mContentResolver;
private final List<ClockInfo> mClockInfos = new ArrayList<>();
@@ -62,7 +64,6 @@
}
}
};
-
private final ExtensionController mExtensionController;
/**
* Used to select between plugin or default implementations of ClockPlugin interface.
@@ -72,13 +73,35 @@
* Consumer that accepts the a new ClockPlugin implementation when the Extension reloads.
*/
private final Consumer<ClockPlugin> mClockPluginConsumer = this::setClockPlugin;
+ /**
+ * Supplier of default ClockPlugin implementation.
+ */
+ private final DefaultClockSupplier mDefaultClockSupplier;
+ /**
+ * Observe changes to dock state to know when to switch the clock face.
+ */
+ private final DockEventListener mDockEventListener =
+ new DockEventListener() {
+ @Override
+ public void onEvent(int event) {
+ final boolean isDocked = (event == DockManager.STATE_DOCKED
+ || event == DockManager.STATE_DOCKED_HIDE);
+ mDefaultClockSupplier.setDocked(isDocked);
+ if (mClockExtension != null) {
+ mClockExtension.reload();
+ }
+ }
+ };
+ @Nullable
+ private final DockManager mDockManager;
private final List<ClockChangedListener> mListeners = new ArrayList<>();
@Inject
- public ClockManager(Context context, ExtensionController extensionController) {
+ public ClockManager(Context context, ExtensionController extensionController,
+ @Nullable DockManager dockManager) {
mExtensionController = extensionController;
- mLayoutInflater = LayoutInflater.from(context);
+ mDockManager = dockManager;
mContentResolver = context.getContentResolver();
Resources res = context.getResources();
@@ -110,6 +133,9 @@
.setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.type_thumbnail))
.setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.type_preview))
.build());
+
+ mDefaultClockSupplier = new DefaultClockSupplier(new SettingsWrapper(mContentResolver),
+ LayoutInflater.from(context));
}
/**
@@ -154,41 +180,32 @@
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE),
false, mContentObserver);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.DOCKED_CLOCK_FACE),
+ false, mContentObserver);
+ if (mDockManager != null) {
+ mDockManager.addListener(mDockEventListener);
+ }
mClockExtension = mExtensionController.newExtension(ClockPlugin.class)
.withPlugin(ClockPlugin.class)
.withCallback(mClockPluginConsumer)
- // Using withDefault even though this isn't the default as a workaround.
- // ExtensionBuilder doesn't provide the ability to supply a ClockPlugin
- // instance based off of the value of a setting. Since multiple "default"
- // can be provided, using a supplier that changes the settings value.
- // A null return will cause Extension#reload to look at the next "default"
- // supplier.
- .withDefault(
- new SettingsGattedSupplier(
- mContentResolver,
- Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
- BubbleClockController.class.getName(),
- () -> BubbleClockController.build(mLayoutInflater)))
- .withDefault(
- new SettingsGattedSupplier(
- mContentResolver,
- Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
- StretchAnalogClockController.class.getName(),
- () -> StretchAnalogClockController.build(mLayoutInflater)))
- .withDefault(
- new SettingsGattedSupplier(
- mContentResolver,
- Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
- TypeClockController.class.getName(),
- () -> TypeClockController.build(mLayoutInflater)))
+ .withDefault(mDefaultClockSupplier)
.build();
}
private void unregister() {
mContentResolver.unregisterContentObserver(mContentObserver);
+ if (mDockManager != null) {
+ mDockManager.removeListener(mDockEventListener);
+ }
mClockExtension.destroy();
}
+ @VisibleForTesting
+ boolean isDocked() {
+ return mDefaultClockSupplier.isDocked();
+ }
+
/**
* Listener for events that should cause the custom clock face to change.
*/
@@ -200,44 +217,4 @@
*/
void onClockChanged(ClockPlugin clock);
}
-
- /**
- * Supplier that only gets an instance when a settings value matches expected value.
- */
- private static class SettingsGattedSupplier implements Supplier<ClockPlugin> {
-
- private final ContentResolver mContentResolver;
- private final String mKey;
- private final String mValue;
- private final Supplier<ClockPlugin> mSupplier;
-
- /**
- * Constructs a supplier that changes secure setting key against value.
- *
- * @param contentResolver Used to look up settings value.
- * @param key Settings key.
- * @param value If the setting matches this values that get supplies a ClockPlugin
- * instance.
- * @param supplier Supplier of ClockPlugin instance, only used if the setting
- * matches value.
- */
- SettingsGattedSupplier(ContentResolver contentResolver, String key, String value,
- Supplier<ClockPlugin> supplier) {
- mContentResolver = contentResolver;
- mKey = key;
- mValue = value;
- mSupplier = supplier;
- }
-
- /**
- * Returns null if the settings value doesn't match the expected value.
- *
- * A null return causes Extension#reload to skip this supplier and move to the next.
- */
- @Override
- public ClockPlugin get() {
- final String currentValue = Settings.Secure.getString(mContentResolver, mKey);
- return Objects.equals(currentValue, mValue) ? mSupplier.get() : null;
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockSupplier.java b/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockSupplier.java
new file mode 100644
index 0000000..7fdd235
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockSupplier.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard.clock;
+
+import android.util.ArrayMap;
+import android.view.LayoutInflater;
+
+import com.android.systemui.plugins.ClockPlugin;
+
+import java.util.Map;
+import java.util.function.Supplier;
+
+/**
+ * Supplier that only gets an instance when a settings value matches expected value.
+ */
+public class DefaultClockSupplier implements Supplier<ClockPlugin> {
+
+ private final SettingsWrapper mSettingsWrapper;
+ /**
+ * Map from expected value stored in settings to supplier of custom clock face.
+ */
+ private final Map<String, Supplier<ClockPlugin>> mClocks = new ArrayMap<>();
+ /**
+ * When docked, the DOCKED_CLOCK_FACE setting will be checked for the custom clock face
+ * to show.
+ */
+ private boolean mIsDocked;
+
+ /**
+ * Constructs a supplier that changes secure setting key against value.
+ *
+ * @param settingsWrapper Wrapper around settings used to look up the custom clock face.
+ * @param layoutInflater Provided to clocks as dependency to inflate clock views.
+ */
+ public DefaultClockSupplier(SettingsWrapper settingsWrapper, LayoutInflater layoutInflater) {
+ mSettingsWrapper = settingsWrapper;
+
+ mClocks.put(BubbleClockController.class.getName(),
+ () -> BubbleClockController.build(layoutInflater));
+ mClocks.put(StretchAnalogClockController.class.getName(),
+ () -> StretchAnalogClockController.build(layoutInflater));
+ mClocks.put(TypeClockController.class.getName(),
+ () -> TypeClockController.build(layoutInflater));
+ }
+
+ /**
+ * Sets the dock state.
+ *
+ * @param isDocked True when docked, false otherwise.
+ */
+ public void setDocked(boolean isDocked) {
+ mIsDocked = isDocked;
+ }
+
+ boolean isDocked() {
+ return mIsDocked;
+ }
+
+ /**
+ * Get the custom clock face based on values in settings.
+ *
+ * @return Custom clock face, null if the settings value doesn't match a custom clock.
+ */
+ @Override
+ public ClockPlugin get() {
+ ClockPlugin plugin = null;
+ if (mIsDocked) {
+ final String name = mSettingsWrapper.getDockedClockFace();
+ if (name != null) {
+ Supplier<ClockPlugin> supplier = mClocks.get(name);
+ if (supplier != null) {
+ plugin = supplier.get();
+ if (plugin != null) {
+ return plugin;
+ }
+ }
+ }
+ }
+ final String name = mSettingsWrapper.getLockScreenCustomClockFace();
+ if (name != null) {
+ Supplier<ClockPlugin> supplier = mClocks.get(name);
+ if (supplier != null) {
+ plugin = supplier.get();
+ }
+ }
+ return plugin;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java b/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java
new file mode 100644
index 0000000..58e1155
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard.clock;
+
+import android.content.ContentResolver;
+import android.provider.Settings;
+
+/**
+ * Wrapper around Settings used for testing.
+ */
+public class SettingsWrapper {
+
+ private static final String CUSTOM_CLOCK_FACE = Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE;
+ private static final String DOCKED_CLOCK_FACE = Settings.Secure.DOCKED_CLOCK_FACE;
+
+ private ContentResolver mContentResolver;
+
+ public SettingsWrapper(ContentResolver contentResolver) {
+ mContentResolver = contentResolver;
+ }
+
+ /**
+ * Gets the value stored in settings for the custom clock face.
+ */
+ public String getLockScreenCustomClockFace() {
+ return Settings.Secure.getString(mContentResolver, CUSTOM_CLOCK_FACE);
+ }
+
+ /**
+ * Gets the value stored in settings for the clock face to use when docked.
+ */
+ public String getDockedClockFace() {
+ return Settings.Secure.getString(mContentResolver, DOCKED_CLOCK_FACE);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
index ce9c637..3c6f081 100644
--- a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
@@ -21,11 +21,13 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.VolumeDialogController;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.PowerNotificationWarnings;
import com.android.systemui.power.PowerUI;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
@@ -194,6 +196,12 @@
/**
*/
@Binds
+ public abstract StatusBarStateController provideStatusBarStateController(
+ StatusBarStateControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
public abstract StatusBarIconController provideStatusBarIconController(
StatusBarIconControllerImpl controllerImpl);
diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
index e28aa9d..2a1d066 100644
--- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
@@ -23,7 +23,6 @@
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
-import android.content.res.Configuration;
import android.provider.Settings;
import android.util.AttributeSet;
import android.view.Gravity;
@@ -59,7 +58,6 @@
private int mEndPoint;
private boolean mEdgeBleed;
private boolean mRoundedDivider;
- private int mRotation = ROTATION_NONE;
private boolean mRotatedBackground;
private boolean mSwapOrientation = true;
@@ -89,7 +87,7 @@
}
@Override
- public ViewGroup getParentView(boolean separated, int index) {
+ public ViewGroup getParentView(boolean separated, int index, boolean reverse) {
if (separated) {
return getSeparatedView();
} else {
@@ -174,7 +172,6 @@
mSeparatedView.setBackground(mSeparatedViewBackground);
updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding());
mOldHeight = mList.getMeasuredHeight();
- updateRotation();
} else {
return;
}
@@ -188,25 +185,13 @@
post(() -> updatePosition());
}
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- updateRotation();
- }
-
public void setSwapOrientation(boolean swapOrientation) {
mSwapOrientation = swapOrientation;
}
- private void updateRotation() {
- int rotation = RotationUtils.getRotation(getContext());
- if (rotation != mRotation) {
- rotate(mRotation, rotation);
- mRotation = rotation;
- }
- }
-
- private void rotate(int from, int to) {
+ @Override
+ protected void rotate(int from, int to) {
+ super.rotate(from, to);
if (from != ROTATION_NONE && to != ROTATION_NONE) {
// Rather than handling this confusing case, just do 2 rotations.
rotate(from, ROTATION_NONE);
diff --git a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
index 85265f4..00ff518 100644
--- a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
@@ -17,11 +17,14 @@
package com.android.systemui;
import android.content.Context;
+import android.content.res.Configuration;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
+import com.android.systemui.util.leak.RotationUtils;
+
/**
* Layout class representing the Global Actions menu which appears when the power button is held.
*/
@@ -32,8 +35,12 @@
protected int mExpectedSeparatedItemCount;
protected int mExpectedListItemCount;
+ protected int mRotation;
+ protected RotationListener mRotationListener;
+
public MultiListLayout(Context context, AttributeSet attrs) {
super(context, attrs);
+ mRotation = RotationUtils.getRotation(context);
}
protected abstract ViewGroup getSeparatedView();
@@ -50,10 +57,12 @@
* @param separated Whether or not this index refers to a position in the separated or list
* container.
* @param index The index of the item within the container.
+ * @param reverse If the MultiListLayout contains sub-lists within the list container, reverse
+ * the order that they are filled.
* @return The parent ViewGroup which will be used to contain the specified item
* after it has been added to the layout.
*/
- public abstract ViewGroup getParentView(boolean separated, int index);
+ public abstract ViewGroup getParentView(boolean separated, int index, boolean reverse);
/**
* Sets the divided view, which may have a differently-colored background.
@@ -111,6 +120,26 @@
setFocusable(true);
}
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ int newRotation = RotationUtils.getRotation(mContext);
+ if (newRotation != mRotation) {
+ rotate(mRotation, newRotation);
+ mRotation = newRotation;
+ }
+ }
+
+ protected void rotate(int from, int to) {
+ if (mRotationListener != null) {
+ mRotationListener.onRotate(from, to);
+ }
+ }
+
+ public void setRotationListener(RotationListener listener) {
+ mRotationListener = listener;
+ }
+
/**
* Retrieve the MultiListLayout associated with the given view.
*/
@@ -121,4 +150,8 @@
}
return null;
}
+
+ interface RotationListener {
+ void onRotate(int from, int to);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 755d6fc..6d583df 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -31,6 +31,7 @@
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.dock.DockManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -41,7 +42,6 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotificationData;
@@ -153,15 +153,6 @@
return new VolumeDialogComponent(systemUi, context);
}
- /**
- * Provides status bar state controller implementation
- */
- @Singleton
- @Provides
- public StatusBarStateController provideStatusBarStateController(Context context) {
- return new StatusBarStateControllerImpl();
- }
-
@Singleton
@Provides
public NotificationData.KeyguardEnvironment provideKeyguardEnvironment(Context context) {
@@ -228,6 +219,16 @@
return SysUiServiceProvider.getComponent(context, StatusBar.class);
}
+ /**
+ * Provides DockManager.
+ */
+ @Singleton
+ @Provides
+ @Nullable
+ public DockManager providesDockManager(Context context) {
+ return SysUiServiceProvider.getComponent(context, DockManager.class);
+ }
+
@Module
protected static class ContextHolder {
private Context mContext;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
new file mode 100644
index 0000000..4778434
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.bubbles;
+
+
+import android.view.LayoutInflater;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+/**
+ * Encapsulates the data and UI elements of a bubble.
+ */
+class Bubble {
+
+ public BubbleView iconView;
+ public BubbleExpandedView expandedView;
+ public String key;
+ public NotificationEntry entry;
+
+ Bubble(NotificationEntry e, LayoutInflater inflater, BubbleStackView stackView,
+ BubbleExpandedView.OnBubbleBlockedListener listener) {
+ entry = e;
+ key = entry.key;
+
+ iconView = (BubbleView) inflater.inflate(
+ R.layout.bubble_view, stackView, false /* attachToRoot */);
+ iconView.setNotif(entry);
+
+ expandedView = (BubbleExpandedView) inflater.inflate(
+ R.layout.bubble_expanded_view, stackView, false /* attachToRoot */);
+ expandedView.setEntry(entry, stackView);
+
+ expandedView.setOnBlockedListener(listener);
+ }
+
+ public void setEntry(NotificationEntry entry) {
+ key = entry.key;
+ iconView.update(entry);
+ // TODO: should also update the expanded view here (e.g. height change)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 41bc1b2..a67e1b7 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -16,10 +16,6 @@
package com.android.systemui.bubbles;
-import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
-import static android.util.StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
-import static android.util.StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
-import static android.util.StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
@@ -33,21 +29,14 @@
import android.app.IActivityTaskManager;
import android.app.INotificationManager;
import android.app.Notification;
-import android.app.PendingIntent;
import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.graphics.Point;
import android.graphics.Rect;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
-import android.util.Log;
-import android.util.StatsLog;
import android.view.Display;
-import android.view.LayoutInflater;
import android.view.ViewGroup;
-import android.view.WindowManager;
import android.widget.FrameLayout;
import androidx.annotation.MainThread;
@@ -66,10 +55,7 @@
import com.android.systemui.statusbar.notification.row.NotificationInflater;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -105,11 +91,9 @@
private final BubbleTaskStackListener mTaskStackListener;
private BubbleStateChangeListener mStateChangeListener;
private BubbleExpandListener mExpandListener;
- private LayoutInflater mInflater;
- private final Map<String, BubbleView> mBubbles = new HashMap<>();
+ private BubbleData mBubbleData;
private BubbleStackView mStackView;
- private final Point mDisplaySize;
// Bubbles get added to the status bar view
private final StatusBarWindowController mStatusBarWindowController;
@@ -168,10 +152,6 @@
@Inject
public BubbleController(Context context, StatusBarWindowController statusBarWindowController) {
mContext = context;
- WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- mDisplaySize = new Point();
- wm.getDefaultDisplay().getSize(mDisplaySize);
- mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
@@ -190,6 +170,8 @@
mActivityTaskManager = ActivityTaskManager.getService();
mTaskStackListener = new BubbleTaskStackListener();
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+
+ mBubbleData = BubbleData.getInstance();
}
/**
@@ -219,8 +201,11 @@
* screen (e.g. if on AOD).
*/
public boolean hasBubbles() {
- for (BubbleView bv : mBubbles.values()) {
- if (!bv.getEntry().isBubbleDismissed()) {
+ if (mStackView == null) {
+ return false;
+ }
+ for (Bubble bubble : mBubbleData.getBubbles()) {
+ if (!bubble.entry.isBubbleDismissed()) {
return true;
}
}
@@ -250,10 +235,6 @@
if (mStackView == null) {
return;
}
- Set<String> keys = mBubbles.keySet();
- for (String key: keys) {
- mBubbles.get(key).getEntry().setBubbleDismissed(true);
- }
mStackView.stackDismissed();
updateVisibility();
@@ -267,10 +248,9 @@
* @param updatePosition whether this update should promote the bubble to the top of the stack.
*/
public void updateBubble(NotificationEntry notif, boolean updatePosition) {
- if (mBubbles.containsKey(notif.key)) {
+ if (mStackView != null && mBubbleData.getBubble(notif.key) != null) {
// It's an update
- BubbleView bubble = mBubbles.get(notif.key);
- mStackView.updateBubble(bubble, notif, updatePosition);
+ mStackView.updateBubble(notif, updatePosition);
} else {
if (mStackView == null) {
mStackView = new BubbleStackView(mContext);
@@ -286,15 +266,7 @@
mStackView.setOnBlockedListener(this);
}
// It's new
- BubbleView bubble = (BubbleView) mInflater.inflate(
- R.layout.bubble_view, mStackView, false /* attachToRoot */);
- bubble.setNotif(notif);
- PendingIntent bubbleIntent = getValidBubbleIntent(notif);
- if (bubbleIntent != null) {
- bubble.setBubbleIntent(bubbleIntent);
- }
- mBubbles.put(bubble.getKey(), bubble);
- mStackView.addBubble(bubble);
+ mStackView.addBubble(notif);
}
updateVisibility();
}
@@ -306,25 +278,18 @@
*/
@MainThread
void removeBubble(String key) {
- BubbleView bv = mBubbles.remove(key);
- if (mStackView != null && bv != null) {
- mStackView.removeBubble(bv);
- bv.destroyActivityView(mStackView);
+ if (mStackView != null) {
+ mStackView.removeBubble(key);
}
-
- NotificationEntry entry = bv != null ? bv.getEntry() : null;
- if (entry != null) {
- entry.setBubbleDismissed(true);
- mNotificationEntryManager.updateNotifications();
- }
+ mNotificationEntryManager.updateNotifications();
updateVisibility();
}
@Override
public void onBubbleBlocked(NotificationEntry entry) {
- Object[] bubbles = mBubbles.values().toArray();
+ Object[] bubbles = mBubbleData.getBubbles().toArray();
for (int i = 0; i < bubbles.length; i++) {
- NotificationEntry e = ((BubbleView) bubbles[i]).getEntry();
+ NotificationEntry e = ((Bubble) bubbles[i]).entry;
boolean samePackage = entry.notification.getPackageName().equals(
e.notification.getPackageName());
if (samePackage) {
@@ -368,10 +333,8 @@
&& alertAgain(entry, entry.notification.getNotification())) {
entry.setShowInShadeWhenBubble(true);
entry.setBubbleDismissed(false); // updates come back as bubbles even if dismissed
- if (mBubbles.containsKey(entry.key)) {
- mBubbles.get(entry.key).updateDotVisibility();
- }
updateBubble(entry, true /* updatePosition */);
+ mStackView.updateDotVisibility(entry.key);
}
}
@@ -383,8 +346,8 @@
return;
}
entry.setShowInShadeWhenBubble(false);
- if (mBubbles.containsKey(entry.key)) {
- mBubbles.get(entry.key).updateDotVisibility();
+ if (mStackView != null) {
+ mStackView.updateDotVisibility(entry.key);
}
if (!removedByUser) {
// This was a cancel so we should remove the bubble
@@ -441,65 +404,6 @@
return mStackView;
}
- @Nullable
- private PendingIntent getValidBubbleIntent(NotificationEntry notif) {
- Notification notification = notif.notification.getNotification();
- String packageName = notif.notification.getPackageName();
- Notification.BubbleMetadata data = notif.getBubbleMetadata();
- if (data != null && canLaunchInActivityView(data.getIntent(),
- true /* enable logging for bubbles */, packageName)) {
- return data.getIntent();
- }
- if (shouldUseContentIntent(mContext)
- && canLaunchInActivityView(notification.contentIntent,
- false /* disable logging for notifications */, packageName)) {
- Log.d(TAG, "[addBubble " + notif.key
- + "]: No appOverlayIntent, using contentIntent.");
- return notification.contentIntent;
- }
- Log.d(TAG, "[addBubble " + notif.key + "]: No supported intent for ActivityView.");
- return null;
- }
-
- /**
- * Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
- *
- * @param intent the pending intent of the bubble.
- * @param enableLogging whether bubble developer error should be logged.
- * @param packageName the notification package name for this bubble.
- * @return
- */
- private boolean canLaunchInActivityView(PendingIntent intent, boolean enableLogging,
- String packageName) {
- if (intent == null) {
- return false;
- }
- ActivityInfo info =
- intent.getIntent().resolveActivityInfo(mContext.getPackageManager(), 0);
- if (info == null) {
- if (enableLogging) {
- StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
- BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING);
- }
- return false;
- }
- if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
- if (enableLogging) {
- StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
- BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE);
- }
- return false;
- }
- if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) {
- if (enableLogging) {
- StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
- BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS);
- }
- return false;
- }
- return (info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0;
- }
-
/**
* Whether the notification has been developer configured to bubble and is allowed by the user.
*/
@@ -620,7 +524,7 @@
ENABLE_AUTO_BUBBLE_ALL, 0) != 0;
}
- private static boolean shouldUseContentIntent(Context context) {
+ static boolean shouldUseContentIntent(Context context) {
return Settings.Secure.getInt(context.getContentResolver(),
ENABLE_BUBBLE_CONTENT_INTENT, 0) != 0;
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
new file mode 100644
index 0000000..89b0de8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.bubbles;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import java.util.Collection;
+import java.util.HashMap;
+
+/**
+ * Keeps track of active bubbles.
+ */
+class BubbleData {
+
+ private HashMap<String, Bubble> mBubbles = new HashMap<>();
+
+ private static BubbleData sBubbleData = null;
+
+ private BubbleData() {}
+
+ public static BubbleData getInstance() {
+ if (sBubbleData == null) {
+ sBubbleData = new BubbleData();
+ }
+ return sBubbleData;
+ }
+
+ /**
+ * The set of bubbles.
+ */
+ public Collection<Bubble> getBubbles() {
+ return mBubbles.values();
+ }
+
+ @Nullable
+ public Bubble getBubble(String key) {
+ return mBubbles.get(key);
+ }
+
+ public void addBubble(Bubble b) {
+ mBubbles.put(b.key, b);
+ }
+
+ @Nullable
+ public Bubble removeBubble(String key) {
+ return mBubbles.remove(key);
+ }
+
+ public void updateBubble(String key, NotificationEntry newEntry) {
+ Bubble oldBubble = mBubbles.get(key);
+ if (oldBubble != null) {
+ oldBubble.setEntry(newEntry);
+ }
+ }
+
+ public void clear() {
+ mBubbles.clear();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 976a766..3389c46 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -16,19 +16,28 @@
package com.android.systemui.bubbles;
+import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
+import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
+import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
+import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS;
+
import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
import android.annotation.Nullable;
+import android.app.ActivityView;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
+import android.graphics.Insets;
+import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.os.RemoteException;
@@ -39,16 +48,21 @@
import android.util.Log;
import android.util.StatsLog;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowInsets;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.recents.TriangleShape;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
/**
* Container for the expanded bubble view, handles rendering the caret and header of the view.
@@ -68,8 +82,15 @@
// Permission view
private View mPermissionView;
- // The view that is being displayed for the expanded state
- private View mExpandedView;
+ // Views for expanded state
+ private ExpandableNotificationRow mNotifRow;
+ private ActivityView mActivityView;
+
+ private boolean mActivityViewReady = false;
+ private PendingIntent mBubbleIntent;
+
+ private int mBubbleHeight;
+ private int mDefaultHeight;
private NotificationEntry mEntry;
private PackageManager mPm;
@@ -77,11 +98,38 @@
private Drawable mAppIcon;
private INotificationManager mNotificationManagerService;
+ private BubbleController mBubbleController = Dependency.get(BubbleController.class);
- // Need reference to let it know to collapse when new task is launched
private BubbleStackView mStackView;
- private OnBubbleBlockedListener mOnBubbleBlockedListener;
+ private BubbleExpandedView.OnBubbleBlockedListener mOnBubbleBlockedListener;
+
+ private ActivityView.StateCallback mStateCallback = new ActivityView.StateCallback() {
+ @Override
+ public void onActivityViewReady(ActivityView view) {
+ mActivityViewReady = true;
+ mActivityView.startActivity(mBubbleIntent);
+ }
+
+ @Override
+ public void onActivityViewDestroyed(ActivityView view) {
+ mActivityViewReady = false;
+ }
+
+ /**
+ * This is only called for tasks on this ActivityView, which is also set to
+ * single-task mode -- meaning never more than one task on this display. If a task
+ * is being removed, it's the top Activity finishing and this bubble should
+ * be removed or collapsed.
+ */
+ @Override
+ public void onTaskRemovalStarted(int taskId) {
+ if (mEntry != null) {
+ // Must post because this is called from a binder thread.
+ post(() -> mBubbleController.removeBubble(mEntry.key));
+ }
+ }
+ };
public BubbleExpandedView(Context context) {
this(context, null);
@@ -99,6 +147,8 @@
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mPm = context.getPackageManager();
+ mDefaultHeight = getResources().getDimensionPixelSize(
+ R.dimen.bubble_expanded_default_height);
try {
mNotificationManagerService = INotificationManager.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.NOTIFICATION_SERVICE));
@@ -152,6 +202,28 @@
mPermissionView = findViewById(R.id.permission_layout);
findViewById(R.id.no_bubbles_button).setOnClickListener(this);
findViewById(R.id.yes_bubbles_button).setOnClickListener(this);
+
+ mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
+ true /* singleTaskInstance */);
+ addView(mActivityView);
+
+ mActivityView.setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
+ ActivityView activityView = (ActivityView) view;
+ // Here we assume that the position of the ActivityView on the screen
+ // remains regardless of IME status. When we move ActivityView, the
+ // forwardedInsets should be computed not against the current location
+ // and size, but against the post-moved location and size.
+ Point displaySize = new Point();
+ view.getContext().getDisplay().getSize(displaySize);
+ int[] windowLocation = view.getLocationOnScreen();
+ final int windowBottom = windowLocation[1] + view.getHeight();
+ final int keyboardHeight = insets.getSystemWindowInsetBottom()
+ - insets.getStableInsetBottom();
+ final int insetsBottom = Math.max(0,
+ windowBottom + keyboardHeight - displaySize.y);
+ activityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
+ return view.onApplyWindowInsets(insets);
+ });
}
/**
@@ -189,6 +261,8 @@
}
updateHeaderView();
updatePermissionView();
+ updateExpandedView();
+ mActivityView.setCallback(mStateCallback);
}
private void updateHeaderView() {
@@ -225,6 +299,34 @@
}
}
+ private void updateExpandedView() {
+ mBubbleIntent = getBubbleIntent(mEntry);
+ if (mBubbleIntent != null) {
+ if (mNotifRow != null) {
+ // Clear out the row if we had it previously
+ removeView(mNotifRow);
+ mNotifRow = null;
+ }
+ Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
+ mBubbleHeight = data != null && data.getDesiredHeight() > 0
+ ? data.getDesiredHeight()
+ : mDefaultHeight;
+ // XXX: enforce max / min height
+ LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams();
+ lp.height = mBubbleHeight;
+ mActivityView.setLayoutParams(lp);
+ mActivityView.setVisibility(VISIBLE);
+ } else {
+ // Hide activity view if we had it previously
+ mActivityView.setVisibility(GONE);
+
+ // Use notification view
+ mNotifRow = mEntry.getRow();
+ addView(mNotifRow);
+ }
+ updateView();
+ }
+
@Override
public void onClick(View view) {
if (mEntry == null) {
@@ -279,36 +381,87 @@
}
/**
+ * Update appearance of the expanded view being displayed.
+ */
+ public void updateView() {
+ if (usingActivityView()
+ && mActivityView.getVisibility() == VISIBLE
+ && mActivityView.isAttachedToWindow()) {
+ mActivityView.onLocationChanged();
+ } else if (mNotifRow != null) {
+ applyRowState(mNotifRow);
+ }
+ }
+
+ /**
* Set the x position that the tip of the triangle should point to.
*/
- public void setPointerPosition(int x) {
+ public void setPointerPosition(float x) {
// Adjust for the pointer size
- x -= (mPointerView.getWidth() / 2);
+ x -= (mPointerView.getWidth() / 2f);
mPointerView.setTranslationX(x);
}
/**
- * Set the view to display for the expanded state. Passing null will clear the view.
+ * Removes and releases an ActivityView if one was previously created for this bubble.
*/
- public void setExpandedView(View view) {
- if (mExpandedView == view) {
+ public void destroyActivityView(ViewGroup tmpParent) {
+ if (mActivityView == null) {
return;
}
- if (mExpandedView != null) {
- removeView(mExpandedView);
+ if (!mActivityViewReady) {
+ // release not needed, never initialized?
+ mActivityView = null;
+ return;
}
- mExpandedView = view;
- if (mExpandedView != null) {
- addView(mExpandedView);
+ // HACK: release() will crash if the view is not attached.
+ if (!isAttachedToWindow()) {
+ mActivityView.setVisibility(View.GONE);
+ tmpParent.addView(this);
}
+
+ mActivityView.release();
+ ((ViewGroup) getParent()).removeView(this);
+ mActivityView = null;
+ mActivityViewReady = false;
}
- /**
- * @return the view containing the expanded content, can be null.
- */
- @Nullable
- public View getExpandedView() {
- return mExpandedView;
+ private boolean usingActivityView() {
+ return mBubbleIntent != null;
+ }
+
+ private void applyRowState(ExpandableNotificationRow view) {
+ view.reset();
+ view.setHeadsUp(false);
+ view.resetTranslation();
+ view.setOnKeyguard(false);
+ view.setOnAmbient(false);
+ view.setClipBottomAmount(0);
+ view.setClipTopAmount(0);
+ view.setContentTransformationAmount(0, false);
+ view.setIconsVisible(true);
+
+ // TODO - Need to reset this (and others) when view goes back in shade, leave for now
+ // view.setTopRoundness(1, false);
+ // view.setBottomRoundness(1, false);
+
+ ExpandableViewState viewState = view.getViewState();
+ viewState = viewState == null ? new ExpandableViewState() : viewState;
+ viewState.height = view.getIntrinsicHeight();
+ viewState.gone = false;
+ viewState.hidden = false;
+ viewState.dimmed = false;
+ viewState.dark = false;
+ viewState.alpha = 1f;
+ viewState.notGoneIndex = -1;
+ viewState.xTranslation = 0;
+ viewState.yTranslation = 0;
+ viewState.zTranslation = 0;
+ viewState.scaleX = 1;
+ viewState.scaleY = 1;
+ viewState.inShelf = true;
+ viewState.headsUpIsVisible = false;
+ viewState.applyToView(view);
}
private Intent getSettingsIntent(String packageName, final int appUid) {
@@ -320,6 +473,61 @@
return intent;
}
+ @Nullable
+ private PendingIntent getBubbleIntent(NotificationEntry entry) {
+ Notification notif = entry.notification.getNotification();
+ String packageName = entry.notification.getPackageName();
+ Notification.BubbleMetadata data = notif.getBubbleMetadata();
+ if (data != null && canLaunchInActivityView(data.getIntent(), true /* enableLogging */,
+ packageName)) {
+ return data.getIntent();
+ } else if (BubbleController.shouldUseContentIntent(mContext)
+ && canLaunchInActivityView(notif.contentIntent, false /* enableLogging */,
+ packageName)) {
+ return notif.contentIntent;
+ }
+ return null;
+ }
+
+ /**
+ * Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
+ *
+ * @param intent the pending intent of the bubble.
+ * @param enableLogging whether bubble developer error should be logged.
+ * @param packageName the notification package name for this bubble.
+ * @return
+ */
+ private boolean canLaunchInActivityView(PendingIntent intent, boolean enableLogging,
+ String packageName) {
+ if (intent == null) {
+ return false;
+ }
+ ActivityInfo info =
+ intent.getIntent().resolveActivityInfo(mContext.getPackageManager(), 0);
+ if (info == null) {
+ if (enableLogging) {
+ StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
+ BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING);
+ }
+ return false;
+ }
+ if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
+ if (enableLogging) {
+ StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
+ BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE);
+ }
+ return false;
+ }
+ if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) {
+ if (enableLogging) {
+ StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
+ BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS);
+ }
+ return false;
+ }
+ return (info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0;
+ }
+
/**
* Listener that is notified when a bubble is blocked.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 2b344f6..8bc83d4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -19,7 +19,6 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import android.app.ActivityView;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Outline;
@@ -33,13 +32,11 @@
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.widget.FrameLayout;
-import android.widget.LinearLayout;
import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
@@ -54,8 +51,6 @@
import com.android.systemui.bubbles.animation.PhysicsAnimationLayout;
import com.android.systemui.bubbles.animation.StackAnimationController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import java.math.BigDecimal;
import java.math.RoundingMode;
@@ -95,22 +90,27 @@
private StackAnimationController mStackAnimationController;
private ExpandedAnimationController mExpandedAnimationController;
- private BubbleExpandedView mExpandedViewContainer;
+ private FrameLayout mExpandedViewContainer;
+
+ private BubbleData mBubbleData;
private int mBubbleSize;
private int mBubblePadding;
private int mExpandedAnimateXDistance;
private int mExpandedAnimateYDistance;
+ private Bubble mExpandedBubble;
private boolean mIsExpanded;
- private int mExpandedBubbleHeight;
+
private BubbleTouchHandler mTouchHandler;
- private BubbleView mExpandedBubble;
private BubbleController.BubbleExpandListener mExpandListener;
+ private BubbleExpandedView.OnBubbleBlockedListener mBlockedListener;
private boolean mViewUpdatedRequested = false;
private boolean mIsAnimating = false;
+ private LayoutInflater mInflater;
+
// Used for determining view / touch intersection
int[] mTempLoc = new int[2];
RectF mTempRect = new RectF();
@@ -142,7 +142,9 @@
public BubbleStackView(Context context) {
super(context);
+ mBubbleData = BubbleData.getInstance();
+ mInflater = LayoutInflater.from(context);
mTouchHandler = new BubbleTouchHandler(context);
setOnTouchListener(mTouchHandler);
@@ -154,7 +156,6 @@
mExpandedAnimateYDistance =
res.getDimensionPixelSize(R.dimen.bubble_expanded_animate_y_distance);
- mExpandedBubbleHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
mDisplaySize = new Point();
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getSize(mDisplaySize);
@@ -174,9 +175,7 @@
mBubbleContainer.setClipChildren(false);
addView(mBubbleContainer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
- mExpandedViewContainer = (BubbleExpandedView)
- LayoutInflater.from(context).inflate(R.layout.bubble_expanded_view,
- this /* parent */, false /* attachToRoot */);
+ mExpandedViewContainer = new FrameLayout(context);
mExpandedViewContainer.setElevation(elevation);
mExpandedViewContainer.setPadding(padding, padding, padding, padding);
mExpandedViewContainer.setClipChildren(false);
@@ -218,6 +217,17 @@
}
/**
+ * Updates the visibility of the 'dot' indicating an update on the bubble.
+ * @param key the {@link NotificationEntry#key} associated with the bubble.
+ */
+ public void updateDotVisibility(String key) {
+ Bubble b = mBubbleData.getBubble(key);
+ if (b != null) {
+ b.iconView.updateDotVisibility();
+ }
+ }
+
+ /**
* Sets the listener to notify when the bubble stack is expanded.
*/
public void setExpandListener(BubbleController.BubbleExpandListener listener) {
@@ -228,7 +238,10 @@
* Sets the listener to notify when a bubble is blocked.
*/
public void setOnBlockedListener(BubbleExpandedView.OnBubbleBlockedListener listener) {
- mExpandedViewContainer.setOnBlockedListener(listener);
+ mBlockedListener = listener;
+ for (Bubble b : mBubbleData.getBubbles()) {
+ b.expandedView.setOnBlockedListener(mBlockedListener);
+ }
}
/**
@@ -241,19 +254,29 @@
/**
* The {@link BubbleView} that is expanded, null if one does not exist.
*/
- public BubbleView getExpandedBubble() {
+ BubbleView getExpandedBubbleView() {
+ return mExpandedBubble != null ? mExpandedBubble.iconView : null;
+ }
+
+ /**
+ * The {@link Bubble} that is expanded, null if one does not exist.
+ */
+ Bubble getExpandedBubble() {
return mExpandedBubble;
}
/**
* Sets the bubble that should be expanded and expands if needed.
+ *
+ * @param key the {@link NotificationEntry#key} associated with the bubble to expand.
*/
- public void setExpandedBubble(BubbleView bubbleToExpand) {
+ void setExpandedBubble(String key) {
+ Bubble bubbleToExpand = mBubbleData.getBubble(key);
if (mIsExpanded && !bubbleToExpand.equals(mExpandedBubble)) {
// Previously expanded, notify that this bubble is no longer expanded
- notifyExpansionChanged(mExpandedBubble, false /* expanded */);
+ notifyExpansionChanged(mExpandedBubble.entry, false /* expanded */);
}
- BubbleView prevBubble = mExpandedBubble;
+ Bubble prevBubble = mExpandedBubble;
mExpandedBubble = bubbleToExpand;
if (!mIsExpanded) {
// If we weren't previously expanded we should animate open.
@@ -268,8 +291,8 @@
logBubbleEvent(prevBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
}
- mExpandedBubble.getEntry().setShowInShadeWhenBubble(false);
- notifyExpansionChanged(mExpandedBubble, true /* expanded */);
+ mExpandedBubble.entry.setShowInShadeWhenBubble(false);
+ notifyExpansionChanged(mExpandedBubble.entry, true /* expanded */);
}
/**
@@ -280,7 +303,7 @@
for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i);
if (entry.equals(bv.getEntry())) {
- setExpandedBubble(bv);
+ setExpandedBubble(entry.key);
}
}
}
@@ -288,48 +311,67 @@
/**
* Adds a bubble to the top of the stack.
*
- * @param bubbleView the view to add to the stack.
+ * @param entry the notification to add to the stack of bubbles.
*/
- public void addBubble(BubbleView bubbleView) {
- mBubbleContainer.addView(bubbleView, 0,
+ public void addBubble(NotificationEntry entry) {
+ Bubble b = new Bubble(entry, mInflater, this /* stackView */, mBlockedListener);
+ mBubbleData.addBubble(b);
+
+ mBubbleContainer.addView(b.iconView, 0,
new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
- ViewClippingUtil.setClippingDeactivated(bubbleView, true, mClippingParameters);
+ ViewClippingUtil.setClippingDeactivated(b.iconView, true, mClippingParameters);
+
requestUpdate();
- logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
+ logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
}
/**
* Remove a bubble from the stack.
*/
- public void removeBubble(BubbleView bubbleView) {
- int removedIndex = mBubbleContainer.indexOfChild(bubbleView);
- mBubbleContainer.removeView(bubbleView);
+ public void removeBubble(String key) {
+ Bubble b = mBubbleData.removeBubble(key);
+ if (b == null) {
+ return;
+ }
+ b.entry.setBubbleDismissed(true);
+
+ // Remove it from the views
+ int removedIndex = mBubbleContainer.indexOfChild(b.iconView);
+ b.expandedView.destroyActivityView(this /* tmpParent */);
+ mBubbleContainer.removeView(b.iconView);
+
int bubbleCount = mBubbleContainer.getChildCount();
if (bubbleCount == 0) {
// If no bubbles remain, collapse the entire stack.
collapseStack();
return;
- } else if (bubbleView.equals(mExpandedBubble)) {
+ } else if (b.equals(mExpandedBubble)) {
// Was the current bubble just removed?
// If we have other bubbles and are expanded go to the next one or previous
// if the bubble removed was last
int nextIndex = bubbleCount > removedIndex ? removedIndex : bubbleCount - 1;
BubbleView expandedBubble = (BubbleView) mBubbleContainer.getChildAt(nextIndex);
if (mIsExpanded) {
- setExpandedBubble(expandedBubble);
+ setExpandedBubble(expandedBubble.getKey());
} else {
mExpandedBubble = null;
}
}
- logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
+ logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
}
/**
* Dismiss the stack of bubbles.
*/
public void stackDismissed() {
+ for (Bubble bubble : mBubbleData.getBubbles()) {
+ bubble.entry.setBubbleDismissed(true);
+ bubble.expandedView.destroyActivityView(this /* tmpParent */);
+ }
+ mBubbleData.clear();
collapseStack();
mBubbleContainer.removeAllViews();
+ mExpandedViewContainer.removeAllViews();
logBubbleEvent(null /* no bubble associated with bubble stack dismiss */,
StatsLog.BUBBLE_UICHANGED__ACTION__STACK_DISMISSED);
}
@@ -337,25 +379,26 @@
/**
* Updates a bubble in the stack.
*
- * @param bubbleView the view to update in the stack.
- * @param entry the entry to update it with.
+ * @param entry the entry to update in the stack.
* @param updatePosition whether this bubble should be moved to top of the stack.
*/
- public void updateBubble(BubbleView bubbleView, NotificationEntry entry,
- boolean updatePosition) {
- bubbleView.update(entry);
+ public void updateBubble(NotificationEntry entry, boolean updatePosition) {
+ Bubble b = mBubbleData.getBubble(entry.key);
+ b.iconView.update(entry);
+ // TODO: should also update the expanded view here (e.g. height change)
+
if (updatePosition && !mIsExpanded) {
// If alerting it gets promoted to top of the stack.
- if (mBubbleContainer.indexOfChild(bubbleView) != 0) {
- mBubbleContainer.moveViewTo(bubbleView, 0);
+ if (mBubbleContainer.indexOfChild(b.iconView) != 0) {
+ mBubbleContainer.moveViewTo(b.iconView, 0);
}
requestUpdate();
}
- if (mIsExpanded && bubbleView.equals(mExpandedBubble)) {
+ if (mIsExpanded && entry.equals(mExpandedBubble.entry)) {
entry.setShowInShadeWhenBubble(false);
requestUpdate();
}
- logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__UPDATED);
+ logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__UPDATED);
}
/**
@@ -393,7 +436,7 @@
if (mIsExpanded) {
// TODO: Save opened bubble & move it to top of stack
animateExpansion(false /* shouldExpand */);
- notifyExpansionChanged(mExpandedBubble, mIsExpanded);
+ notifyExpansionChanged(mExpandedBubble.entry, mIsExpanded);
logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
}
}
@@ -412,8 +455,8 @@
@MainThread
public void expandStack() {
if (!mIsExpanded) {
- mExpandedBubble = getTopBubble();
- setExpandedBubble(mExpandedBubble);
+ String expandedBubbleKey = getBubbleAt(0).getKey();
+ setExpandedBubble(expandedBubbleKey);
logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED);
}
}
@@ -459,7 +502,8 @@
final float yStart = Math.min(
mStackAnimationController.getStackPosition().y,
mExpandedAnimateYDistance);
- final float yDest = getStatusBarHeight() + mExpandedBubble.getHeight() + mBubblePadding;
+ final float yDest = getStatusBarHeight()
+ + mExpandedBubble.iconView.getHeight() + mBubblePadding;
if (shouldExpand) {
mExpandedViewContainer.setTranslationX(xStart);
@@ -484,17 +528,12 @@
+ mBubbleContainer.getPaddingStart();
}
- private void notifyExpansionChanged(BubbleView bubbleView, boolean expanded) {
+ private void notifyExpansionChanged(NotificationEntry entry, boolean expanded) {
if (mExpandListener != null) {
- NotificationEntry entry = bubbleView != null ? bubbleView.getEntry() : null;
mExpandListener.onBubbleExpandChanged(expanded, entry != null ? entry.key : null);
}
}
- private BubbleView getTopBubble() {
- return getBubbleAt(0);
- }
-
/** Return the BubbleView at the given index from the bubble container. */
public BubbleView getBubbleAt(int i) {
return mBubbleContainer.getChildCount() > i
@@ -665,31 +704,10 @@
}
private void updateExpandedBubble() {
- if (mExpandedBubble == null) {
- return;
- }
-
- mExpandedViewContainer.setEntry(mExpandedBubble.getEntry(), this);
- if (mExpandedBubble.hasAppOverlayIntent()) {
- // Bubble with activity view expanded state
- ActivityView expandedView = mExpandedBubble.getActivityView();
- // XXX: gets added to linear layout
- expandedView.setLayoutParams(new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, mExpandedBubbleHeight));
-
- mExpandedViewContainer.setExpandedView(expandedView);
- } else {
- // Bubble with notification view expanded state
- ExpandableNotificationRow row = mExpandedBubble.getRowView();
- if (row.getParent() != null) {
- // Row might still be in the shade when we expand
- ((ViewGroup) row.getParent()).removeView(row);
- }
- if (mIsExpanded) {
- mExpandedViewContainer.setExpandedView(row);
- } else {
- mExpandedViewContainer.setExpandedView(null);
- }
+ mExpandedViewContainer.removeAllViews();
+ if (mExpandedBubble != null && mIsExpanded) {
+ mExpandedViewContainer.addView(mExpandedBubble.expandedView);
+ mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
}
}
@@ -697,18 +715,10 @@
Log.d(TAG, "applyCurrentState: mIsExpanded=" + mIsExpanded);
mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
- if (!mIsExpanded) {
- mExpandedViewContainer.setExpandedView(null);
- } else {
- View expandedView = mExpandedViewContainer.getExpandedView();
- if (expandedView instanceof ActivityView) {
- if (expandedView.isAttachedToWindow()) {
- ((ActivityView) expandedView).onLocationChanged();
- }
- } else {
- applyRowState(mExpandedBubble.getRowView());
- }
+ if (mIsExpanded) {
+ mExpandedBubble.expandedView.updateView();
}
+
int bubbsCount = mBubbleContainer.getChildCount();
for (int i = 0; i < bubbsCount; i++) {
BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i);
@@ -730,46 +740,12 @@
private void updatePointerPosition() {
if (mExpandedBubble != null) {
- float pointerPosition = mExpandedBubble.getPosition().x
- + (mExpandedBubble.getWidth() / 2f);
- mExpandedViewContainer.setPointerPosition((int) pointerPosition);
+ float pointerPosition = mExpandedBubble.iconView.getPosition().x
+ + (mExpandedBubble.iconView.getWidth() / 2f);
+ mExpandedBubble.expandedView.setPointerPosition(pointerPosition);
}
}
- private void applyRowState(ExpandableNotificationRow view) {
- view.reset();
- view.setHeadsUp(false);
- view.resetTranslation();
- view.setOnKeyguard(false);
- view.setOnAmbient(false);
- view.setClipBottomAmount(0);
- view.setClipTopAmount(0);
- view.setContentTransformationAmount(0, false);
- view.setIconsVisible(true);
-
- // TODO - Need to reset this (and others) when view goes back in shade, leave for now
- // view.setTopRoundness(1, false);
- // view.setBottomRoundness(1, false);
-
- ExpandableViewState viewState = view.getViewState();
- viewState = viewState == null ? new ExpandableViewState() : viewState;
- viewState.height = view.getIntrinsicHeight();
- viewState.gone = false;
- viewState.hidden = false;
- viewState.dimmed = false;
- viewState.dark = false;
- viewState.alpha = 1f;
- viewState.notGoneIndex = -1;
- viewState.xTranslation = 0;
- viewState.yTranslation = 0;
- viewState.zTranslation = 0;
- viewState.scaleX = 1;
- viewState.scaleY = 1;
- viewState.inShelf = true;
- viewState.headsUpIsVisible = false;
- viewState.applyToView(view);
- }
-
/**
* @return the number of bubbles in the stack view.
*/
@@ -780,12 +756,12 @@
/**
* Finds the bubble index within the stack.
*
- * @param bubbleView the view of the bubble.
+ * @param bubble the bubble to look up.
* @return the index of the bubble view within the bubble stack. The range of the position
* is between 0 and the bubble count minus 1.
*/
- public int getBubbleIndex(BubbleView bubbleView) {
- return mBubbleContainer.indexOfChild(bubbleView);
+ int getBubbleIndex(Bubble bubble) {
+ return mBubbleContainer.indexOfChild(bubble.iconView);
}
/**
@@ -813,7 +789,7 @@
* the user interaction is not specific to one bubble.
* @param action the user interaction enum.
*/
- private void logBubbleEvent(@Nullable BubbleView bubble, int action) {
+ private void logBubbleEvent(@Nullable Bubble bubble, int action) {
if (bubble == null) {
StatsLog.write(StatsLog.BUBBLE_UI_CHANGED,
null /* package name */,
@@ -825,7 +801,7 @@
getNormalizedXPosition(),
getNormalizedYPosition());
} else {
- StatusBarNotification notification = bubble.getEntry().notification;
+ StatusBarNotification notification = bubble.entry.notification;
StatsLog.write(StatsLog.BUBBLE_UI_CHANGED,
notification.getPackageName(),
notification.getNotification().getChannelId(),
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index 22cd2fc..7d3c0f8 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -188,7 +188,7 @@
} else {
stack.onBubbleDragFinish(mBubbleDraggingOut, x, y, velX, velY);
}
- } else if (floatingView.equals(stack.getExpandedBubble())) {
+ } else if (floatingView.equals(stack.getExpandedBubbleView())) {
stack.collapseStack();
} else if (isBubbleStack) {
if (stack.isExpanded()) {
@@ -197,7 +197,7 @@
stack.expandStack();
}
} else {
- stack.setExpandedBubble((BubbleView) floatingView);
+ stack.setExpandedBubble(((BubbleView) floatingView).getKey());
}
cleanUpDismissTarget();
mVelocityTracker.recycle();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index 74ddc8f..1a4b199 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -17,13 +17,9 @@
package com.android.systemui.bubbles;
import android.annotation.Nullable;
-import android.app.ActivityView;
import android.app.Notification;
-import android.app.PendingIntent;
import android.content.Context;
import android.graphics.Color;
-import android.graphics.Insets;
-import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -31,16 +27,10 @@
import android.graphics.drawable.InsetDrawable;
import android.graphics.drawable.LayerDrawable;
import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowInsets;
import android.widget.FrameLayout;
-import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.internal.graphics.ColorUtils;
-import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -62,11 +52,6 @@
private int mIconInset;
private NotificationEntry mEntry;
- private PendingIntent mAppOverlayIntent;
- private BubbleController mBubbleController;
- private ActivityView mActivityView;
- private boolean mActivityViewReady;
- private boolean mActivityViewStarted;
public BubbleView(Context context) {
this(context, null);
@@ -86,7 +71,6 @@
// XXX: can this padding just be on the view and we look it up?
mPadding = getResources().getDimensionPixelSize(R.dimen.bubble_view_padding);
mIconInset = getResources().getDimensionPixelSize(R.dimen.bubble_icon_inset);
- mBubbleController = Dependency.get(BubbleController.class);
}
@Override
@@ -168,7 +152,6 @@
updateViews();
}
-
/**
* @return the {@link ExpandableNotificationRow} view to display notification content when the
* bubble is expanded.
@@ -235,88 +218,6 @@
return ColorUtils.blendARGB(defaultTint, Color.WHITE, WHITE_SCRIM_ALPHA);
}
- /**
- * @return a view used to display app overlay content when expanded.
- */
- public ActivityView getActivityView() {
- if (mActivityView == null) {
- mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
- true /* singleTaskInstance */);
- Log.d(TAG, "[getActivityView] created: " + mActivityView);
- mActivityView.setCallback(new ActivityView.StateCallback() {
- @Override
- public void onActivityViewReady(ActivityView view) {
- mActivityViewReady = true;
- mActivityView.startActivity(mAppOverlayIntent);
- }
-
- @Override
- public void onActivityViewDestroyed(ActivityView view) {
- mActivityViewReady = false;
- }
-
- /**
- * This is only called for tasks on this ActivityView, which is also set to
- * single-task mode -- meaning never more than one task on this display. If a task
- * is being removed, it's the top Activity finishing and this bubble should
- * be removed or collapsed.
- */
- @Override
- public void onTaskRemovalStarted(int taskId) {
- if (mEntry != null) {
- // Must post because this is called from a binder thread.
- post(() -> mBubbleController.removeBubble(mEntry.key));
- }
- }
- });
- mActivityView.setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
- ActivityView activityView = (ActivityView) view;
- // Here we assume that the position of the ActivityView on the screen
- // remains regardless of IME status. When we move ActivityView, the
- // forwardedInsets should be computed not against the current location
- // and size, but against the post-moved location and size.
- Point displaySize = new Point();
- view.getContext().getDisplay().getSize(displaySize);
- int[] windowLocation = view.getLocationOnScreen();
- final int windowBottom = windowLocation[1] + view.getHeight();
- final int keyboardHeight = insets.getSystemWindowInsetBottom()
- - insets.getStableInsetBottom();
- final int insetsBottom = Math.max(0,
- windowBottom + keyboardHeight - displaySize.y);
- activityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
- return view.onApplyWindowInsets(insets);
- });
-
- }
- return mActivityView;
- }
-
- /**
- * Removes and releases an ActivityView if one was previously created for this bubble.
- */
- public void destroyActivityView(ViewGroup tmpParent) {
- if (mActivityView == null) {
- return;
- }
- if (!mActivityViewReady) {
- // release not needed, never initialized?
- mActivityView = null;
- return;
- }
- // HACK: release() will crash if the view is not attached.
- if (!mActivityView.isAttachedToWindow()) {
- mActivityView.setVisibility(View.GONE);
- tmpParent.addView(mActivityView, new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
- }
-
- mActivityView.release();
-
- ((ViewGroup) mActivityView.getParent()).removeView(mActivityView);
- mActivityView = null;
- }
-
@Override
public void setPosition(float x, float y) {
setPositionX(x);
@@ -337,20 +238,4 @@
public PointF getPosition() {
return new PointF(getTranslationX(), getTranslationY());
}
-
- /**
- * @return whether an ActivityView should be used to display the content of this Bubble
- */
- public boolean hasAppOverlayIntent() {
- return mAppOverlayIntent != null;
- }
-
- public PendingIntent getAppOverlayIntent() {
- return mAppOverlayIntent;
-
- }
-
- public void setBubbleIntent(PendingIntent intent) {
- mAppOverlayIntent = intent;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java b/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java
index 4a2e06c..f5cf149 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java
@@ -25,9 +25,4 @@
* Invoked every time a minute is elapsed in doze mode
*/
void dozeTimeTick();
-
- /**
- * When view is double tapped in doze mode.
- */
- void onDozeDoubleTap();
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 5efdc2f..2d1dba6 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -131,7 +131,7 @@
new PluginSensor(
new SensorManagerPlugin.Sensor(TYPE_WAKE_LOCK_SCREEN),
Settings.Secure.DOZE_WAKE_SCREEN_GESTURE,
- mConfig.wakeScreenGestureAvailable(),
+ mConfig.wakeScreenGestureAvailable() && alwaysOn,
DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
false /* reports touch coordinates */,
false /* touchscreen */),
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 873fbc2..e207cb1 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -161,7 +161,8 @@
} else {
mDozeHost.extendPulse();
}
- }, sensorPerformedProxCheck || mDockManager.isDocked(), pulseReason);
+ }, sensorPerformedProxCheck
+ || (mDockManager != null && mDockManager.isDocked()), pulseReason);
}
if (isPickup) {
@@ -224,7 +225,9 @@
case INITIALIZED:
mBroadcastReceiver.register(mContext);
mDozeHost.addCallback(mHostCallback);
- mDockManager.addListener(mDockEventListener);
+ if (mDockManager != null) {
+ mDockManager.addListener(mDockEventListener);
+ }
checkTriggersAtInit();
break;
case DOZE:
@@ -250,7 +253,9 @@
case FINISH:
mBroadcastReceiver.unregister(mContext);
mDozeHost.removeCallback(mHostCallback);
- mDockManager.removeListener(mDockEventListener);
+ if (mDockManager != null) {
+ mDockManager.removeListener(mDockEventListener);
+ }
mDozeSensors.setListening(false);
mDozeSensors.setProxListening(false);
break;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index f5ac0d3..7c9b286 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -33,6 +33,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.UserInfo;
+import android.content.res.Configuration;
import android.database.ContentObserver;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
@@ -91,6 +92,7 @@
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.EmergencyDialerConstants;
+import com.android.systemui.util.leak.RotationUtils;
import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator;
import java.util.ArrayList;
@@ -159,6 +161,8 @@
private final ScreenshotHelper mScreenshotHelper;
private final ScreenRecordHelper mScreenRecordHelper;
+ private int mLastRotation;
+
/**
* @param context everything needs a context :(
*/
@@ -201,6 +205,8 @@
mScreenshotHelper = new ScreenshotHelper(context);
mScreenRecordHelper = new ScreenRecordHelper(context);
+ mLastRotation = RotationUtils.getRotation(mContext);
+
Dependency.get(ConfigurationController.class).addCallback(this);
}
@@ -426,6 +432,15 @@
mContext.getTheme().applyStyle(mContext.getThemeResId(), true);
}
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ int rotation = RotationUtils.getRotation(mContext);
+ if (rotation != mLastRotation) {
+ mDialog.onRotate();
+ }
+ mLastRotation = rotation;
+ }
+
public void destroy() {
Dependency.get(ConfigurationController.class).removeCallback(this);
}
@@ -1091,7 +1106,7 @@
}
protected int getActionLayoutId(Context context) {
- if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.GLOBAL_ACTIONS_GRID_ENABLED)) {
+ if (isGridEnabled(context)) {
return com.android.systemui.R.layout.global_actions_grid_item;
}
return com.android.systemui.R.layout.global_actions_item;
@@ -1465,7 +1480,7 @@
private final Context mContext;
private final MyAdapter mAdapter;
- private final MultiListLayout mGlobalActionsLayout;
+ private MultiListLayout mGlobalActionsLayout;
private final OnClickListener mClickListener;
private final OnItemLongClickListener mLongClickListener;
private final GradientDrawable mGradientDrawable;
@@ -1505,8 +1520,13 @@
window.setBackgroundDrawable(mGradientDrawable);
window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
+ initializeLayout();
- setContentView(getGlobalActionsLayoutId(context));
+ setTitle(R.string.global_actions);
+ }
+
+ private void initializeLayout() {
+ setContentView(getGlobalActionsLayoutId(mContext));
mGlobalActionsLayout = (MultiListLayout)
findViewById(com.android.systemui.R.id.global_actions_view);
mGlobalActionsLayout.setOutsideTouchListener(view -> dismiss());
@@ -1520,11 +1540,20 @@
return true;
}
});
- setTitle(R.string.global_actions);
+ }
+
+ public void onRotate() {
+ if (mShowing && isGridEnabled(mContext)) {
+ initializeLayout();
+ updateList();
+ }
}
private int getGlobalActionsLayoutId(Context context) {
- if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.GLOBAL_ACTIONS_GRID_ENABLED)) {
+ if (isGridEnabled(context)) {
+ if (RotationUtils.getRotation(context) == RotationUtils.ROTATION_SEASCAPE) {
+ return com.android.systemui.R.layout.global_actions_grid_seascape;
+ }
return com.android.systemui.R.layout.global_actions_grid;
}
return com.android.systemui.R.layout.global_actions_wrapped;
@@ -1543,10 +1572,20 @@
int separatedIndex = separatedActions.indexOf(action);
ViewGroup parent;
if (separatedIndex != -1) {
- parent = mGlobalActionsLayout.getParentView(true, separatedIndex);
+ parent = mGlobalActionsLayout.getParentView(true, separatedIndex, false);
} else {
+ boolean reverse = false;
+
+ // If we're using the grid layout and we're in seascape, reverse the order
+ // of sublists to make sure they render in the correct positions,
+ // since we can't reverse vertical LinearLayouts through the layout xml.
+
+ if (isGridEnabled(mContext) && RotationUtils.getRotation(mContext)
+ == RotationUtils.ROTATION_SEASCAPE) {
+ reverse = true;
+ }
int listIndex = listActions.indexOf(action);
- parent = mGlobalActionsLayout.getParentView(false, listIndex);
+ parent = mGlobalActionsLayout.getParentView(false, listIndex, reverse);
}
View v = mAdapter.getView(i, null, parent);
final int pos = i;
@@ -1665,4 +1704,11 @@
mKeyguardShowing = keyguardShowing;
}
}
+
+ /**
+ * Determines whether or not the Global Actions Dialog should use the newer grid-style layout.
+ */
+ public static boolean isGridEnabled(Context context) {
+ return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.GLOBAL_ACTIONS_GRID_ENABLED);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
index 0e49b5f..1d04277 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
@@ -83,11 +83,11 @@
}
@Override
- public ViewGroup getParentView(boolean separated, int index) {
+ public ViewGroup getParentView(boolean separated, int index, boolean reverseOrder) {
if (separated) {
return getSeparatedView();
} else {
- return getListView().getParentView(index);
+ return getListView().getParentView(index, reverseOrder);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java
index 3775515..d5dcd74 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java
@@ -28,16 +28,13 @@
*
* * Try to maintain a 'square' grid (equal number of columns and rows) based on the expected item
* count.
+ * * Determine the position and parent of any item by its index and the total item count.
* * Display and hide sub-lists as needed, depending on the expected item count.
- * * Favor bias toward having more rows or columns depending on the orientation of the device
- * (TODO(123344999): Implement this, currently always favors adding more rows.)
- * * Change the orientation (horizontal vs. vertical) of the container and sub-lists to act as rows
- * or columns depending on the orientation of the device.
- * (TODO(123344999): Implement this, currently always columns.)
*
* While we could implement this behavior with a GridLayout, it would take significantly more
* time and effort, and would require more substantial refactoring of the existing code in
- * GlobalActionsDialog, since it would require manipulation of the child items themselves.
+ * GlobalActionsDialog, since it would require manipulation of layout properties on the child items
+ * themselves.
*
*/
@@ -65,14 +62,25 @@
/**
* Get the parent view associated with the item which should be placed at the given position.
*/
- public ViewGroup getParentView(int index) {
- ViewGroup firstParent = (ViewGroup) getChildAt(0);
+ public ViewGroup getParentView(int index, boolean reverseSublists) {
if (mRows == 0) {
- return firstParent;
+ return null;
}
+ int column = getParentViewIndex(index, reverseSublists);
+ return (ViewGroup) getChildAt(column);
+ }
+
+ private int reverseSublistIndex(int index) {
+ return getChildCount() - (index + 1);
+ }
+
+ private int getParentViewIndex(int index, boolean reverseSublists) {
int column = (int) Math.floor(index / mRows);
- ViewGroup parent = (ViewGroup) getChildAt(column);
- return parent != null ? parent : firstParent;
+ int columnCount = getChildCount();
+ if (reverseSublists) {
+ column = reverseSublistIndex(column);
+ }
+ return column;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 684175c..85d975f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -25,7 +25,9 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.graphics.Rect;
import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.icu.text.DateFormat;
import android.icu.text.DisplayContext;
@@ -35,10 +37,13 @@
import android.os.Trace;
import android.provider.Settings;
import android.service.notification.ZenModeConfig;
-import android.text.Spannable;
import android.text.SpannableStringBuilder;
+import android.text.Spanned;
import android.text.TextUtils;
+import android.text.style.DynamicDrawableSpan;
+import android.text.style.ImageSpan;
import android.text.style.StyleSpan;
+import android.util.MathUtils;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
@@ -72,6 +77,8 @@
private static final StyleSpan BOLD_STYLE = new StyleSpan(Typeface.BOLD);
public static final String KEYGUARD_SLICE_URI = "content://com.android.systemui.keyguard/main";
+ private static final String KEYGUARD_HEADER_URI =
+ "content://com.android.systemui.keyguard/header";
public static final String KEYGUARD_DATE_URI = "content://com.android.systemui.keyguard/date";
public static final String KEYGUARD_NEXT_ALARM_URI =
"content://com.android.systemui.keyguard/alarm";
@@ -90,6 +97,7 @@
private static KeyguardSliceProvider sInstance;
protected final Uri mSliceUri;
+ protected final Uri mHeaderUri;
protected final Uri mDateUri;
protected final Uri mAlarmUri;
protected final Uri mDndUri;
@@ -163,6 +171,7 @@
KeyguardSliceProvider(Handler handler) {
mHandler = handler;
mSliceUri = Uri.parse(KEYGUARD_SLICE_URI);
+ mHeaderUri = Uri.parse(KEYGUARD_HEADER_URI);
mDateUri = Uri.parse(KEYGUARD_DATE_URI);
mAlarmUri = Uri.parse(KEYGUARD_NEXT_ALARM_URI);
mDndUri = Uri.parse(KEYGUARD_DND_URI);
@@ -213,26 +222,32 @@
protected void addMediaLocked(ListBuilder listBuilder) {
if (mMediaMetaData != null) {
SpannableStringBuilder builder = new SpannableStringBuilder();
+
+ Icon notificationIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon();
+ if (notificationIcon != null) {
+ Drawable drawable = notificationIcon.loadDrawable(getContext());
+ Rect mediaBounds = new Rect(0 /* left */, 0 /* top */,
+ drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+ int iconHeaderSize = getContext().getResources()
+ .getDimensionPixelSize(R.dimen.header_icon_size);
+ MathUtils.fitRect(mediaBounds, iconHeaderSize);
+ drawable.setBounds(mediaBounds);
+ builder.append("# ");
+ builder.setSpan(new ImageSpan(drawable, DynamicDrawableSpan.ALIGN_CENTER),
+ 0 /* start */, 1 /* end */, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ }
+
CharSequence title = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_TITLE);
if (TextUtils.isEmpty(title)) {
title = getContext().getResources().getString(R.string.music_controls_no_title);
}
builder.append(title);
- builder.setSpan(BOLD_STYLE, 0, title.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+ listBuilder.setHeader(new ListBuilder.HeaderBuilder(mHeaderUri).setTitle(builder));
CharSequence album = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_ARTIST);
if (!TextUtils.isEmpty(album)) {
- builder.append(" ").append(album);
+ listBuilder.addRow(new RowBuilder(mMediaUri).setTitle(album));
}
-
- RowBuilder mediaBuilder = new RowBuilder(mMediaUri).setTitle(builder);
- Icon notificationIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon();
- if (notificationIcon != null) {
- IconCompat icon = IconCompat.createFromIcon(notificationIcon);
- mediaBuilder.addEndItem(icon, ListBuilder.ICON_IMAGE);
- }
-
- listBuilder.addRow(mediaBuilder);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index faeacf7..0f33937 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -26,16 +26,23 @@
import android.os.UserHandle
import android.os.UserManager
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.Dependency
+import com.android.systemui.Dependency.BG_HANDLER_NAME
+import com.android.systemui.Dependency.MAIN_HANDLER_NAME
+import com.android.systemui.R
import com.android.systemui.appops.AppOpItem
import com.android.systemui.appops.AppOpsController
-import com.android.systemui.R
import java.lang.ref.WeakReference
import javax.inject.Inject
+import javax.inject.Named
import javax.inject.Singleton
@Singleton
-class PrivacyItemController @Inject constructor(private val context: Context) {
+class PrivacyItemController @Inject constructor(
+ val context: Context,
+ private val appOpsController: AppOpsController,
+ @Named(MAIN_HANDLER_NAME) private val uiHandler: Handler,
+ @Named(BG_HANDLER_NAME) private val bgHandler: Handler
+) {
companion object {
val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
@@ -48,16 +55,10 @@
const val TAG = "PrivacyItemController"
const val SYSTEM_UID = 1000
}
- private var privacyList = emptyList<PrivacyItem>()
- @Suppress("DEPRECATION")
- private val appOpsController = Dependency.get(AppOpsController::class.java)
+ private var privacyList = emptyList<PrivacyItem>()
private val userManager = context.getSystemService(UserManager::class.java)
private var currentUserIds = emptyList<Int>()
- @Suppress("DEPRECATION")
- private val bgHandler = Handler(Dependency.get(Dependency.BG_LOOPER))
- @Suppress("DEPRECATION")
- private val uiHandler = Dependency.get(Dependency.MAIN_HANDLER)
private var listening = false
val systemApp =
PrivacyApplication(context.getString(R.string.device_services), SYSTEM_UID, context)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 01b0bb1..110d515 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -348,15 +348,13 @@
if (notGoneIndex == 0) {
StatusBarIconView icon = row.getEntry().expandedIcon;
NotificationIconContainer.IconState iconState = getIconState(icon);
+ // The icon state might be null in rare cases where the notification is actually
+ // added to the layout, but not to the shelf. An example are replied messages, since
+ // they don't show up on AOD
if (iconState != null && iconState.clampedAppearAmount == 1.0f) {
// only if the first icon is fully in the shelf we want to clip to it!
backgroundTop = (int) (row.getTranslationY() - getTranslationY());
firstElementRoundness = row.getCurrentTopRoundness();
- } else if (iconState == null) {
- Log.wtf(TAG, "iconState is null. ExpandedIcon: " + row.getEntry().expandedIcon
- + (row.getEntry().expandedIcon != null
- ? "\n icon parent: " + row.getEntry().expandedIcon.getParent() : "")
- + " \n number of notifications: " + mHostLayout.getChildCount() );
}
}
if (row.isFirstInSection() && previousRow != null && previousRow.isLastInSection()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 662cf51..ee5ac7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -24,6 +24,7 @@
import android.view.ViewGroup;
import com.android.systemui.R;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -61,7 +62,7 @@
protected final NotificationLockscreenUserManager mLockscreenUserManager;
protected final NotificationGroupManager mGroupManager;
protected final VisualStabilityManager mVisualStabilityManager;
- private final StatusBarStateControllerImpl mStatusBarStateController;
+ private final SysuiStatusBarStateController mStatusBarStateController;
private final NotificationEntryManager mEntryManager;
// Lazy
@@ -82,13 +83,13 @@
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationGroupManager groupManager,
VisualStabilityManager visualStabilityManager,
- StatusBarStateControllerImpl statusBarStateController,
+ StatusBarStateController statusBarStateController,
NotificationEntryManager notificationEntryManager,
Lazy<ShadeController> shadeController) {
mLockscreenUserManager = notificationLockscreenUserManager;
mGroupManager = groupManager;
mVisualStabilityManager = visualStabilityManager;
- mStatusBarStateController = statusBarStateController;
+ mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController;
mEntryManager = notificationEntryManager;
mShadeController = shadeController;
Resources res = context.getResources();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 8f7778b..69828c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -1213,6 +1213,7 @@
l.initView();
l.reInflateViews();
}
+ mStatusBarNotification.clearPackageContext();
mNotificationInflater.clearCachesAndReInflate();
onNotificationUpdated();
}
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 6b6998d..653ec50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -189,7 +189,6 @@
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
@@ -653,7 +652,7 @@
mColorExtractor.addOnColorsChangedListener(this);
mStatusBarStateController.addCallback(this,
- StatusBarStateControllerImpl.RANK_STATUS_BAR);
+ SysuiStatusBarStateController.RANK_STATUS_BAR);
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mDreamManager = IDreamManager.Stub.asInterface(
@@ -4004,8 +4003,7 @@
float viewY = screenY - mTmpInt2[1];
if (0 <= viewX && viewX <= mAmbientIndicationContainer.getWidth()
&& 0 <= viewY && viewY <= mAmbientIndicationContainer.getHeight()) {
- if (mAmbientIndicationContainer instanceof DozeReceiver)
- ((DozeReceiver) mAmbientIndicationContainer).onDozeDoubleTap();
+ dispatchTap(mAmbientIndicationContainer, viewX, viewY);
}
}
}
@@ -4020,6 +4018,12 @@
mScrimController.setAodFrontScrimAlpha(scrimOpacity);
}
+ private void dispatchTap(View view, float x, float y) {
+ long now = SystemClock.elapsedRealtime();
+ dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_DOWN);
+ dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_UP);
+ }
+
private void dispatchTouchEvent(View view, float x, float y, long now, int action) {
MotionEvent ev = MotionEvent.obtain(now, now, action, x, y, 0 /* meta */);
view.dispatchTouchEvent(ev);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
index 77895c9..190ce75 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
@@ -75,6 +75,17 @@
}
@Test
+ public void hasHeader_readsSliceData() {
+ ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY);
+ mKeyguardSliceView.onChanged(builder.build());
+ Assert.assertFalse("View should not have a header", mKeyguardSliceView.hasHeader());
+
+ builder.setHeader(new ListBuilder.HeaderBuilder().setTitle("header title!"));
+ mKeyguardSliceView.onChanged(builder.build());
+ Assert.assertTrue("View should have a header", mKeyguardSliceView.hasHeader());
+ }
+
+ @Test
public void refresh_replacesSliceContentAndNotifiesListener() {
AtomicBoolean notified = new AtomicBoolean();
mKeyguardSliceView.setContentChangeListener(()-> notified.set(true));
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
new file mode 100644
index 0000000..f813ac6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard.clock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.LeakCheck;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dock.DockManager;
+import com.android.systemui.dock.DockManagerFake;
+import com.android.systemui.utils.leaks.FakeExtensionController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public final class ClockManagerTest extends SysuiTestCase {
+
+ private ClockManager mClockManager;
+ private LeakCheck mLeakCheck;
+ private FakeExtensionController mFakeExtensionController;
+ private DockManagerFake mFakeDockManager;
+ @Mock ClockManager.ClockChangedListener mMockListener;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mLeakCheck = new LeakCheck();
+ mFakeExtensionController = new FakeExtensionController(mLeakCheck);
+ mFakeDockManager = new DockManagerFake();
+ mClockManager = new ClockManager(getContext(), mFakeExtensionController,
+ mFakeDockManager);
+ mClockManager.addOnClockChangedListener(mMockListener);
+ }
+
+ @After
+ public void tearDown() {
+ mClockManager.removeOnClockChangedListener(mMockListener);
+ }
+
+ @Test
+ public void dockEvent() {
+ mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED);
+ assertThat(mClockManager.isDocked()).isTrue();
+ }
+
+ @Test
+ public void undockEvent() {
+ mFakeDockManager.setDockEvent(DockManager.STATE_NONE);
+ assertThat(mClockManager.isDocked()).isFalse();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/DefaultClockSupplierTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/DefaultClockSupplierTest.java
new file mode 100644
index 0000000..1a3b198
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/DefaultClockSupplierTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard.clock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.LayoutInflater;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.ClockPlugin;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public final class DefaultClockSupplierTest extends SysuiTestCase {
+
+ private static final String BUBBLE_CLOCK = BubbleClockController.class.getName();
+ private static final Class<?> BUBBLE_CLOCK_CLASS = BubbleClockController.class;
+
+ private DefaultClockSupplier mDefaultClockSupplier;
+ @Mock SettingsWrapper mMockSettingsWrapper;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mDefaultClockSupplier = new DefaultClockSupplier(mMockSettingsWrapper,
+ LayoutInflater.from(getContext()));
+ }
+
+ @Test
+ public void get_default() {
+ // GIVEN that settings doesn't contain any values
+ when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(null);
+ when(mMockSettingsWrapper.getDockedClockFace()).thenReturn(null);
+ // WHEN get is called
+ ClockPlugin plugin = mDefaultClockSupplier.get();
+ // THEN the result is null, indicated the default clock face should be used.
+ assertThat(plugin).isNull();
+ }
+
+ @Test
+ public void get_customClock() {
+ // GIVEN that settings is set to the bubble clock face
+ when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK);
+ // WHEN get is called
+ ClockPlugin plugin = mDefaultClockSupplier.get();
+ // THEN the plugin is the bubble clock face.
+ assertThat(plugin).isInstanceOf(BUBBLE_CLOCK_CLASS);
+ }
+
+ @Test
+ public void get_badSettingsValue() {
+ // GIVEN that settings contains a value that doesn't correspond to a
+ // custom clock face.
+ when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn("bad value");
+ // WHEN get is called
+ ClockPlugin plugin = mDefaultClockSupplier.get();
+ // THEN the result is null.
+ assertThat(plugin).isNull();
+ }
+
+ @Test
+ public void get_dockedDefault() {
+ // GIVEN docked
+ mDefaultClockSupplier.setDocked(true);
+ // WHEN get is called
+ ClockPlugin plugin = mDefaultClockSupplier.get();
+ // THEN the result is null, indicating the default clock face.
+ assertThat(plugin).isNull();
+ }
+
+ @Test
+ public void get_dockedCustomClock() {
+ // GIVEN docked and settings is set to the bubble clock face
+ mDefaultClockSupplier.setDocked(true);
+ when(mMockSettingsWrapper.getDockedClockFace()).thenReturn(BUBBLE_CLOCK);
+ // WHEN get is called
+ ClockPlugin plugin = mDefaultClockSupplier.get();
+ // THEN the plugin is the bubble clock face.
+ assertThat(plugin).isInstanceOf(BUBBLE_CLOCK_CLASS);
+ }
+
+ @Test
+ public void get_badDockedSettingsValue() {
+ // GIVEN docked and settings contains a value that doesn't correspond to
+ // an available clock face.
+ mDefaultClockSupplier.setDocked(true);
+ when(mMockSettingsWrapper.getDockedClockFace()).thenReturn("bad value");
+ // WHEN get is called
+ ClockPlugin plugin = mDefaultClockSupplier.get();
+ // THEN the result is null.
+ assertThat(plugin).isNull();
+ }
+
+ @Test
+ public void get_badDockedSettingsFallback() {
+ // GIVEN docked and settings contains a value that doesn't correspond to
+ // an available clock face, but locked screen settings is set to bubble
+ // clock.
+ mDefaultClockSupplier.setDocked(true);
+ when(mMockSettingsWrapper.getDockedClockFace()).thenReturn("bad value");
+ when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK);
+ // WHEN get is called
+ ClockPlugin plugin = mDefaultClockSupplier.get();
+ // THEN the plugin is the bubble clock face.
+ assertThat(plugin).isInstanceOf(BUBBLE_CLOCK_CLASS);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 2742577..d9315f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -112,6 +112,9 @@
verify(mNotificationEntryManager, atLeastOnce())
.addNotificationEntryListener(mEntryListenerCaptor.capture());
mEntryListener = mEntryListenerCaptor.getValue();
+
+ // Reset the data
+ BubbleData.getInstance().clear();
}
@Test
@@ -207,12 +210,12 @@
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key);
// Last added is the one that is expanded
- assertEquals(mRow2.getEntry(), stackView.getExpandedBubble().getEntry());
+ assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry());
assertFalse(mRow2.getEntry().showInShadeWhenBubble());
// Switch which bubble is expanded
stackView.setExpandedBubble(mRow.getEntry());
- assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry());
+ assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry());
assertFalse(mRow.getEntry().showInShadeWhenBubble());
// collapse for previous bubble
@@ -262,19 +265,19 @@
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key);
// Last added is the one that is expanded
- assertEquals(mRow2.getEntry(), stackView.getExpandedBubble().getEntry());
+ assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry());
assertFalse(mRow2.getEntry().showInShadeWhenBubble());
// Dismiss currently expanded
- mBubbleController.removeBubble(stackView.getExpandedBubble().getKey());
+ mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey());
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().key);
// Make sure next bubble is selected
- assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry());
+ assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
// Dismiss that one
- mBubbleController.removeBubble(stackView.getExpandedBubble().getKey());
+ mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey());
// Make sure state changes and collapse happens
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index 98bf3c27..5d3f6ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -18,6 +18,7 @@
import android.app.ActivityManager
import android.app.AppOpsManager
+import android.content.Context
import android.content.Intent
import android.content.pm.UserInfo
import android.os.Handler
@@ -28,6 +29,8 @@
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import com.android.systemui.Dependency
+import com.android.systemui.Dependency.BG_HANDLER
+import com.android.systemui.Dependency.MAIN_HANDLER
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.appops.AppOpItem
@@ -81,15 +84,20 @@
private lateinit var testableLooper: TestableLooper
private lateinit var privacyItemController: PrivacyItemController
+ private lateinit var handler: Handler
+
+ fun PrivacyItemController(context: Context) =
+ PrivacyItemController(context, appOpsController, handler, handler)
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
+ handler = Handler(testableLooper.looper)
appOpsController = mDependency.injectMockDependency(AppOpsController::class.java)
- mDependency.injectTestDependency(Dependency.BG_LOOPER, testableLooper.looper)
- mDependency.injectTestDependency(Dependency.MAIN_HANDLER, Handler(testableLooper.looper))
+ mDependency.injectTestDependency(Dependency.BG_HANDLER, handler)
+ mDependency.injectTestDependency(Dependency.MAIN_HANDLER, handler)
mContext.addMockSystemService(UserManager::class.java, userManager)
mContext.getOrCreateTestableResources().addOverride(R.string.device_services,
DEVICE_SERVICES_STRING)
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 9b863a9..61f63d3 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -47,7 +47,7 @@
private static final String TAG = RemoteAugmentedAutofillService.class.getSimpleName();
- private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
+ private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
RemoteAugmentedAutofillService(Context context, ComponentName serviceName,
int userId, RemoteAugmentedAutofillServiceCallbacks callbacks,
@@ -106,6 +106,12 @@
activityComponent, focusedId, focusedValue));
}
+ @Override
+ public String toString() {
+ return "RemoteAugmentedAutofillService["
+ + ComponentName.flattenToShortString(getComponentName()) + "]";
+ }
+
/**
* Called by {@link Session} when it's time to destroy all augmented autofill requests.
*/
@@ -181,11 +187,13 @@
@Override
protected void onTimeout(RemoteAugmentedAutofillService remoteService) {
- Slog.wtf(TAG, "timed out: " + this);
+ // TODO(b/122858578): must update the logged AUTOFILL_AUGMENTED_REQUEST with the
+ // timeout
+ Slog.w(TAG, "PendingAutofillRequest timed out (" + TIMEOUT_REMOTE_REQUEST_MILLIS
+ + "ms) for " + remoteService);
// NOTE: so far we don't need notify RemoteAugmentedAutofillServiceCallbacks
finish();
}
-
}
public interface RemoteAugmentedAutofillServiceCallbacks
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
index 3c52e17..4ed5c3d 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
@@ -37,6 +37,9 @@
final IBinder mActivityToken;
private final ContentCapturePerUserService mService;
private final RemoteContentCaptureService mRemoteService;
+
+ // NOTE: this is the "internal" context (like package and taskId), not the explicit content
+ // set by apps - those are only send to the ContentCaptureService.
private final ContentCaptureContext mContentCaptureContext;
/**
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d1cd072..dad428a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2842,6 +2842,8 @@
if (DBG) {
log(nai.name() + " got DISCONNECTED, was satisfying " + nai.numNetworkRequests());
}
+ // Clear all notifications of this network.
+ mNotifier.clearNotification(nai.network.netId);
// A network agent has disconnected.
// TODO - if we move the logic to the network agent (have them disconnect
// because they lost all their requests or because their score isn't good)
diff --git a/services/core/java/com/android/server/ExtconUEventObserver.java b/services/core/java/com/android/server/ExtconUEventObserver.java
index ebd4c55..eb59152 100644
--- a/services/core/java/com/android/server/ExtconUEventObserver.java
+++ b/services/core/java/com/android/server/ExtconUEventObserver.java
@@ -49,7 +49,7 @@
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.";
+ "This probably means the selinux policies need to be changed.";
private final Map<String, ExtconInfo> mExtconInfos = new ArrayMap<>();
@@ -159,6 +159,15 @@
/** Does the {@link /sys/class/extcon} directory exist */
public static boolean extconExists() {
File extconDir = new File("/sys/class/extcon");
- return extconDir.exists() && extconDir.isDirectory();
+ boolean retVal = extconDir.exists() && extconDir.isDirectory();
+ // TODO(b/124364409): return the correct value after selinux policy is updated.
+ if (retVal) {
+ Slog.w(TAG, extconDir + " exists " + extconDir.exists() + " isDir "
+ + extconDir.isDirectory()
+ + " but reporting it does not exist until selinux policies are updated."
+ + " see b/124364409"
+ );
+ }
+ return false;
}
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 3479e18..0b0934b 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -2069,7 +2069,7 @@
}
if (!provider.isUseableLocked()) {
if (isSettingsExemptLocked(record)) {
- providerRequest.forceLocation = true;
+ providerRequest.locationSettingsIgnored = true;
providerRequest.lowPowerMode = false;
} else {
continue;
@@ -2079,8 +2079,9 @@
LocationRequest locationRequest = record.mRealRequest;
long interval = locationRequest.getInterval();
+
// if we're forcing location, don't apply any throttling
- if (!providerRequest.forceLocation && !isThrottlingExemptLocked(
+ if (!providerRequest.locationSettingsIgnored && !isThrottlingExemptLocked(
record.mReceiver.mCallerIdentity)) {
if (!record.mIsForegroundUid) {
interval = Math.max(interval, backgroundThrottleInterval);
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index f505b76..dc394d0 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -20,18 +20,18 @@
import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.Manifest.permission.NETWORK_STACK;
import static android.Manifest.permission.SHUTDOWN;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_BLACKLIST;
+import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_CHAIN_NONE;
+import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
+import static android.net.INetd.FIREWALL_RULE_ALLOW;
+import static android.net.INetd.FIREWALL_RULE_DENY;
+import static android.net.INetd.FIREWALL_WHITELIST;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
-import static android.net.NetworkPolicyManager.FIREWALL_TYPE_BLACKLIST;
-import static android.net.NetworkPolicyManager.FIREWALL_TYPE_WHITELIST;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.STATS_PER_UID;
import static android.net.NetworkStats.TAG_ALL;
@@ -1946,7 +1946,7 @@
int numUids = 0;
if (DBG) Slog.d(TAG, "Closing sockets after enabling chain " + chainName);
- if (getFirewallType(chain) == FIREWALL_TYPE_WHITELIST) {
+ if (getFirewallType(chain) == FIREWALL_WHITELIST) {
// Close all sockets on all non-system UIDs...
ranges = new UidRange[] {
// TODO: is there a better way of finding all existing users? If so, we could
@@ -1958,7 +1958,7 @@
final SparseIntArray rules = getUidFirewallRulesLR(chain);
exemptUids = new int[rules.size()];
for (int i = 0; i < exemptUids.length; i++) {
- if (rules.valueAt(i) == NetworkPolicyManager.FIREWALL_RULE_ALLOW) {
+ if (rules.valueAt(i) == FIREWALL_RULE_ALLOW) {
exemptUids[numUids] = rules.keyAt(i);
numUids++;
}
@@ -1980,7 +1980,7 @@
final SparseIntArray rules = getUidFirewallRulesLR(chain);
ranges = new UidRange[rules.size()];
for (int i = 0; i < ranges.length; i++) {
- if (rules.valueAt(i) == NetworkPolicyManager.FIREWALL_RULE_DENY) {
+ if (rules.valueAt(i) == FIREWALL_RULE_DENY) {
int uid = rules.keyAt(i);
ranges[numUids] = new UidRange(uid, uid);
numUids++;
@@ -2052,13 +2052,13 @@
private int getFirewallType(int chain) {
switch (chain) {
case FIREWALL_CHAIN_STANDBY:
- return FIREWALL_TYPE_BLACKLIST;
+ return FIREWALL_BLACKLIST;
case FIREWALL_CHAIN_DOZABLE:
- return FIREWALL_TYPE_WHITELIST;
+ return FIREWALL_WHITELIST;
case FIREWALL_CHAIN_POWERSAVE:
- return FIREWALL_TYPE_WHITELIST;
+ return FIREWALL_WHITELIST;
default:
- return isFirewallEnabled() ? FIREWALL_TYPE_WHITELIST : FIREWALL_TYPE_BLACKLIST;
+ return isFirewallEnabled() ? FIREWALL_WHITELIST : FIREWALL_BLACKLIST;
}
}
@@ -2160,14 +2160,14 @@
private @NonNull String getFirewallRuleName(int chain, int rule) {
String ruleName;
- if (getFirewallType(chain) == FIREWALL_TYPE_WHITELIST) {
- if (rule == NetworkPolicyManager.FIREWALL_RULE_ALLOW) {
+ if (getFirewallType(chain) == FIREWALL_WHITELIST) {
+ if (rule == FIREWALL_RULE_ALLOW) {
ruleName = "allow";
} else {
ruleName = "deny";
}
} else { // Blacklist mode
- if (rule == NetworkPolicyManager.FIREWALL_RULE_DENY) {
+ if (rule == FIREWALL_RULE_DENY) {
ruleName = "deny";
} else {
ruleName = "allow";
@@ -2194,7 +2194,7 @@
private int getFirewallRuleType(int chain, int rule) {
if (rule == NetworkPolicyManager.FIREWALL_RULE_DEFAULT) {
- return getFirewallType(chain) == FIREWALL_TYPE_WHITELIST
+ return getFirewallType(chain) == FIREWALL_WHITELIST
? INetd.FIREWALL_RULE_DENY : INetd.FIREWALL_RULE_ALLOW;
}
return rule;
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 2f1510e..8120976 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -26,6 +26,7 @@
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -246,7 +247,10 @@
private PreciseDataConnectionState mPreciseDataConnectionState =
new PreciseDataConnectionState();
- static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK =
+ // Nothing here yet, but putting it here in case we want to add more in the future.
+ static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK = 0;
+
+ static final int ENFORCE_FINE_LOCATION_PERMISSION_MASK =
PhoneStateListener.LISTEN_CELL_LOCATION
| PhoneStateListener.LISTEN_CELL_INFO;
@@ -637,8 +641,14 @@
if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
try {
if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]);
- r.callback.onServiceStateChanged(
- new ServiceState(mServiceState[phoneId]));
+ ServiceState rawSs = new ServiceState(mServiceState[phoneId]);
+ if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+ r.callback.onServiceStateChanged(rawSs);
+ } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
+ r.callback.onServiceStateChanged(rawSs.sanitizeLocationInfo(false));
+ } else {
+ r.callback.onServiceStateChanged(rawSs.sanitizeLocationInfo(true));
+ }
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -673,7 +683,7 @@
try {
if (DBG_LOC) log("listen: mCellLocation = "
+ mCellLocation[phoneId]);
- if (checkLocationAccess(r)) {
+ if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
r.callback.onCellLocationChanged(
new Bundle(mCellLocation[phoneId]));
}
@@ -722,7 +732,7 @@
try {
if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = "
+ mCellInfo.get(phoneId));
- if (checkLocationAccess(r)) {
+ if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
}
} catch (RemoteException ex) {
@@ -1009,13 +1019,22 @@
}
if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SERVICE_STATE) &&
idMatch(r.subId, subId, phoneId)) {
+
try {
+ ServiceState stateToSend;
+ if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+ stateToSend = new ServiceState(state);
+ } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
+ stateToSend = state.sanitizeLocationInfo(false);
+ } else {
+ stateToSend = state.sanitizeLocationInfo(true);
+ }
if (DBG) {
log("notifyServiceStateForSubscriber: callback.onSSC r=" + r
+ " subId=" + subId + " phoneId=" + phoneId
+ " state=" + state);
}
- r.callback.onServiceStateChanged(new ServiceState(state));
+ r.callback.onServiceStateChanged(stateToSend);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
@@ -1198,7 +1217,7 @@
for (Record r : mRecords) {
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) &&
idMatch(r.subId, subId, phoneId) &&
- checkLocationAccess(r)) {
+ checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
try {
if (DBG_LOC) {
log("notifyCellInfo: mCellInfo=" + cellInfo + " r=" + r);
@@ -1500,7 +1519,7 @@
for (Record r : mRecords) {
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) &&
idMatch(r.subId, subId, phoneId) &&
- checkLocationAccess(r)) {
+ checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
try {
if (DBG_LOC) {
log("notifyCellLocation: cellLocation=" + cellLocation
@@ -2108,12 +2127,35 @@
private boolean checkListenerPermission(
int events, int subId, String callingPackage, String message) {
+ LocationAccessPolicy.LocationPermissionQuery.Builder locationQueryBuilder =
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(callingPackage)
+ .setMethod(message + " events: " + events)
+ .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(Binder.getCallingUid());
+
+ boolean shouldCheckLocationPermissions = false;
if ((events & ENFORCE_COARSE_LOCATION_PERMISSION_MASK) != 0) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
- if (mAppOps.noteOp(AppOpsManager.OP_COARSE_LOCATION, Binder.getCallingUid(),
- callingPackage) != AppOpsManager.MODE_ALLOWED) {
- return false;
+ locationQueryBuilder.setMinSdkVersionForCoarse(0);
+ shouldCheckLocationPermissions = true;
+ }
+
+ if ((events & ENFORCE_FINE_LOCATION_PERMISSION_MASK) != 0) {
+ // Everything that requires fine location started in Q. So far...
+ locationQueryBuilder.setMinSdkVersionForFine(Build.VERSION_CODES.Q);
+ shouldCheckLocationPermissions = true;
+ }
+
+ if (shouldCheckLocationPermissions) {
+ LocationAccessPolicy.LocationPermissionResult result =
+ LocationAccessPolicy.checkLocationPermission(
+ mContext, locationQueryBuilder.build());
+ switch (result) {
+ case DENIED_HARD:
+ throw new SecurityException("Unable to listen for events " + events + " due to "
+ + "insufficient location permissions.");
+ case DENIED_SOFT:
+ return false;
}
}
@@ -2228,15 +2270,38 @@
}
}
- private boolean checkLocationAccess(Record r) {
- long token = Binder.clearCallingIdentity();
- try {
- return LocationAccessPolicy.canAccessCellLocation(mContext,
- r.callingPackage, r.callerUid, r.callerPid,
- /*throwOnDeniedPermission*/ false);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ private boolean checkFineLocationAccess(Record r, int minSdk) {
+ LocationAccessPolicy.LocationPermissionQuery query =
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(r.callingPackage)
+ .setCallingPid(r.callerPid)
+ .setCallingUid(r.callerUid)
+ .setMethod("TelephonyRegistry push")
+ .setMinSdkVersionForFine(minSdk)
+ .build();
+
+ return Binder.withCleanCallingIdentity(() -> {
+ LocationAccessPolicy.LocationPermissionResult locationResult =
+ LocationAccessPolicy.checkLocationPermission(mContext, query);
+ return locationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
+ });
+ }
+
+ private boolean checkCoarseLocationAccess(Record r, int minSdk) {
+ LocationAccessPolicy.LocationPermissionQuery query =
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(r.callingPackage)
+ .setCallingPid(r.callerPid)
+ .setCallingUid(r.callerUid)
+ .setMethod("TelephonyRegistry push")
+ .setMinSdkVersionForCoarse(minSdk)
+ .build();
+
+ return Binder.withCleanCallingIdentity(() -> {
+ LocationAccessPolicy.LocationPermissionResult locationResult =
+ LocationAccessPolicy.checkLocationPermission(mContext, query);
+ return locationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
+ });
}
private void checkPossibleMissNotify(Record r, int phoneId) {
@@ -2286,7 +2351,7 @@
log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = "
+ mCellInfo.get(phoneId));
}
- if (checkLocationAccess(r)) {
+ if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
}
} catch (RemoteException ex) {
@@ -2336,7 +2401,7 @@
try {
if (DBG_LOC) log("checkPossibleMissNotify: onCellLocationChanged mCellLocation = "
+ mCellLocation[phoneId]);
- if (checkLocationAccess(r)) {
+ if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId]));
}
} catch (RemoteException ex) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c4a9db6..2f20572 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18100,8 +18100,10 @@
if (!queue.isIdle()) {
final String msg = "Waiting for queue " + queue + " to become idle...";
pw.println(msg);
+ pw.println(queue.describeState());
pw.flush();
Slog.v(TAG, msg);
+ queue.cancelDeferrals();
idle = false;
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastDispatcher.java b/services/core/java/com/android/server/am/BroadcastDispatcher.java
index 6371cd3..0b38ef9 100644
--- a/services/core/java/com/android/server/am/BroadcastDispatcher.java
+++ b/services/core/java/com/android/server/am/BroadcastDispatcher.java
@@ -65,6 +65,14 @@
broadcasts.add(br);
}
+ int size() {
+ return broadcasts.size();
+ }
+
+ boolean isEmpty() {
+ return broadcasts.isEmpty();
+ }
+
void writeToProto(ProtoOutputStream proto, long fieldId) {
for (BroadcastRecord br : broadcasts) {
br.writeToProto(proto, fieldId);
@@ -252,22 +260,48 @@
synchronized (mLock) {
return mCurrentBroadcast == null
&& mOrderedBroadcasts.isEmpty()
- && mDeferredBroadcasts.isEmpty()
- && mAlarmBroadcasts.isEmpty();
+ && isDeferralsListEmpty(mDeferredBroadcasts)
+ && isDeferralsListEmpty(mAlarmBroadcasts);
}
}
- /**
- * Not quite the traditional size() measurement; includes any in-process but
- * not yet retired active outbound broadcast.
- */
- public int totalUndelivered() {
- synchronized (mLock) {
- return mAlarmBroadcasts.size()
- + mDeferredBroadcasts.size()
- + mOrderedBroadcasts.size()
- + (mCurrentBroadcast == null ? 0 : 1);
+ private static int pendingInDeferralsList(ArrayList<Deferrals> list) {
+ int pending = 0;
+ final int numEntries = list.size();
+ for (int i = 0; i < numEntries; i++) {
+ pending += list.get(i).size();
}
+ return pending;
+ }
+
+ private static boolean isDeferralsListEmpty(ArrayList<Deferrals> list) {
+ return pendingInDeferralsList(list) == 0;
+ }
+
+ /**
+ * Strictly for logging, describe the currently pending contents in a human-
+ * readable way
+ */
+ public String describeStateLocked() {
+ final StringBuilder sb = new StringBuilder(128);
+ if (mCurrentBroadcast != null) {
+ sb.append("1 in flight, ");
+ }
+ sb.append(mOrderedBroadcasts.size());
+ sb.append(" ordered");
+ int n = pendingInDeferralsList(mAlarmBroadcasts);
+ if (n > 0) {
+ sb.append(", ");
+ sb.append(n);
+ sb.append(" deferrals in alarm recipients");
+ }
+ n = pendingInDeferralsList(mDeferredBroadcasts);
+ if (n > 0) {
+ sb.append(", ");
+ sb.append(n);
+ sb.append(" deferred");
+ }
+ return sb.toString();
}
// ----------------------------------
@@ -579,6 +613,26 @@
}
}
+ /**
+ * Cancel all current deferrals; that is, make all currently-deferred broadcasts
+ * immediately deliverable. Used by the wait-for-broadcast-idle mechanism.
+ */
+ public void cancelDeferrals() {
+ synchronized (mLock) {
+ zeroDeferralTimes(mAlarmBroadcasts);
+ zeroDeferralTimes(mDeferredBroadcasts);
+ }
+ }
+
+ private static void zeroDeferralTimes(ArrayList<Deferrals> list) {
+ final int num = list.size();
+ for (int i = 0; i < num; i++) {
+ Deferrals d = list.get(i);
+ // Safe to do this in-place because it won't break ordering
+ d.deferUntil = d.deferredBy = 0;
+ }
+ }
+
// ----------------------------------
/**
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index f0b137a..d9ea1da 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -898,6 +898,11 @@
for (int i = perms.length-1; i >= 0; i--) {
try {
PermissionInfo pi = pm.getPermissionInfo(perms[i], "android", 0);
+ if (pi == null) {
+ // a required permission that no package has actually
+ // defined cannot be signature-required.
+ return false;
+ }
if ((pi.protectionLevel & (PermissionInfo.PROTECTION_MASK_BASE
| PermissionInfo.PROTECTION_FLAG_PRIVILEGED))
!= PermissionInfo.PROTECTION_SIGNATURE) {
@@ -923,8 +928,8 @@
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast ["
+ mQueueName + "]: "
- + mParallelBroadcasts.size() + " parallel broadcasts, "
- + mDispatcher.totalUndelivered() + " ordered broadcasts");
+ + mParallelBroadcasts.size() + " parallel broadcasts; "
+ + mDispatcher.describeStateLocked());
mService.updateCpuStats();
@@ -1822,11 +1827,24 @@
record.intent == null ? "" : record.intent.getAction());
}
- final boolean isIdle() {
+ boolean isIdle() {
return mParallelBroadcasts.isEmpty() && mDispatcher.isEmpty()
&& (mPendingBroadcast == null);
}
+ // Used by wait-for-broadcast-idle : fast-forward all current deferrals to
+ // be immediately deliverable.
+ void cancelDeferrals() {
+ mDispatcher.cancelDeferrals();
+ }
+
+ String describeState() {
+ synchronized (mService) {
+ return mParallelBroadcasts.size() + " parallel; "
+ + mDispatcher.describeStateLocked();
+ }
+ }
+
void writeToProto(ProtoOutputStream proto, long fieldId) {
long token = proto.start(fieldId);
proto.write(BroadcastQueueProto.QUEUE_NAME, mQueueName);
diff --git a/services/core/java/com/android/server/gpu/GpuService.java b/services/core/java/com/android/server/gpu/GpuService.java
index a68ceed..6899c3f 100644
--- a/services/core/java/com/android/server/gpu/GpuService.java
+++ b/services/core/java/com/android/server/gpu/GpuService.java
@@ -22,24 +22,33 @@
import android.annotation.NonNull;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.gamedriver.GameDriverProto.Blacklist;
+import android.gamedriver.GameDriverProto.Blacklists;
import android.net.Uri;
import android.os.Build;
+import android.os.Handler;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.Base64;
import android.util.Slog;
+import com.android.framework.protobuf.InvalidProtocolBufferException;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.SystemService;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
+import java.util.List;
/**
* Service to manage GPU related features.
@@ -52,17 +61,25 @@
public static final boolean DEBUG = false;
private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
- private static final String WHITELIST_FILENAME = "whitelist.txt";
+ private static final String GAME_DRIVER_WHITELIST_FILENAME = "whitelist.txt";
+ private static final int BASE64_FLAGS = Base64.NO_PADDING | Base64.NO_WRAP;
private final Context mContext;
private final String mDriverPackageName;
private final PackageManager mPackageManager;
+ private final Object mLock = new Object();
+ private ContentResolver mContentResolver;
+ private long mGameDriverVersionCode;
+ private SettingsObserver mSettingsObserver;
+ @GuardedBy("mLock")
+ private Blacklists mBlacklists;
public GpuService(Context context) {
super(context);
mContext = context;
mDriverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER);
+ mGameDriverVersionCode = -1;
mPackageManager = context.getPackageManager();
if (mDriverPackageName != null && !mDriverPackageName.isEmpty()) {
final IntentFilter packageFilter = new IntentFilter();
@@ -82,10 +99,37 @@
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_BOOT_COMPLETED) {
+ mContentResolver = mContext.getContentResolver();
+ mSettingsObserver = new SettingsObserver();
if (mDriverPackageName == null || mDriverPackageName.isEmpty()) {
return;
}
fetchGameDriverPackageProperties();
+ processBlacklists();
+ setBlacklist();
+ }
+ }
+
+ private final class SettingsObserver extends ContentObserver {
+ private final Uri mGameDriverBlackUri =
+ Settings.Global.getUriFor(Settings.Global.GAME_DRIVER_BLACKLISTS);
+
+ SettingsObserver() {
+ super(new Handler());
+ mContentResolver.registerContentObserver(mGameDriverBlackUri, false, this,
+ UserHandle.USER_ALL);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (uri == null) {
+ return;
+ }
+
+ if (mGameDriverBlackUri.equals(uri)) {
+ processBlacklists();
+ setBlacklist();
+ }
}
}
@@ -109,6 +153,7 @@
case ACTION_PACKAGE_CHANGED:
case ACTION_PACKAGE_REMOVED:
fetchGameDriverPackageProperties();
+ setBlacklist();
break;
default:
// do nothing
@@ -138,16 +183,22 @@
return;
}
+ // Reset the whitelist.
+ Settings.Global.putString(mContentResolver,
+ Settings.Global.GAME_DRIVER_WHITELIST, "");
+ mGameDriverVersionCode = driverInfo.longVersionCode;
+
try {
final Context driverContext = mContext.createPackageContext(mDriverPackageName,
Context.CONTEXT_RESTRICTED);
final BufferedReader reader = new BufferedReader(
- new InputStreamReader(driverContext.getAssets().open(WHITELIST_FILENAME)));
+ new InputStreamReader(driverContext.getAssets()
+ .open(GAME_DRIVER_WHITELIST_FILENAME)));
final ArrayList<String> whitelistedPackageNames = new ArrayList<>();
for (String packageName; (packageName = reader.readLine()) != null; ) {
whitelistedPackageNames.add(packageName);
}
- Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.putString(mContentResolver,
Settings.Global.GAME_DRIVER_WHITELIST,
String.join(",", whitelistedPackageNames));
} catch (PackageManager.NameNotFoundException e) {
@@ -160,4 +211,48 @@
}
}
}
+
+ private void processBlacklists() {
+ // TODO(b/121350991) Switch to DeviceConfig with property listener.
+ String base64String =
+ Settings.Global.getString(mContentResolver, Settings.Global.GAME_DRIVER_BLACKLISTS);
+ if (base64String == null || base64String.isEmpty()) {
+ return;
+ }
+
+ synchronized (mLock) {
+ // Reset all blacklists
+ mBlacklists = null;
+ try {
+ mBlacklists = Blacklists.parseFrom(Base64.decode(base64String, BASE64_FLAGS));
+ } catch (IllegalArgumentException e) {
+ if (DEBUG) {
+ Slog.w(TAG, "Can't parse blacklist, skip and continue...");
+ }
+ } catch (InvalidProtocolBufferException e) {
+ if (DEBUG) {
+ Slog.w(TAG, "Can't parse blacklist, skip and continue...");
+ }
+ }
+ }
+ }
+
+ private void setBlacklist() {
+ Settings.Global.putString(mContentResolver,
+ Settings.Global.GAME_DRIVER_BLACKLIST, "");
+ synchronized (mLock) {
+ if (mBlacklists == null) {
+ return;
+ }
+ List<Blacklist> blacklists = mBlacklists.getBlacklistsList();
+ for (Blacklist blacklist : blacklists) {
+ if (blacklist.getVersionCode() == mGameDriverVersionCode) {
+ Settings.Global.putString(mContentResolver,
+ Settings.Global.GAME_DRIVER_BLACKLIST,
+ String.join(",", blacklist.getPackageNamesList()));
+ return;
+ }
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 3abacc2..e71b156 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -917,7 +917,7 @@
synchronized (mLock) {
boolean enabled =
((mProviderRequest != null && mProviderRequest.reportLocation
- && mProviderRequest.forceLocation) || (
+ && mProviderRequest.locationSettingsIgnored) || (
mContext.getSystemService(LocationManager.class).isLocationEnabled()
&& !mDisableGps)) && !mShutdown;
if (enabled == mEnabled) {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index b6ef180..b221241 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -1207,6 +1207,15 @@
}
}
+ public void setPlaybackSpeed(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, float speed) {
+ try {
+ mCb.notifySetPlaybackSpeed(packageName, pid, uid, caller, speed);
+ } catch (RuntimeException e) {
+ Slog.e(TAG, "Remote failure in setPlaybackSpeed.", e);
+ }
+ }
+
public void adjustVolume(String packageName, int pid, int uid,
ControllerCallbackLink caller, boolean asSystemService, int direction) {
try {
@@ -1446,6 +1455,13 @@
}
@Override
+ public void setPlaybackSpeed(String packageName, ControllerCallbackLink caller,
+ float speed) {
+ mSessionCb.setPlaybackSpeed(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
+ caller, speed);
+ }
+
+ @Override
public void sendCustomAction(String packageName, ControllerCallbackLink caller,
String action, Bundle args) {
mSessionCb.sendCustomAction(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 9e5b92a..3f15b38 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -17,9 +17,6 @@
package com.android.server.net;
import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
import static android.provider.Settings.ACTION_VPN_SETTINGS;
import android.app.Notification;
@@ -30,17 +27,14 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
-import android.net.LinkProperties;
import android.net.LinkAddress;
+import android.net.LinkProperties;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkInfo.State;
-import android.net.NetworkPolicyManager;
import android.os.INetworkManagementService;
-import android.os.RemoteException;
import android.security.Credentials;
import android.security.KeyStore;
-import android.system.Os;
import android.text.TextUtils;
import android.util.Slog;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 4bd8f45..6d82c1c 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -15,15 +15,15 @@
*/
package com.android.server.net;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
+import static android.net.INetd.FIREWALL_RULE_ALLOW;
+import static android.net.INetd.FIREWALL_RULE_DENY;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
import android.app.ActivityManager;
import android.net.NetworkPolicyManager;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index af55605..75b62cb 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -38,6 +38,11 @@
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
+import static android.net.INetd.FIREWALL_RULE_ALLOW;
+import static android.net.INetd.FIREWALL_RULE_DENY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -45,12 +50,7 @@
import static android.net.NetworkPolicy.SNOOZE_NEVER;
import static android.net.NetworkPolicy.WARNING_DISABLED;
import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS;
import static android.net.NetworkPolicyManager.MASK_METERED_NETWORKS;
import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 400443a..941de89 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3306,7 +3306,8 @@
// feature flags should cause us to invalidate any caches.
final String cacheName = SystemProperties.digestOf(
"ro.build.fingerprint",
- "persist.sys.isolated_storage");
+ StorageManager.PROP_ISOLATED_STORAGE,
+ StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT);
// Reconcile cache directories, keeping only what we'd actually use.
for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) {
@@ -12803,7 +12804,7 @@
public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames,
int restrictionFlags, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
- "setPackagesSuspendedAsUser");
+ "setDistractingPackageRestrictionsAsUser");
final int callingUid = Binder.getCallingUid();
if (callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 30c2281..6c212d6 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -156,12 +156,8 @@
boolean success = true;
// STOPSHIP: TODO(b/123753157): Verify APKs through Package Verifier.
- if (!sessionContainsApex(session)) {
- // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs,
- // right away.
- session.setStagedSessionReady();
- return;
- }
+ // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs,
+ // right away.
final ApexInfoList apexInfoList = new ApexInfoList();
// APEX checks. For single-package sessions, check if they contain an APEX. For
@@ -227,7 +223,8 @@
}
session.setStagedSessionReady();
- if (!mApexManager.markStagedSessionReady(session.sessionId)) {
+ if (sessionContainsApex(session)
+ && !mApexManager.markStagedSessionReady(session.sessionId)) {
session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
"APEX staging failed, check logcat messages from apexd for more "
+ "details.");
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 2036ed7..bd577598 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -721,7 +721,7 @@
grantSystemFixedPermissionsToSystemPackage(
getDefaultSystemHandlerActivityPackage(
RingtoneManager.ACTION_RINGTONE_PICKER, userId),
- userId, STORAGE_PERMISSIONS);
+ userId, STORAGE_PERMISSIONS, MEDIA_AURAL_PERMISSIONS);
// TextClassifier Service
String textClassifierPackageName =
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 95c3f4c..ceaf829 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -40,6 +40,7 @@
import android.os.HandlerThread;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.provider.DeviceConfig;
import android.util.IntArray;
import android.util.Log;
import android.util.SparseBooleanArray;
@@ -61,6 +62,7 @@
import java.util.Map;
import java.util.Random;
import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
/**
* Implementation of service that manages APK level rollbacks.
@@ -71,13 +73,19 @@
// Rollbacks expire after 48 hours.
// TODO: How to test rollback expiration works properly?
- private static final long ROLLBACK_LIFETIME_DURATION_MILLIS = 48 * 60 * 60 * 1000;
+ private static final long DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS =
+ TimeUnit.HOURS.toMillis(48);
// Lock used to synchronize accesses to in-memory rollback data
// structures. By convention, methods with the suffix "Locked" require
// mLock is held when they are called.
private final Object mLock = new Object();
+ // No need for guarding with lock because value is only accessed in handler thread
+ // and the value will be written on boot complete. Initialization here happens before
+ // handler threads are running so that's fine.
+ private long mRollbackLifetimeDurationInMillis = DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS;
+
// Used for generating rollback IDs.
private final Random mRandom = new SecureRandom();
@@ -484,7 +492,25 @@
});
}
+ private void updateRollbackLifetimeDurationInMillis() {
+ String strRollbackLifetimeInMillis = DeviceConfig.getProperty(
+ DeviceConfig.Rollback.BOOT_NAMESPACE,
+ DeviceConfig.Rollback.ROLLBACK_LIFETIME_IN_MILLIS);
+
+ try {
+ mRollbackLifetimeDurationInMillis = (strRollbackLifetimeInMillis == null)
+ ? DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS
+ : Long.parseLong(strRollbackLifetimeInMillis);
+ } catch (NumberFormatException e) {
+ mRollbackLifetimeDurationInMillis = DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS;
+ }
+ }
+
void onBootCompleted() {
+ getHandler().post(() -> updateRollbackLifetimeDurationInMillis());
+ // Also posts to handler thread
+ scheduleExpiration(0);
+
getHandler().post(() -> {
// Check to see if any staged sessions with rollback enabled have
// been applied.
@@ -565,8 +591,6 @@
for (RollbackInfo info : mRecentlyExecutedRollbacks) {
mAllocatedRollbackIds.put(info.getRollbackId(), true);
}
-
- scheduleExpiration(0);
}
/**
@@ -700,8 +724,7 @@
if (!data.isAvailable) {
continue;
}
-
- if (!now.isBefore(data.timestamp.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS))) {
+ if (!now.isBefore(data.timestamp.plusMillis(mRollbackLifetimeDurationInMillis))) {
iter.remove();
deleteRollback(data);
} else if (oldest == null || oldest.isAfter(data.timestamp)) {
@@ -711,7 +734,7 @@
}
if (oldest != null) {
- scheduleExpiration(now.until(oldest.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS),
+ scheduleExpiration(now.until(oldest.plusMillis(mRollbackLifetimeDurationInMillis),
ChronoUnit.MILLIS));
}
}
@@ -1144,8 +1167,8 @@
packages.add(data.packages.get(i).getPackageName());
}
mPackageHealthObserver.startObservingHealth(packages,
- ROLLBACK_LIFETIME_DURATION_MILLIS);
- scheduleExpiration(ROLLBACK_LIFETIME_DURATION_MILLIS);
+ mRollbackLifetimeDurationInMillis);
+ scheduleExpiration(mRollbackLifetimeDurationInMillis);
} catch (IOException e) {
Log.e(TAG, "Unable to enable rollback", e);
deleteRollback(data);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 0251efb..f33c518 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2677,8 +2677,9 @@
* Get the configuration orientation by the requested screen orientation
* ({@link ActivityInfo.ScreenOrientation}) of this activity.
*
- * @return orientation in ({@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT},
- * {@link #ORIENTATION_UNDEFINED}).
+ * @return orientation in ({@link Configuration#ORIENTATION_LANDSCAPE},
+ * {@link Configuration#ORIENTATION_PORTRAIT},
+ * {@link Configuration#ORIENTATION_UNDEFINED}).
*/
int getRequestedConfigurationOrientation() {
final int screenOrientation = getOrientation();
@@ -2936,14 +2937,36 @@
// should be given the aspect ratio.
activityWidth = (int) ((activityHeight * maxAspectRatio) + 0.5f);
}
- } else if (containingRatio < minAspectRatio && minAspectRatio != 0) {
- if (containingAppWidth < containingAppHeight) {
- // Width is the shorter side, so we use the height to figure-out what the max. width
- // should be given the aspect ratio.
+ } else if (containingRatio < minAspectRatio) {
+ boolean adjustWidth;
+ switch (getRequestedConfigurationOrientation()) {
+ case ORIENTATION_LANDSCAPE:
+ // Width should be the longer side for this landscape app, so we use the width
+ // to figure-out what the max. height should be given the aspect ratio.
+ adjustWidth = false;
+ break;
+ case ORIENTATION_PORTRAIT:
+ // Height should be the longer side for this portrait app, so we use the height
+ // to figure-out what the max. width should be given the aspect ratio.
+ adjustWidth = true;
+ break;
+ default:
+ // This app doesn't have a preferred orientation, so we keep the length of the
+ // longer side, and use it to figure-out the length of the shorter side.
+ if (containingAppWidth < containingAppHeight) {
+ // Width is the shorter side, so we use the height to figure-out what the
+ // max. width should be given the aspect ratio.
+ adjustWidth = true;
+ } else {
+ // Height is the shorter side, so we use the width to figure-out what the
+ // max. height should be given the aspect ratio.
+ adjustWidth = false;
+ }
+ break;
+ }
+ if (adjustWidth) {
activityWidth = (int) ((activityHeight / minAspectRatio) + 0.5f);
} else {
- // Height is the shorter side, so we use the width to figure-out what the max.
- // height should be given the aspect ratio.
activityHeight = (int) ((activityWidth / minAspectRatio) + 0.5f);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 5cfc20b..4795555 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1500,8 +1500,8 @@
final int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity;
final int longSizeDp = longSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity;
- mDisplayRotation.configure(width, height, shortSizeDp, longSizeDp);
mDisplayPolicy.configure(width, height, shortSizeDp);
+ mDisplayRotation.configure(width, height, shortSizeDp, longSizeDp);
mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo,
calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 2ee30ac..91d573d 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2617,9 +2617,8 @@
DisplayCutout displayCutout) {
int width = fullWidth;
if (hasNavigationBar()) {
- // For a basic navigation bar, when we are in landscape mode we place
- // the navigation bar to the side.
- if (navigationBarCanMove() && fullWidth > fullHeight) {
+ final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation);
+ if (navBarPosition == NAV_BAR_LEFT || navBarPosition == NAV_BAR_RIGHT) {
width -= getNavigationBarWidth(rotation, uiMode);
}
}
@@ -2646,9 +2645,8 @@
DisplayCutout displayCutout) {
int height = fullHeight;
if (hasNavigationBar()) {
- // For a basic navigation bar, when we are in portrait mode we place
- // the navigation bar to the bottom.
- if (!navigationBarCanMove() || fullWidth < fullHeight) {
+ final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation);
+ if (navBarPosition == NAV_BAR_BOTTOM) {
height -= getNavigationBarHeight(rotation, uiMode);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 5f341ee..543f196 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -38,6 +38,7 @@
import android.provider.Settings;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.DisplayCutout;
import android.view.Surface;
import com.android.internal.annotations.VisibleForTesting;
@@ -70,6 +71,8 @@
private final int mDeskDockRotation;
private final int mUndockedHdmiRotation;
+ private final float mCloseToSquareMaxAspectRatio;
+
private OrientationListener mOrientationListener;
private StatusBarManagerInternal mStatusBarManagerInternal;
private SettingsObserver mSettingsObserver;
@@ -132,6 +135,9 @@
mUndockedHdmiRotation = readRotation(
com.android.internal.R.integer.config_undockedHdmiRotation);
+ mCloseToSquareMaxAspectRatio = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_closeToSquareDisplayMaxAspectRatio);
+
if (isDefaultDisplay) {
final Handler uiHandler = UiThread.getHandler();
mOrientationListener = new OrientationListener(mContext, uiHandler);
@@ -212,10 +218,12 @@
// so if the orientation is forced, we need to respect that no matter what.
final boolean isTv = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_LEANBACK);
+ final boolean isCloseToSquare =
+ isNonDecorDisplayCloseToSquare(Surface.ROTATION_0, width, height);
final boolean forceDefaultOrientationInRes =
res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation);
final boolean forceDefaultOrienation =
- ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv)
+ ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv || isCloseToSquare)
&& forceDefaultOrientationInRes
// For debug purposes the next line turns this feature off with:
// $ adb shell setprop config.override_forced_orient true
@@ -227,6 +235,18 @@
setFixedToUserRotation(forceDefaultOrienation);
}
+ private boolean isNonDecorDisplayCloseToSquare(int rotation, int width, int height) {
+ final DisplayCutout displayCutout =
+ mDisplayContent.calculateDisplayCutoutForRotation(rotation).getDisplayCutout();
+ final int uiMode = mService.mPolicy.getUiMode();
+ final int w = mDisplayPolicy.getNonDecorDisplayWidth(
+ width, height, rotation, uiMode, displayCutout);
+ final int h = mDisplayPolicy.getNonDecorDisplayHeight(
+ width, height, rotation, uiMode, displayCutout);
+ final float aspectRatio = Math.max(w, h) / (float) Math.min(w, h);
+ return aspectRatio <= mCloseToSquareMaxAspectRatio;
+ }
+
void setRotation(int rotation) {
if (mOrientationListener != null) {
mOrientationListener.setCurrentRotation(rotation);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c747c6d..168c9ad 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -446,7 +446,8 @@
final boolean mLimitedAlphaCompositing;
final int mMaxUiWidth;
- final WindowManagerPolicy mPolicy;
+ @VisibleForTesting
+ WindowManagerPolicy mPolicy;
final IActivityManager mActivityManager;
// TODO: Probably not needed once activities are fully in WM.
@@ -4263,9 +4264,12 @@
if (mMaxUiWidth > 0) {
mRoot.forAllDisplays(displayContent -> displayContent.setMaxUiWidth(mMaxUiWidth));
}
- applyForcedPropertiesForDefaultDisplay();
+ final boolean changed = applyForcedPropertiesForDefaultDisplay();
mAnimator.ready();
mDisplayReady = true;
+ if (changed) {
+ reconfigureDisplayLocked(getDefaultDisplayContentLocked());
+ }
mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TOUCHSCREEN);
}
@@ -4865,7 +4869,8 @@
}
/** The global settings only apply to default display. */
- private void applyForcedPropertiesForDefaultDisplay() {
+ private boolean applyForcedPropertiesForDefaultDisplay() {
+ boolean changed = false;
final DisplayContent displayContent = getDefaultDisplayContentLocked();
// Display size.
String sizeStr = Settings.Global.getString(mContext.getContentResolver(),
@@ -4885,6 +4890,7 @@
Slog.i(TAG_WM, "FORCED DISPLAY SIZE: " + width + "x" + height);
displayContent.updateBaseDisplayMetrics(width, height,
displayContent.mBaseDisplayDensity);
+ changed = true;
}
} catch (NumberFormatException ex) {
}
@@ -4893,17 +4899,20 @@
// Display density.
final int density = getForcedDisplayDensityForUserLocked(mCurrentUserId);
- if (density != 0) {
+ if (density != 0 && density != displayContent.mBaseDisplayDensity) {
displayContent.mBaseDisplayDensity = density;
+ changed = true;
}
// Display scaling mode.
int mode = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DISPLAY_SCALING_FORCE, 0);
- if (mode != 0) {
+ if (displayContent.mDisplayScalingDisabled != (mode != 0)) {
Slog.i(TAG_WM, "FORCED DISPLAY SCALING DISABLED");
displayContent.mDisplayScalingDisabled = true;
+ changed = true;
}
+ return changed;
}
@Override
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 638ec95..9946cc3 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -1,6 +1,9 @@
java_library_static {
name: "services.net",
srcs: ["java/**/*.java"],
+ static_libs: [
+ "netd_aidl_interface-java",
+ ]
}
filegroup {
diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java
index 4bac200..ebbebcb 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java
@@ -16,12 +16,12 @@
package com.android.server;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
+import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
+import static android.net.INetd.FIREWALL_RULE_ALLOW;
+import static android.net.INetd.FIREWALL_RULE_DENY;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
import static android.util.DebugUtils.valueToString;
import static org.junit.Assert.assertEquals;
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index a7520dc..2627ec7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -30,6 +30,7 @@
import android.graphics.Rect;
import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
@@ -48,6 +49,7 @@
* atest WmTests:AppChangeTransitionTests
*/
@SmallTest
+@Presubmit
public class AppChangeTransitionTests extends WindowTestsBase {
private TaskStack mStack;
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index cd13209..1dd72ec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -51,7 +51,6 @@
import android.view.Surface;
import android.view.WindowManager;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.Before;
@@ -142,7 +141,6 @@
mToken.removeImmediately();
}
- @FlakyTest(detail = "Promote to presubmit when shown to be stable.")
@Test
public void testLandscapeSeascapeRotationByApp() {
// Some plumbing to get the service ready for rotation updates.
@@ -303,7 +301,6 @@
}
@Test
- @FlakyTest(detail = "Promote once confirmed non-flaky")
public void testStuckExitingWindow() {
final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
"closingWindow");
@@ -346,7 +343,6 @@
assertNoStartingWindow(mToken);
}
- @FlakyTest(detail = "Promote to presubmit when shown to be stable.")
@Test
public void testAddRemoveRace() {
// There was once a race condition between adding and removing starting windows
diff --git a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
index 3f83cae..1e02a12 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
@@ -46,6 +46,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.platform.test.annotations.Presubmit;
import android.util.Log;
import android.view.IWindowManager;
@@ -71,6 +72,7 @@
* atest WmTests:AssistDataRequesterTest
*/
@MediumTest
+@Presubmit
public class AssistDataRequesterTest extends ActivityTestsBase {
private static final String TAG = AssistDataRequesterTest.class.getSimpleName();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index 198e7ce..b15e99a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -60,6 +60,7 @@
import com.android.server.UiThread;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.wm.utils.WmDisplayCutout;
import org.junit.After;
import org.junit.Before;
@@ -113,6 +114,7 @@
public static void setUpOnce() {
sMockWm = mock(WindowManagerService.class);
sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
+ sMockWm.mPolicy = mock(WindowManagerPolicy.class);
}
@Before
@@ -807,6 +809,8 @@
mMockDisplayContent = mock(WindowTestUtils.TestDisplayContent.class);
mMockDisplayContent.isDefaultDisplay = mIsDefaultDisplay;
+ when(mMockDisplayContent.calculateDisplayCutoutForRotation(anyInt()))
+ .thenReturn(WmDisplayCutout.NO_CUTOUT);
mMockDisplayPolicy = mock(DisplayPolicy.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java b/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java
index ce22788..df26679 100644
--- a/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java
@@ -34,6 +34,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
import android.util.SparseBooleanArray;
import com.android.server.wm.LockTaskController.LockTaskToken;
@@ -43,6 +44,7 @@
import java.lang.reflect.Constructor;
+@Presubmit
public class KeyguardDisableHandlerTest {
private KeyguardDisableHandler mKeyguardDisable;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index cc6a58a..a03d28b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -40,7 +40,6 @@
import android.view.IRecentsAnimationRunner;
import android.view.SurfaceControl;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
@@ -112,7 +111,6 @@
}
}
- @FlakyTest(bugId = 117117823)
@Test
public void testIncludedApps_expectTargetAndVisible() {
mWm.setRecentsAnimationController(mController);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
index c595868..2377df4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
@@ -34,10 +34,12 @@
import android.app.IActivityTaskManager;
import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.Before;
@@ -50,6 +52,8 @@
* atest WmTests:TaskPositionerTests
*/
@SmallTest
+@Presubmit
+@FlakyTest
public class TaskPositionerTests extends WindowTestsBase {
private static final boolean DEBUGGING = false;
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
index 649b785..99ceb20 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
@@ -31,6 +31,7 @@
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.PointF;
+import android.platform.test.annotations.Presubmit;
import android.view.DisplayInfo;
import org.junit.Before;
@@ -42,6 +43,7 @@
* Build/Install/Run:
* atest WmTests:CoordinateTransformsTest
*/
+@Presubmit
public class CoordinateTransformsTest {
private static final int W = 200;
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index e99a289..d509168 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -291,6 +291,19 @@
"android.telecom.extra.OUTGOING_CALL_EXTRAS";
/**
+ * An optional boolean extra on {@link android.content.Intent#ACTION_CALL_EMERGENCY} to tell
+ * whether the user's dial intent is emergency; this is required to specify when the dialed
+ * number is ambiguous, identified as both emergency number and any other non-emergency number;
+ * e.g. in some situation, 611 could be both an emergency number in a country and a
+ * non-emergency number of a carrier's customer service hotline.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_IS_USER_INTENT_EMERGENCY_CALL =
+ "android.telecom.extra.IS_USER_INTENT_EMERGENCY_CALL";
+
+ /**
* @hide
*/
public static final String EXTRA_UNKNOWN_CALL_HANDLE =
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 3814333..dba437a 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -141,6 +141,14 @@
return mCpid;
}
+ /**
+ * @return 16-bit UMTS Absolute RF Channel Number,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+ */
+ public int getUarfcn() {
+ return mUarfcn;
+ }
+
/** @hide */
@Override
public int getChannelNumber() {
diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/java/android/telephony/LocationAccessPolicy.java
index 53d69f4..24db438 100644
--- a/telephony/java/android/telephony/LocationAccessPolicy.java
+++ b/telephony/java/android/telephony/LocationAccessPolicy.java
@@ -26,11 +26,12 @@
import android.content.pm.UserInfo;
import android.location.LocationManager;
import android.os.Binder;
+import android.os.Build;
import android.os.Process;
-import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
+import android.widget.Toast;
import java.util.List;
@@ -41,59 +42,234 @@
public final class LocationAccessPolicy {
private static final String TAG = "LocationAccessPolicy";
private static final boolean DBG = false;
+ public static final int MAX_SDK_FOR_ANY_ENFORCEMENT = Build.VERSION_CODES.P;
- /**
- * API to determine if the caller has permissions to get cell location.
- *
- * @param pkgName Package name of the application requesting access
- * @param uid The uid of the package
- * @param pid The pid of the package
- * @param throwOnDeniedPermission Whether to throw if the location permission is denied.
- * @return boolean true or false if permissions is granted
- */
- public static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName,
- int uid, int pid, boolean throwOnDeniedPermission) throws SecurityException {
- Trace.beginSection("TelephonyLocationCheck");
- try {
- // Always allow the phone process and system server to access location. This avoid
- // breaking legacy code that rely on public-facing APIs to access cell location, and
- // it doesn't create an info leak risk because the cell location is stored in the phone
- // process anyway, and the system server already has location access.
- if (uid == Process.PHONE_UID || uid == Process.SYSTEM_UID || uid == Process.ROOT_UID) {
- return true;
- }
+ public enum LocationPermissionResult {
+ ALLOWED,
+ /**
+ * Indicates that the denial is due to a transient device state
+ * (e.g. app-ops, location master switch)
+ */
+ DENIED_SOFT,
+ /**
+ * Indicates that the denial is due to a misconfigured app (e.g. missing entry in manifest)
+ */
+ DENIED_HARD,
+ }
- // We always require the location permission and also require the
- // location mode to be on for non-legacy apps. Legacy apps are
- // required to be in the foreground to at least mitigate the case
- // where a legacy app the user is not using tracks their location.
- // Granting ACCESS_FINE_LOCATION to an app automatically grants it
- // ACCESS_COARSE_LOCATION.
- if (throwOnDeniedPermission) {
- context.enforcePermission(Manifest.permission.ACCESS_COARSE_LOCATION,
- pid, uid, "canAccessCellLocation");
- } else if (context.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION,
- pid, uid) == PackageManager.PERMISSION_DENIED) {
- if (DBG) Log.w(TAG, "Permission checked failed (" + pid + "," + uid + ")");
- return false;
- }
- final int opCode = AppOpsManager.permissionToOpCode(
- Manifest.permission.ACCESS_COARSE_LOCATION);
- if (opCode != AppOpsManager.OP_NONE && context.getSystemService(AppOpsManager.class)
- .noteOpNoThrow(opCode, uid, pkgName) != AppOpsManager.MODE_ALLOWED) {
- if (DBG) Log.w(TAG, "AppOp check failed (" + uid + "," + pkgName + ")");
- return false;
- }
- if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))) {
- if (DBG) Log.w(TAG, "Location disabled, failed, (" + uid + ")");
- return false;
- }
- // If the user or profile is current, permission is granted.
- // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
- return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context);
- } finally {
- Trace.endSection();
+ public static class LocationPermissionQuery {
+ public final String callingPackage;
+ public final int callingUid;
+ public final int callingPid;
+ public final int minSdkVersionForCoarse;
+ public final int minSdkVersionForFine;
+ public final String method;
+
+ private LocationPermissionQuery(String callingPackage, int callingUid, int callingPid,
+ int minSdkVersionForCoarse, int minSdkVersionForFine, String method) {
+ this.callingPackage = callingPackage;
+ this.callingUid = callingUid;
+ this.callingPid = callingPid;
+ this.minSdkVersionForCoarse = minSdkVersionForCoarse;
+ this.minSdkVersionForFine = minSdkVersionForFine;
+ this.method = method;
}
+
+ public static class Builder {
+ private String mCallingPackage;
+ private int mCallingUid;
+ private int mCallingPid;
+ private int mMinSdkVersionForCoarse = Integer.MAX_VALUE;
+ private int mMinSdkVersionForFine = Integer.MAX_VALUE;
+ private String mMethod;
+
+ /**
+ * Mandatory parameter, used for performing permission checks.
+ */
+ public Builder setCallingPackage(String callingPackage) {
+ mCallingPackage = callingPackage;
+ return this;
+ }
+
+ /**
+ * Mandatory parameter, used for performing permission checks.
+ */
+ public Builder setCallingUid(int callingUid) {
+ mCallingUid = callingUid;
+ return this;
+ }
+
+ /**
+ * Mandatory parameter, used for performing permission checks.
+ */
+ public Builder setCallingPid(int callingPid) {
+ mCallingPid = callingPid;
+ return this;
+ }
+
+ /**
+ * Apps that target at least this sdk version will be checked for coarse location
+ * permission. Defaults to INT_MAX (which means don't check)
+ */
+ public Builder setMinSdkVersionForCoarse(
+ int minSdkVersionForCoarse) {
+ mMinSdkVersionForCoarse = minSdkVersionForCoarse;
+ return this;
+ }
+
+ /**
+ * Apps that target at least this sdk version will be checked for fine location
+ * permission. Defaults to INT_MAX (which means don't check)
+ */
+ public Builder setMinSdkVersionForFine(
+ int minSdkVersionForFine) {
+ mMinSdkVersionForFine = minSdkVersionForFine;
+ return this;
+ }
+
+ /**
+ * Optional, for logging purposes only.
+ */
+ public Builder setMethod(String method) {
+ mMethod = method;
+ return this;
+ }
+
+ public LocationPermissionQuery build() {
+ return new LocationPermissionQuery(mCallingPackage, mCallingUid,
+ mCallingPid, mMinSdkVersionForCoarse, mMinSdkVersionForFine, mMethod);
+ }
+ }
+ }
+
+ private static void logError(Context context, String errorMsg) {
+ Log.e(TAG, errorMsg);
+ try {
+ if (Build.IS_DEBUGGABLE) {
+ Toast.makeText(context, errorMsg, Toast.LENGTH_SHORT).show();
+ }
+ } catch (Throwable t) {
+ // whatever, not important
+ }
+ }
+
+ private static LocationPermissionResult appOpsModeToPermissionResult(int appOpsMode) {
+ switch (appOpsMode) {
+ case AppOpsManager.MODE_ALLOWED:
+ return LocationPermissionResult.ALLOWED;
+ case AppOpsManager.MODE_ERRORED:
+ return LocationPermissionResult.DENIED_HARD;
+ default:
+ return LocationPermissionResult.DENIED_SOFT;
+ }
+ }
+
+ private static LocationPermissionResult checkAppLocationPermissionHelper(Context context,
+ LocationPermissionQuery query, String permissionToCheck) {
+ String locationTypeForLog =
+ Manifest.permission.ACCESS_FINE_LOCATION.equals(permissionToCheck)
+ ? "fine" : "coarse";
+
+ // Do the app-ops and the manifest check without any of the allow-overrides first.
+ boolean hasManifestPermission = checkManifestPermission(context, query.callingPid,
+ query.callingUid, permissionToCheck);
+
+ int appOpMode = context.getSystemService(AppOpsManager.class)
+ .noteOpNoThrow(AppOpsManager.permissionToOpCode(permissionToCheck),
+ query.callingUid, query.callingPackage);
+
+ if (hasManifestPermission && appOpMode == AppOpsManager.MODE_ALLOWED) {
+ // If the app did everything right, return without logging.
+ return LocationPermissionResult.ALLOWED;
+ }
+
+ // If the app has the manifest permission but not the app-op permission, it means that
+ // it's aware of the requirement and the user denied permission explicitly. If we see
+ // this, don't let any of the overrides happen.
+ if (hasManifestPermission) {
+ Log.i(TAG, query.callingPackage + " is aware of " + locationTypeForLog + " but the"
+ + " app-ops permission is specifically denied.");
+ return appOpsModeToPermissionResult(appOpMode);
+ }
+
+ int minSdkVersion = Manifest.permission.ACCESS_FINE_LOCATION.equals(permissionToCheck)
+ ? query.minSdkVersionForFine : query.minSdkVersionForCoarse;
+
+ // If the app fails for some reason, see if it should be allowed to proceed.
+ if (minSdkVersion > MAX_SDK_FOR_ANY_ENFORCEMENT) {
+ String errorMsg = "Allowing " + query.callingPackage + " " + locationTypeForLog
+ + " because we're not enforcing API " + query.minSdkVersionForFine + " yet."
+ + " Please fix this app because it will break in the future. Called from "
+ + query.method;
+ logError(context, errorMsg);
+ return null;
+ } else if (!isAppAtLeastSdkVersion(context, query.callingPackage, minSdkVersion)) {
+ String errorMsg = "Allowing " + query.callingPackage + " " + locationTypeForLog
+ + " because it doesn't target API " + query.minSdkVersionForFine + " yet."
+ + " Please fix this app. Called from " + query.method;
+ logError(context, errorMsg);
+ return null;
+ } else {
+ // If we're not allowing it due to the above two conditions, this means that the app
+ // did not declare the permission in their manifest.
+ return LocationPermissionResult.DENIED_HARD;
+ }
+ }
+
+ public static LocationPermissionResult checkLocationPermission(
+ Context context, LocationPermissionQuery query) {
+ // Always allow the phone process and system server to access location. This avoid
+ // breaking legacy code that rely on public-facing APIs to access cell location, and
+ // it doesn't create an info leak risk because the cell location is stored in the phone
+ // process anyway, and the system server already has location access.
+ if (query.callingUid == Process.PHONE_UID || query.callingUid == Process.SYSTEM_UID
+ || query.callingUid == Process.ROOT_UID) {
+ return LocationPermissionResult.ALLOWED;
+ }
+
+ // Check the system-wide requirements. If the location master switch is off or
+ // the app's profile isn't in foreground, return a soft denial.
+ if (!checkSystemLocationAccess(context, query.callingUid, query.callingPid)) {
+ return LocationPermissionResult.DENIED_SOFT;
+ }
+
+ // Do the check for fine, then for coarse.
+ if (query.minSdkVersionForFine < Integer.MAX_VALUE) {
+ LocationPermissionResult resultForFine = checkAppLocationPermissionHelper(
+ context, query, Manifest.permission.ACCESS_FINE_LOCATION);
+ if (resultForFine != null) {
+ return resultForFine;
+ }
+ }
+
+ if (query.minSdkVersionForCoarse < Integer.MAX_VALUE) {
+ LocationPermissionResult resultForCoarse = checkAppLocationPermissionHelper(
+ context, query, Manifest.permission.ACCESS_COARSE_LOCATION);
+ if (resultForCoarse != null) {
+ return resultForCoarse;
+ }
+ }
+
+ // At this point, we're out of location checks to do. If the app bypassed all the previous
+ // ones due to the SDK grandfathering schemes, allow it access.
+ return LocationPermissionResult.ALLOWED;
+ }
+
+
+ private static boolean checkManifestPermission(Context context, int pid, int uid,
+ String permissionToCheck) {
+ return context.checkPermission(permissionToCheck, pid, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private static boolean checkSystemLocationAccess(@NonNull Context context, int uid, int pid) {
+ if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))) {
+ if (DBG) Log.w(TAG, "Location disabled, failed, (" + uid + ")");
+ return false;
+ }
+ // If the user or profile is current, permission is granted.
+ // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
+ return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context, uid, pid);
}
private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
@@ -105,10 +281,10 @@
return locationManager.isLocationEnabledForUser(UserHandle.of(userId));
}
- private static boolean checkInteractAcrossUsersFull(@NonNull Context context) {
- return context.checkCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
- == PackageManager.PERMISSION_GRANTED;
+ private static boolean checkInteractAcrossUsersFull(
+ @NonNull Context context, int pid, int uid) {
+ return checkManifestPermission(context, pid, uid,
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL);
}
private static boolean isCurrentProfile(@NonNull Context context, int uid) {
@@ -132,4 +308,18 @@
Binder.restoreCallingIdentity(token);
}
}
-}
+
+ private static boolean isAppAtLeastSdkVersion(Context context, String pkgName, int sdkVersion) {
+ try {
+ if (context.getPackageManager().getApplicationInfo(pkgName, 0).targetSdkVersion
+ >= sdkVersion) {
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // In case of exception, assume known app (more strict checking)
+ // Note: This case will never happen since checkPackage is
+ // called to verify validity before checking app's version.
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
index c37b492..6e6d59e 100644
--- a/telephony/java/android/telephony/NetworkRegistrationState.java
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -152,7 +152,7 @@
private final int[] mAvailableServices;
@Nullable
- private final CellIdentity mCellIdentity;
+ private CellIdentity mCellIdentity;
@Nullable
private VoiceSpecificRegistrationStates mVoiceSpecificStates;
@@ -521,4 +521,22 @@
return new NetworkRegistrationState[size];
}
};
+
+ /**
+ * @hide
+ */
+ public NetworkRegistrationState sanitizeLocationInfo() {
+ NetworkRegistrationState result = copy();
+ result.mCellIdentity = null;
+ return result;
+ }
+
+ private NetworkRegistrationState copy() {
+ Parcel p = Parcel.obtain();
+ this.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ NetworkRegistrationState result = new NetworkRegistrationState(p);
+ p.recycle();
+ return result;
+ }
}
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 2c9ba1d..3ce646c 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -46,7 +46,7 @@
* Override the methods for the state that you wish to receive updates for, and
* pass your PhoneStateListener object, along with bitwise-or of the LISTEN_
* flags to {@link TelephonyManager#listen TelephonyManager.listen()}. Methods are
- * called when the state changes, os well as once on initial registration.
+ * called when the state changes, as well as once on initial registration.
* <p>
* Note that access to some telephony information is
* permission-protected. Your application won't receive updates for protected
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 402763e52..a1aee6d 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -36,6 +36,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import java.util.stream.Collectors;
/**
* Contains phone state and service related information.
@@ -1885,4 +1886,29 @@
? range1
: range2;
}
+
+ /**
+ * Returns a copy of self with location-identifying information removed.
+ * Always clears the NetworkRegistrationState's CellIdentity fields, but if removeCoarseLocation
+ * is true, clears other info as well.
+ * @hide
+ */
+ public ServiceState sanitizeLocationInfo(boolean removeCoarseLocation) {
+ ServiceState state = new ServiceState(this);
+ if (state.mNetworkRegistrationStates != null) {
+ state.mNetworkRegistrationStates = state.mNetworkRegistrationStates.stream()
+ .map(NetworkRegistrationState::sanitizeLocationInfo)
+ .collect(Collectors.toList());
+ }
+ if (!removeCoarseLocation) return state;
+
+ state.mDataOperatorAlphaLong = null;
+ state.mDataOperatorAlphaShort = null;
+ state.mDataOperatorNumeric = null;
+ state.mVoiceOperatorAlphaLong = null;
+ state.mVoiceOperatorAlphaShort = null;
+ state.mVoiceOperatorNumeric = null;
+
+ return state;
+ }
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 3a4d33c..836a50b 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -866,7 +866,8 @@
}
/**
- * Callback invoked when there is any change to any SubscriptionInfo. Typically
+ * Callback invoked when there is any change to any SubscriptionInfo, as well as once on
+ * registering for changes with {@link #addOnSubscriptionsChangedListener}. Typically
* this method would invoke {@link #getActiveSubscriptionInfoList}
*/
public void onSubscriptionsChanged() {
@@ -918,7 +919,9 @@
/**
* Register for changes to the list of active {@link SubscriptionInfo} records or to the
* individual records themselves. When a change occurs the onSubscriptionsChanged method of
- * the listener will be invoked immediately if there has been a notification.
+ * the listener will be invoked immediately if there has been a notification. The
+ * onSubscriptionChanged method will also be triggered once initially when calling this
+ * function.
*
* @param listener an instance of {@link OnSubscriptionsChangedListener} with
* onSubscriptionsChanged overridden.
@@ -1861,7 +1864,7 @@
iSub.setDefaultSmsSubId(subscriptionId);
}
} catch (RemoteException ex) {
- // ignore it
+ ex.rethrowFromSystemServer();
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f9cf253..ced4f4a 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1698,10 +1698,7 @@
* @deprecated use {@link #getAllCellInfo} instead, which returns a superset of this API.
*/
@Deprecated
- @RequiresPermission(anyOf = {
- android.Manifest.permission.ACCESS_COARSE_LOCATION,
- android.Manifest.permission.ACCESS_FINE_LOCATION
- })
+ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
public CellLocation getCellLocation() {
try {
ITelephony telephony = getITelephony();
@@ -4951,7 +4948,7 @@
* @return List of {@link android.telephony.CellInfo}; null if cell
* information is unavailable.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
public List<CellInfo> getAllCellInfo() {
try {
ITelephony telephony = getITelephony();
@@ -5028,7 +5025,7 @@
* @param executor the executor on which callback will be invoked.
* @param callback a callback to receive CellInfo.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
public void requestCellInfoUpdate(
@NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
try {
@@ -5067,7 +5064,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(allOf = {android.Manifest.permission.ACCESS_COARSE_LOCATION,
+ @RequiresPermission(allOf = {android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.MODIFY_PHONE_STATE})
public void requestCellInfoUpdate(@NonNull WorkSource workSource,
@NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
@@ -6657,9 +6654,10 @@
*
* <p> Note that this scan can take a long time (sometimes minutes) to happen.
*
- * <p>Requires Permission:
+ * <p>Requires Permissions:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE} or that the calling app has carrier
* privileges (see {@link #hasCarrierPrivileges})
+ * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
*
* @return {@link CellNetworkScanResult} with the status
* {@link CellNetworkScanResult#STATUS_SUCCESS} and a list of
@@ -6668,12 +6666,15 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ })
public CellNetworkScanResult getAvailableNetworks() {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getCellNetworkScanResults(getSubId());
+ return telephony.getCellNetworkScanResults(getSubId(), getOpPackageName());
}
} catch (RemoteException ex) {
Rlog.e(TAG, "getAvailableNetworks RemoteException", ex);
@@ -6692,7 +6693,8 @@
*
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
- * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ * app has carrier privileges (see {@link #hasCarrierPrivileges})
+ * and {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
*
* @param request Contains all the RAT with bands/channels that need to be scanned.
* @param executor The executor through which the callback should be invoked. Since the scan
@@ -6703,7 +6705,10 @@
* @return A NetworkScan obj which contains a callback which can be used to stop the scan.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ })
public NetworkScan requestNetworkScan(
NetworkScanRequest request, Executor executor,
TelephonyScanManager.NetworkScanCallback callback) {
@@ -6712,7 +6717,8 @@
mTelephonyScanManager = new TelephonyScanManager();
}
}
- return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback);
+ return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback,
+ getOpPackageName());
}
/**
@@ -6722,7 +6728,10 @@
* @removed
*/
@Deprecated
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ })
public NetworkScan requestNetworkScan(
NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) {
return requestNetworkScan(request, AsyncTask.SERIAL_EXECUTOR, callback);
@@ -8723,10 +8732,14 @@
* given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
*
* <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
- * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges})
+ * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @RequiresPermission(allOf = {
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ })
public ServiceState getServiceState() {
return getServiceStateForSubscriber(getSubId());
}
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 96ff332..91f74b8 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -29,14 +29,14 @@
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.util.Log;
import android.util.SparseArray;
+
+import com.android.internal.telephony.ITelephony;
+
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
-import com.android.internal.telephony.ITelephony;
-
/**
* Manages the radio access network scan requests and callbacks.
*/
@@ -183,6 +183,7 @@
*
* <p>
* Requires Permission:
+ * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
* Or the calling app has carrier privileges. @see #hasCarrierPrivileges
*
@@ -192,11 +193,13 @@
* @hide
*/
public NetworkScan requestNetworkScan(int subId,
- NetworkScanRequest request, Executor executor, NetworkScanCallback callback) {
+ NetworkScanRequest request, Executor executor, NetworkScanCallback callback,
+ String callingPackage) {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- int scanId = telephony.requestNetworkScan(subId, request, mMessenger, new Binder());
+ int scanId = telephony.requestNetworkScan(
+ subId, request, mMessenger, new Binder(), callingPackage);
saveScanInfo(scanId, request, executor, callback);
return new NetworkScan(scanId, subId);
}
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 59167b7..73f0556 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -350,6 +350,9 @@
/** Indicates if the call is for testing purpose */
private boolean mEmergencyCallTesting = false;
+ /** Indicates if we have known the intent of the user for the call is emergency */
+ private boolean mHasKnownUserIntentEmergency = false;
+
/**
* Extras associated with this {@link ImsCallProfile}.
* <p>
@@ -789,12 +792,13 @@
*
* @hide
*/
- public void setEmergencyCallInfo(EmergencyNumber num) {
+ public void setEmergencyCallInfo(EmergencyNumber num, boolean hasKnownUserIntentEmergency) {
setEmergencyServiceCategories(num.getEmergencyServiceCategoryBitmaskInternalDial());
setEmergencyUrns(num.getEmergencyUrns());
setEmergencyCallRouting(num.getEmergencyCallRouting());
setEmergencyCallTesting(num.getEmergencyNumberSourceBitmask()
== EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST);
+ setHasKnownUserIntentEmergency(hasKnownUserIntentEmergency);
}
/**
@@ -860,6 +864,19 @@
}
/**
+ * Set if we have known the user intent of the call is emergency.
+ *
+ * This is only used to specify when the dialed number is ambiguous when it can be identified
+ * as both emergency number and any other non-emergency number; e.g. in some situation, 611
+ * could be both an emergency number in a country and a non-emergency number of a carrier's
+ * customer service hotline.
+ */
+ @VisibleForTesting
+ public void setHasKnownUserIntentEmergency(boolean hasKnownUserIntentEmergency) {
+ mHasKnownUserIntentEmergency = hasKnownUserIntentEmergency;
+ }
+
+ /**
* Get the emergency service categories, only valid if {@link #getServiceType} returns
* {@link #SERVICE_TYPE_EMERGENCY}
*
@@ -916,4 +933,16 @@
public boolean isEmergencyCallTesting() {
return mEmergencyCallTesting;
}
+
+ /**
+ * Checks if we have known the user intent of the call is emergency.
+ *
+ * This is only used to specify when the dialed number is ambiguous when it can be identified
+ * as both emergency number and any other non-emergency number; e.g. in some situation, 611
+ * could be both an emergency number in a country and a non-emergency number of a carrier's
+ * customer service hotline.
+ */
+ public boolean hasKnownUserIntentEmergency() {
+ return mHasKnownUserIntentEmergency;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index caa367f..7089ee5 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -765,7 +765,7 @@
* @param subId the id of the subscription.
* @return CellNetworkScanResult containing status of scan and networks.
*/
- CellNetworkScanResult getCellNetworkScanResults(int subId);
+ CellNetworkScanResult getCellNetworkScanResults(int subId, String callingPackage);
/**
* Perform a radio network scan and return the id of this scan.
@@ -774,10 +774,11 @@
* @param request Defines all the configs for network scan.
* @param messenger Callback messages will be sent using this messenger.
* @param binder the binder object instantiated in TelephonyManager.
+ * @param callingPackage the calling package
* @return An id for this scan.
*/
int requestNetworkScan(int subId, in NetworkScanRequest request, in Messenger messenger,
- in IBinder binder);
+ in IBinder binder, in String callingPackage);
/**
* Stop an existing radio network scan.
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index d5549cc..07b3a97 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -439,6 +439,14 @@
return true;
}
});
+ menu.add("Require unknown permission").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override public boolean onMenuItemClick(MenuItem item) {
+ final Intent intent = new Intent(SLOW_RECEIVER_ACTION);
+ intent.putExtra(SLOW_RECEIVER_EXTRA, 5038);
+ sendOrderedBroadcast(intent, "com.google.android.test.activity.permission.UNDEFINED");
+ return true;
+ }
+ });
menu.add("Stack Doc").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override public boolean onMenuItemClick(MenuItem item) {
ActivityManager.AppTask task = findDocTask();
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index a7c95c7..e57d838 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -3817,11 +3817,14 @@
}
@Test
- public void testNattSocketKeepalives() throws Exception {
+ public void testNattSocketKeepalives_SingleThreadExecutor() throws Exception {
final ExecutorService executorSingleThread = Executors.newSingleThreadExecutor();
doTestNattSocketKeepalivesWithExecutor(executorSingleThread);
executorSingleThread.shutdown();
+ }
+ @Test
+ public void testNattSocketKeepalives_InlineExecutor() throws Exception {
final Executor executorInline = (Runnable r) -> r.run();
doTestNattSocketKeepalivesWithExecutor(executorInline);
}
@@ -3963,6 +3966,7 @@
testSocket2.close();
mWiFiNetworkAgent.disconnect();
+ waitFor(mWiFiNetworkAgent.getDisconnectedCV());
}
@Test
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 73b568e..9a1d942 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -151,10 +151,12 @@
// Represents an overlayable <item> declaration within an <overlayable> tag.
message OverlayableItem {
enum Policy {
- PUBLIC = 0;
- SYSTEM = 1;
- VENDOR = 2;
- PRODUCT = 3;
+ NONE = 0;
+ PUBLIC = 1;
+ SYSTEM = 2;
+ VENDOR = 3;
+ PRODUCT = 4;
+ SIGNATURE = 5;
}
// The location of the <item> declaration in source.
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 488de87..af9fdfb 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -276,14 +276,19 @@
/**
* Returns the service set identifier (SSID) of the current 802.11 network.
+ * <p>
* If the SSID can be decoded as UTF-8, it will be returned surrounded by double
- * quotation marks. Otherwise, it is returned as a string of hex digits. The
- * SSID may be <unknown ssid> if there is no network currently connected,
- * or if the caller has insufficient permissions to access the SSID.
- *
+ * quotation marks. Otherwise, it is returned as a string of hex digits.
+ * The SSID may be
+ * <lt><unknown ssid>, if there is no network currently connected or if the caller has
+ * insufficient permissions to access the SSID.<lt>
+ * </p>
+ * <p>
* Prior to {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method
* always returned the SSID with no quotes around it.
- * @return the SSID
+ * </p>
+ *
+ * @return the SSID.
*/
public String getSSID() {
if (mWifiSsid != null) {
@@ -312,7 +317,13 @@
/**
* Return the basic service set identifier (BSSID) of the current access point.
- * The BSSID may be {@code null} if there is no network currently connected.
+ * <p>
+ * The BSSID may be
+ * <lt>{@code null}, if there is no network currently connected.</lt>
+ * <lt>{@code "02:00:00:00:00:00"}, if the caller has insufficient permissions to access the
+ * BSSID.<lt>
+ * </p>
+ *
* @return the BSSID, in the form of a six-byte MAC address: {@code XX:XX:XX:XX:XX:XX}
*/
public String getBSSID() {
@@ -511,9 +522,13 @@
/**
* Each configured network has a unique small integer ID, used to identify
- * the network when performing operations on the supplicant. This method
- * returns the ID for the currently connected network.
- * @return the network ID, or -1 if there is no currently connected network
+ * the network. This method returns the ID for the currently connected network.
+ * <p>
+ * The networkId may be {@code -1} if there is no currently connected network or if the caller
+ * has insufficient permissions to access the network ID.
+ * </p>
+ *
+ * @return the network ID.
*/
public int getNetworkId() {
return mNetworkId;