Merge "DisplayViewport should only have actual viewports (2/2)"
diff --git a/Android.bp b/Android.bp
index 1b81306..dc9c4d4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -357,6 +357,7 @@
"core/java/android/view/autofill/IAutoFillManagerClient.aidl",
"core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl",
"core/java/android/view/autofill/IAutofillWindowPresenter.aidl",
+ "core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl",
"core/java/android/view/contentcapture/IContentCaptureManager.aidl",
"core/java/android/view/IApplicationToken.aidl",
"core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl",
@@ -387,6 +388,7 @@
"core/java/android/speech/tts/ITextToSpeechService.aidl",
"core/java/com/android/internal/app/IAppOpsActiveCallback.aidl",
"core/java/com/android/internal/app/IAppOpsCallback.aidl",
+ "core/java/com/android/internal/app/IAppOpsNotedCallback.aidl",
"core/java/com/android/internal/app/IAppOpsService.aidl",
"core/java/com/android/internal/app/IBatteryStats.aidl",
"core/java/com/android/internal/app/ISoundTriggerService.aidl",
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 478e4fe..d01e183 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -251,6 +251,7 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/overlay/ExperimentNavigationBarSlim)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/overlay/ExperimentNavigationBarSlim)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/ExperimentNavigationBarSlim)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/SystemUI)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
# ******************************************************************
diff --git a/api/current.txt b/api/current.txt
index 38bf3ab..de4e824 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -24988,8 +24988,9 @@
field public static final int RATING_KEY_BY_USER = 268435457; // 0x10000001
}
- public class MediaMetadataRetriever {
+ public class MediaMetadataRetriever implements java.lang.AutoCloseable {
ctor public MediaMetadataRetriever();
+ method public void close();
method public java.lang.String extractMetadata(int);
method public byte[] getEmbeddedPicture();
method public android.graphics.Bitmap getFrameAtIndex(int, android.media.MediaMetadataRetriever.BitmapParams);
@@ -26076,7 +26077,12 @@
public class ThumbnailUtils {
ctor public ThumbnailUtils();
- method public static android.graphics.Bitmap createVideoThumbnail(java.lang.String, int);
+ method public static deprecated android.graphics.Bitmap createAudioThumbnail(java.lang.String, int);
+ method public static android.graphics.Bitmap createAudioThumbnail(java.io.File, android.util.Size, android.os.CancellationSignal) throws java.io.IOException;
+ method public static deprecated android.graphics.Bitmap createImageThumbnail(java.lang.String, int);
+ method public static android.graphics.Bitmap createImageThumbnail(java.io.File, android.util.Size, android.os.CancellationSignal) throws java.io.IOException;
+ method public static deprecated android.graphics.Bitmap createVideoThumbnail(java.lang.String, int);
+ method public static android.graphics.Bitmap createVideoThumbnail(java.io.File, android.util.Size, android.os.CancellationSignal) throws java.io.IOException;
method public static android.graphics.Bitmap extractThumbnail(android.graphics.Bitmap, int, int);
method public static android.graphics.Bitmap extractThumbnail(android.graphics.Bitmap, int, int, int);
field public static final int OPTIONS_RECYCLE_INPUT = 2; // 0x2
@@ -28319,6 +28325,11 @@
field public int serverAddress;
}
+ public class InetAddresses {
+ method public static boolean isNumericAddress(java.lang.String);
+ method public static java.net.InetAddress parseNumericAddress(java.lang.String);
+ }
+
public final class IpPrefix implements android.os.Parcelable {
method public boolean contains(java.net.InetAddress);
method public int describeContents();
diff --git a/api/system-current.txt b/api/system-current.txt
index 7d10b098..dfcae63 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -205,6 +205,10 @@
field public static final int config_sendPackageName = 17891328; // 0x1110000
}
+ public static final class R.color {
+ field public static final int system_notification_accent_color = 17170460; // 0x106001c
+ }
+
public static final class R.dimen {
field public static final int config_mediaMetadataBitmapMaxSize = 17104904; // 0x1050008
field public static final int config_restrictedIconSize = 17104903; // 0x1050007
@@ -5013,7 +5017,7 @@
method public final java.util.Set<android.content.ComponentName> getContentCaptureDisabledActivities();
method public final java.util.Set<java.lang.String> getContentCaptureDisabledPackages();
method public void onActivitySnapshot(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.SnapshotData);
- method public abstract void onContentCaptureEventsRequest(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.ContentCaptureEventsRequest);
+ method public void onContentCaptureEventsRequest(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.ContentCaptureEventsRequest);
method public void onCreateContentCaptureSession(android.view.contentcapture.ContentCaptureContext, android.view.contentcapture.ContentCaptureSessionId);
method public void onDestroyContentCaptureSession(android.view.contentcapture.ContentCaptureSessionId);
method public final void setActivityContentCaptureEnabled(android.content.ComponentName, boolean);
@@ -5694,6 +5698,83 @@
field public static final java.lang.String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
}
+ public class DisconnectCause {
+ field public static final int ALREADY_DIALING = 72; // 0x48
+ field public static final int ANSWERED_ELSEWHERE = 52; // 0x34
+ field public static final int BUSY = 4; // 0x4
+ field public static final int CALLING_DISABLED = 74; // 0x4a
+ field public static final int CALL_BARRED = 20; // 0x14
+ field public static final int CALL_PULLED = 51; // 0x33
+ field public static final int CANT_CALL_WHILE_RINGING = 73; // 0x49
+ field public static final int CDMA_ACCESS_BLOCKED = 35; // 0x23
+ field public static final int CDMA_ACCESS_FAILURE = 32; // 0x20
+ field public static final int CDMA_ALREADY_ACTIVATED = 49; // 0x31
+ field public static final int CDMA_DROP = 27; // 0x1b
+ field public static final int CDMA_INTERCEPT = 28; // 0x1c
+ field public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 26; // 0x1a
+ field public static final int CDMA_NOT_EMERGENCY = 34; // 0x22
+ field public static final int CDMA_PREEMPTED = 33; // 0x21
+ field public static final int CDMA_REORDER = 29; // 0x1d
+ field public static final int CDMA_RETRY_ORDER = 31; // 0x1f
+ field public static final int CDMA_SO_REJECT = 30; // 0x1e
+ field public static final int CONGESTION = 5; // 0x5
+ field public static final int CS_RESTRICTED = 22; // 0x16
+ field public static final int CS_RESTRICTED_EMERGENCY = 24; // 0x18
+ field public static final int CS_RESTRICTED_NORMAL = 23; // 0x17
+ field public static final int DATA_DISABLED = 54; // 0x36
+ field public static final int DATA_LIMIT_REACHED = 55; // 0x37
+ field public static final int DIALED_CALL_FORWARDING_WHILE_ROAMING = 57; // 0x39
+ field public static final int DIALED_MMI = 39; // 0x27
+ field public static final int DIAL_LOW_BATTERY = 62; // 0x3e
+ field public static final int DIAL_MODIFIED_TO_DIAL = 48; // 0x30
+ field public static final int DIAL_MODIFIED_TO_DIAL_VIDEO = 66; // 0x42
+ field public static final int DIAL_MODIFIED_TO_SS = 47; // 0x2f
+ field public static final int DIAL_MODIFIED_TO_USSD = 46; // 0x2e
+ field public static final int DIAL_VIDEO_MODIFIED_TO_DIAL = 69; // 0x45
+ field public static final int DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 70; // 0x46
+ field public static final int DIAL_VIDEO_MODIFIED_TO_SS = 67; // 0x43
+ field public static final int DIAL_VIDEO_MODIFIED_TO_USSD = 68; // 0x44
+ field public static final int EMERGENCY_PERM_FAILURE = 64; // 0x40
+ field public static final int EMERGENCY_TEMP_FAILURE = 63; // 0x3f
+ field public static final int ERROR_UNSPECIFIED = 36; // 0x24
+ field public static final int FDN_BLOCKED = 21; // 0x15
+ field public static final int ICC_ERROR = 19; // 0x13
+ field public static final int IMEI_NOT_ACCEPTED = 58; // 0x3a
+ field public static final int IMS_ACCESS_BLOCKED = 60; // 0x3c
+ field public static final int IMS_MERGED_SUCCESSFULLY = 45; // 0x2d
+ field public static final int IMS_SIP_ALTERNATE_EMERGENCY_CALL = 71; // 0x47
+ field public static final int INCOMING_MISSED = 1; // 0x1
+ field public static final int INCOMING_REJECTED = 16; // 0x10
+ field public static final int INVALID_CREDENTIALS = 10; // 0xa
+ field public static final int INVALID_NUMBER = 7; // 0x7
+ field public static final int LIMIT_EXCEEDED = 15; // 0xf
+ field public static final int LOCAL = 3; // 0x3
+ field public static final int LOST_SIGNAL = 14; // 0xe
+ field public static final int LOW_BATTERY = 61; // 0x3d
+ field public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 53; // 0x35
+ field public static final int MMI = 6; // 0x6
+ field public static final int NORMAL = 2; // 0x2
+ field public static final int NORMAL_UNSPECIFIED = 65; // 0x41
+ field public static final int NOT_DISCONNECTED = 0; // 0x0
+ field public static final int NOT_VALID = -1; // 0xffffffff
+ field public static final int NO_PHONE_NUMBER_SUPPLIED = 38; // 0x26
+ field public static final int NUMBER_UNREACHABLE = 8; // 0x8
+ field public static final int OTASP_PROVISIONING_IN_PROCESS = 76; // 0x4c
+ field public static final int OUTGOING_CANCELED = 44; // 0x2c
+ field public static final int OUTGOING_FAILURE = 43; // 0x2b
+ field public static final int OUT_OF_NETWORK = 11; // 0xb
+ field public static final int OUT_OF_SERVICE = 18; // 0x12
+ field public static final int POWER_OFF = 17; // 0x11
+ field public static final int SERVER_ERROR = 12; // 0xc
+ field public static final int SERVER_UNREACHABLE = 9; // 0x9
+ field public static final int TIMED_OUT = 13; // 0xd
+ field public static final int TOO_MANY_ONGOING_CALLS = 75; // 0x4b
+ field public static final int UNOBTAINABLE_NUMBER = 25; // 0x19
+ field public static final int VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED = 50; // 0x32
+ field public static final int VOICEMAIL_NUMBER_MISSING = 40; // 0x28
+ field public static final int WIFI_LOST = 59; // 0x3b
+ }
+
public class MbmsDownloadSession implements java.lang.AutoCloseable {
field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_ACTION = "android.telephony.action.EmbmsDownload";
}
@@ -5782,14 +5863,134 @@
}
public class PhoneStateListener {
+ method public void onCallDisconnectCauseChanged(int, int);
+ method public void onPreciseCallStateChanged(android.telephony.PreciseCallState);
method public void onRadioPowerStateChanged(int);
method public void onSrvccStateChanged(int);
method public void onVoiceActivationStateChanged(int);
+ field public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
+ field public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
field public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
field public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000
field public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000
}
+ public final class PreciseCallState implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getBackgroundCallState();
+ method public int getForegroundCallState();
+ method public int getRingingCallState();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.PreciseCallState> CREATOR;
+ field public static final int PRECISE_CALL_STATE_ACTIVE = 1; // 0x1
+ field public static final int PRECISE_CALL_STATE_ALERTING = 4; // 0x4
+ field public static final int PRECISE_CALL_STATE_DIALING = 3; // 0x3
+ field public static final int PRECISE_CALL_STATE_DISCONNECTED = 7; // 0x7
+ field public static final int PRECISE_CALL_STATE_DISCONNECTING = 8; // 0x8
+ field public static final int PRECISE_CALL_STATE_HOLDING = 2; // 0x2
+ field public static final int PRECISE_CALL_STATE_IDLE = 0; // 0x0
+ field public static final int PRECISE_CALL_STATE_INCOMING = 5; // 0x5
+ field public static final int PRECISE_CALL_STATE_NOT_VALID = -1; // 0xffffffff
+ field public static final int PRECISE_CALL_STATE_WAITING = 6; // 0x6
+ }
+
+ public class PreciseDisconnectCause {
+ field public static final int ACCESS_CLASS_BLOCKED = 260; // 0x104
+ field public static final int ACCESS_INFORMATION_DISCARDED = 43; // 0x2b
+ field public static final int ACM_LIMIT_EXCEEDED = 68; // 0x44
+ field public static final int BEARER_CAPABILITY_NOT_AUTHORIZED = 57; // 0x39
+ field public static final int BEARER_NOT_AVAIL = 58; // 0x3a
+ field public static final int BEARER_SERVICE_NOT_IMPLEMENTED = 65; // 0x41
+ field public static final int BUSY = 17; // 0x11
+ field public static final int CALL_BARRED = 240; // 0xf0
+ field public static final int CALL_REJECTED = 21; // 0x15
+ field public static final int CDMA_ACCESS_BLOCKED = 1009; // 0x3f1
+ field public static final int CDMA_ACCESS_FAILURE = 1006; // 0x3ee
+ field public static final int CDMA_DROP = 1001; // 0x3e9
+ field public static final int CDMA_INTERCEPT = 1002; // 0x3ea
+ field public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 1000; // 0x3e8
+ field public static final int CDMA_NOT_EMERGENCY = 1008; // 0x3f0
+ field public static final int CDMA_PREEMPTED = 1007; // 0x3ef
+ field public static final int CDMA_REORDER = 1003; // 0x3eb
+ field public static final int CDMA_RETRY_ORDER = 1005; // 0x3ed
+ field public static final int CDMA_SO_REJECT = 1004; // 0x3ec
+ field public static final int CHANNEL_NOT_AVAIL = 44; // 0x2c
+ field public static final int CHANNEL_UNACCEPTABLE = 6; // 0x6
+ field public static final int CONDITIONAL_IE_ERROR = 100; // 0x64
+ field public static final int DESTINATION_OUT_OF_ORDER = 27; // 0x1b
+ field public static final int ERROR_UNSPECIFIED = 65535; // 0xffff
+ field public static final int FACILITY_REJECTED = 29; // 0x1d
+ field public static final int FDN_BLOCKED = 241; // 0xf1
+ field public static final int IMEI_NOT_ACCEPTED = 243; // 0xf3
+ field public static final int IMSI_UNKNOWN_IN_VLR = 242; // 0xf2
+ field public static final int INCOMING_CALLS_BARRED_WITHIN_CUG = 55; // 0x37
+ field public static final int INCOMPATIBLE_DESTINATION = 88; // 0x58
+ field public static final int INFORMATION_ELEMENT_NON_EXISTENT = 99; // 0x63
+ field public static final int INTERWORKING_UNSPECIFIED = 127; // 0x7f
+ field public static final int INVALID_MANDATORY_INFORMATION = 96; // 0x60
+ field public static final int INVALID_NUMBER_FORMAT = 28; // 0x1c
+ field public static final int INVALID_TRANSACTION_IDENTIFIER = 81; // 0x51
+ field public static final int MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 101; // 0x65
+ field public static final int MESSAGE_TYPE_NON_IMPLEMENTED = 97; // 0x61
+ field public static final int MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 98; // 0x62
+ field public static final int NETWORK_DETACH = 261; // 0x105
+ field public static final int NETWORK_OUT_OF_ORDER = 38; // 0x26
+ field public static final int NETWORK_REJECT = 252; // 0xfc
+ field public static final int NETWORK_RESP_TIMEOUT = 251; // 0xfb
+ field public static final int NORMAL = 16; // 0x10
+ field public static final int NORMAL_UNSPECIFIED = 31; // 0x1f
+ field public static final int NOT_VALID = -1; // 0xffffffff
+ field public static final int NO_ANSWER_FROM_USER = 19; // 0x13
+ field public static final int NO_CIRCUIT_AVAIL = 34; // 0x22
+ field public static final int NO_DISCONNECT_CAUSE_AVAILABLE = 0; // 0x0
+ field public static final int NO_ROUTE_TO_DESTINATION = 3; // 0x3
+ field public static final int NO_USER_RESPONDING = 18; // 0x12
+ field public static final int NO_VALID_SIM = 249; // 0xf9
+ field public static final int NUMBER_CHANGED = 22; // 0x16
+ field public static final int OEM_CAUSE_1 = 61441; // 0xf001
+ field public static final int OEM_CAUSE_10 = 61450; // 0xf00a
+ field public static final int OEM_CAUSE_11 = 61451; // 0xf00b
+ field public static final int OEM_CAUSE_12 = 61452; // 0xf00c
+ field public static final int OEM_CAUSE_13 = 61453; // 0xf00d
+ field public static final int OEM_CAUSE_14 = 61454; // 0xf00e
+ field public static final int OEM_CAUSE_15 = 61455; // 0xf00f
+ field public static final int OEM_CAUSE_2 = 61442; // 0xf002
+ field public static final int OEM_CAUSE_3 = 61443; // 0xf003
+ field public static final int OEM_CAUSE_4 = 61444; // 0xf004
+ field public static final int OEM_CAUSE_5 = 61445; // 0xf005
+ field public static final int OEM_CAUSE_6 = 61446; // 0xf006
+ field public static final int OEM_CAUSE_7 = 61447; // 0xf007
+ field public static final int OEM_CAUSE_8 = 61448; // 0xf008
+ field public static final int OEM_CAUSE_9 = 61449; // 0xf009
+ field public static final int ONLY_DIGITAL_INFORMATION_BEARER_AVAILABLE = 70; // 0x46
+ field public static final int OPERATOR_DETERMINED_BARRING = 8; // 0x8
+ field public static final int OUT_OF_SRV = 248; // 0xf8
+ field public static final int PREEMPTION = 25; // 0x19
+ field public static final int PROTOCOL_ERROR_UNSPECIFIED = 111; // 0x6f
+ field public static final int QOS_NOT_AVAIL = 49; // 0x31
+ field public static final int RADIO_ACCESS_FAILURE = 253; // 0xfd
+ field public static final int RADIO_INTERNAL_ERROR = 250; // 0xfa
+ field public static final int RADIO_LINK_FAILURE = 254; // 0xfe
+ field public static final int RADIO_LINK_LOST = 255; // 0xff
+ field public static final int RADIO_OFF = 247; // 0xf7
+ field public static final int RADIO_RELEASE_ABNORMAL = 259; // 0x103
+ field public static final int RADIO_RELEASE_NORMAL = 258; // 0x102
+ field public static final int RADIO_SETUP_FAILURE = 257; // 0x101
+ field public static final int RADIO_UPLINK_FAILURE = 256; // 0x100
+ field public static final int RECOVERY_ON_TIMER_EXPIRED = 102; // 0x66
+ field public static final int REQUESTED_FACILITY_NOT_IMPLEMENTED = 69; // 0x45
+ field public static final int REQUESTED_FACILITY_NOT_SUBSCRIBED = 50; // 0x32
+ field public static final int RESOURCES_UNAVAILABLE_OR_UNSPECIFIED = 47; // 0x2f
+ field public static final int SEMANTICALLY_INCORRECT_MESSAGE = 95; // 0x5f
+ field public static final int SERVICE_OPTION_NOT_AVAILABLE = 63; // 0x3f
+ field public static final int SERVICE_OR_OPTION_NOT_IMPLEMENTED = 79; // 0x4f
+ field public static final int STATUS_ENQUIRY = 30; // 0x1e
+ field public static final int SWITCHING_CONGESTION = 42; // 0x2a
+ field public static final int TEMPORARY_FAILURE = 41; // 0x29
+ field public static final int UNOBTAINABLE_NUMBER = 1; // 0x1
+ field public static final int USER_NOT_MEMBER_OF_CUG = 87; // 0x57
+ }
+
public class ServiceState implements android.os.Parcelable {
method public android.telephony.NetworkRegistrationState getNetworkRegistrationState(int, int);
method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates();
@@ -5827,6 +6028,7 @@
public class SubscriptionInfo implements android.os.Parcelable {
method public java.util.List<android.telephony.UiccAccessRule> getAccessRules();
+ method public int getCardId();
}
public class SubscriptionManager {
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index 020c443..8d0cee5 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -36,6 +36,7 @@
#include "idmap2/CommandLineOptions.h"
#include "idmap2/Idmap.h"
+#include "idmap2/Result.h"
#include "idmap2/Xml.h"
#include "idmap2/ZipFile.h"
@@ -53,39 +54,40 @@
using android::idmap2::CommandLineOptions;
using android::idmap2::IdmapHeader;
using android::idmap2::ResourceId;
+using android::idmap2::Result;
using android::idmap2::Xml;
using android::idmap2::ZipFile;
using android::util::Utf16ToUtf8;
namespace {
-std::pair<bool, ResourceId> WARN_UNUSED ParseResReference(const AssetManager2& am,
- const std::string& res,
- const std::string& fallback_package) {
+
+Result<ResourceId> WARN_UNUSED ParseResReference(const AssetManager2& am, const std::string& res,
+ const std::string& fallback_package) {
// first, try to parse as a hex number
char* endptr = nullptr;
ResourceId resid;
resid = strtol(res.c_str(), &endptr, 16);
if (*endptr == '\0') {
- return std::make_pair(true, resid);
+ return {resid};
}
// next, try to parse as a package:type/name string
resid = am.GetResourceId(res, "", fallback_package);
if (is_valid_resid(resid)) {
- return std::make_pair(true, resid);
+ return {resid};
}
// end of the road: res could not be parsed
- return std::make_pair(false, 0);
+ return {};
}
-std::pair<bool, std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId resid) {
+Result<std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId resid) {
Res_value value;
ResTable_config config;
uint32_t flags;
ApkAssetsCookie cookie = am.GetResource(resid, false, 0, &value, &config, &flags);
if (cookie == kInvalidCookie) {
- return std::make_pair(false, "");
+ return {};
}
std::string out;
@@ -123,31 +125,31 @@
out.append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data));
break;
}
- return std::make_pair(true, out);
+ return {out};
}
-std::pair<bool, std::string> GetTargetPackageNameFromManifest(const std::string& apk_path) {
+Result<std::string> GetTargetPackageNameFromManifest(const std::string& apk_path) {
const auto zip = ZipFile::Open(apk_path);
if (!zip) {
- return std::make_pair(false, "");
+ return {};
}
const auto entry = zip->Uncompress("AndroidManifest.xml");
if (!entry) {
- return std::make_pair(false, "");
+ return {};
}
const auto xml = Xml::Create(entry->buf, entry->size);
if (!xml) {
- return std::make_pair(false, "");
+ return {};
}
const auto tag = xml->FindTag("overlay");
if (!tag) {
- return std::make_pair(false, "");
+ return {};
}
const auto iter = tag->find("targetPackage");
if (iter == tag->end()) {
- return std::make_pair(false, "");
+ return {};
}
- return std::make_pair(true, iter->second);
+ return {iter->second};
}
} // namespace
@@ -195,14 +197,14 @@
}
apk_assets.push_back(std::move(target_apk));
- bool lookup_ok;
- std::tie(lookup_ok, target_package_name) =
+ const Result<std::string> package_name =
GetTargetPackageNameFromManifest(idmap_header->GetOverlayPath().to_string());
- if (!lookup_ok) {
+ if (!package_name) {
out_error << "error: failed to parse android:targetPackage from overlay manifest"
<< std::endl;
return false;
}
+ target_package_name = *package_name;
} else if (target_path != idmap_header->GetTargetPath()) {
out_error << "error: different target APKs (expected target APK " << target_path << " but "
<< idmap_path << " has target APK " << idmap_header->GetTargetPath() << ")"
@@ -227,21 +229,18 @@
am.SetApkAssets(raw_pointer_apk_assets);
am.SetConfiguration(config);
- ResourceId resid;
- bool lookup_ok;
- std::tie(lookup_ok, resid) = ParseResReference(am, resid_str, target_package_name);
- if (!lookup_ok) {
+ const Result<ResourceId> resid = ParseResReference(am, resid_str, target_package_name);
+ if (!resid) {
out_error << "error: failed to parse resource ID" << std::endl;
return false;
}
- std::string value;
- std::tie(lookup_ok, value) = GetValue(am, resid);
- if (!lookup_ok) {
- out_error << StringPrintf("error: resource 0x%08x not found", resid) << std::endl;
+ const Result<std::string> value = GetValue(am, *resid);
+ if (!value) {
+ out_error << StringPrintf("error: resource 0x%08x not found", *resid) << std::endl;
return false;
}
- std::cout << value << std::endl;
+ std::cout << *value << std::endl;
return true;
}
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index cf72cb9..86b00f1 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -78,6 +78,18 @@
}
}
+Status Idmap2Service::verifyIdmap(const std::string& overlay_apk_path,
+ int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) {
+ assert(_aidl_return);
+ const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
+ std::ifstream fin(idmap_path);
+ const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
+ fin.close();
+ std::stringstream dev_null;
+ *_aidl_return = header && header->IsUpToDate(dev_null);
+ return ok();
+}
+
Status Idmap2Service::createIdmap(const std::string& target_apk_path,
const std::string& overlay_apk_path, int32_t user_id,
std::unique_ptr<std::string>* _aidl_return) {
@@ -90,17 +102,6 @@
_aidl_return->reset(nullptr);
- const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
- std::ifstream fin(idmap_path);
- const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
- fin.close();
- // do not reuse error stream from IsUpToDate below, or error messages will be
- // polluted with irrelevant data
- std::stringstream dev_null;
- if (header && header->IsUpToDate(dev_null)) {
- return ok();
- }
-
const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
if (!target_apk) {
return error("failed to load apk " + target_apk_path);
@@ -119,6 +120,7 @@
}
umask(0133); // u=rw,g=r,o=r
+ const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
std::ofstream fout(idmap_path);
if (fout.fail()) {
return error("failed to open idmap path " + idmap_path);
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h
index 2b32042..4e5abc9 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.h
+++ b/cmds/idmap2/idmap2d/Idmap2Service.h
@@ -39,6 +39,9 @@
binder::Status removeIdmap(const std::string& overlay_apk_path, int32_t user_id,
bool* _aidl_return);
+ binder::Status verifyIdmap(const std::string& overlay_apk_path, int32_t user_id,
+ bool* _aidl_return);
+
binder::Status createIdmap(const std::string& target_apk_path,
const std::string& overlay_apk_path, int32_t user_id,
std::unique_ptr<std::string>* _aidl_return);
diff --git a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
index 5d19610..d475417 100644
--- a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
+++ b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
@@ -22,6 +22,7 @@
interface IIdmap2 {
@utf8InCpp String getIdmapPath(@utf8InCpp String overlayApkPath, int userId);
boolean removeIdmap(@utf8InCpp String overlayApkPath, int userId);
+ boolean verifyIdmap(@utf8InCpp String overlayApkPath, int userId);
@nullable @utf8InCpp String createIdmap(@utf8InCpp String targetApkPath,
@utf8InCpp String overlayApkPath, int userId);
}
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
index 88a835b..d106f19 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -18,19 +18,18 @@
#define IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_
#include <string>
-#include <utility>
#include "android-base/macros.h"
#include "androidfw/AssetManager2.h"
#include "idmap2/Idmap.h"
+#include "idmap2/Result.h"
namespace android {
namespace idmap2 {
namespace utils {
-std::pair<bool, std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am,
- ResourceId resid);
+Result<std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am, ResourceId resid);
} // namespace utils
} // namespace idmap2
diff --git a/cmds/idmap2/include/idmap2/Result.h b/cmds/idmap2/include/idmap2/Result.h
new file mode 100644
index 0000000..6189ea3
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/Result.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_RESULT_H_
+#define IDMAP2_INCLUDE_IDMAP2_RESULT_H_
+
+#include <optional>
+
+namespace android::idmap2 {
+
+template <typename T>
+using Result = std::optional<T>;
+
+static constexpr std::nullopt_t kResultError = std::nullopt;
+
+} // namespace android::idmap2
+
+#endif // IDMAP2_INCLUDE_IDMAP2_RESULT_H_
diff --git a/cmds/idmap2/include/idmap2/ZipFile.h b/cmds/idmap2/include/idmap2/ZipFile.h
index 328bd36..9edbbe0 100644
--- a/cmds/idmap2/include/idmap2/ZipFile.h
+++ b/cmds/idmap2/include/idmap2/ZipFile.h
@@ -19,10 +19,10 @@
#include <memory>
#include <string>
-#include <utility>
#include "android-base/macros.h"
#include "ziparchive/zip_archive.h"
+#include "idmap2/Result.h"
namespace android {
namespace idmap2 {
@@ -43,7 +43,7 @@
static std::unique_ptr<const ZipFile> Open(const std::string& path);
std::unique_ptr<const MemoryChunk> Uncompress(const std::string& entryPath) const;
- std::pair<bool, uint32_t> Crc(const std::string& entryPath) const;
+ Result<uint32_t> Crc(const std::string& entryPath) const;
~ZipFile();
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 5a47e30..1ef3267 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -33,6 +33,7 @@
#include "idmap2/Idmap.h"
#include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
#include "idmap2/ZipFile.h"
namespace android {
@@ -143,18 +144,16 @@
return false;
}
- bool status;
- uint32_t target_crc;
- std::tie(status, target_crc) = target_zip->Crc("resources.arsc");
- if (!status) {
+ Result<uint32_t> target_crc = target_zip->Crc("resources.arsc");
+ if (!target_crc) {
out_error << "error: failed to get target crc" << std::endl;
return false;
}
- if (target_crc_ != target_crc) {
+ if (target_crc_ != *target_crc) {
out_error << base::StringPrintf(
"error: bad target crc: idmap version 0x%08x, file system version 0x%08x",
- target_crc_, target_crc)
+ target_crc_, *target_crc)
<< std::endl;
return false;
}
@@ -165,17 +164,16 @@
return false;
}
- uint32_t overlay_crc;
- std::tie(status, overlay_crc) = overlay_zip->Crc("resources.arsc");
- if (!status) {
+ Result<uint32_t> overlay_crc = overlay_zip->Crc("resources.arsc");
+ if (!overlay_crc) {
out_error << "error: failed to get overlay crc" << std::endl;
return false;
}
- if (overlay_crc_ != overlay_crc) {
+ if (overlay_crc_ != *overlay_crc) {
out_error << base::StringPrintf(
"error: bad overlay crc: idmap version 0x%08x, file system version 0x%08x",
- overlay_crc_, overlay_crc)
+ overlay_crc_, *overlay_crc)
<< std::endl;
return false;
}
@@ -322,17 +320,20 @@
std::unique_ptr<IdmapHeader> header(new IdmapHeader());
header->magic_ = kIdmapMagic;
header->version_ = kIdmapCurrentVersion;
- bool crc_status;
- std::tie(crc_status, header->target_crc_) = target_zip->Crc("resources.arsc");
- if (!crc_status) {
+
+ Result<uint32_t> crc = target_zip->Crc("resources.arsc");
+ if (!crc) {
out_error << "error: failed to get zip crc for target" << std::endl;
return nullptr;
}
- std::tie(crc_status, header->overlay_crc_) = overlay_zip->Crc("resources.arsc");
- if (!crc_status) {
+ header->target_crc_ = *crc;
+
+ crc = overlay_zip->Crc("resources.arsc");
+ if (!crc) {
out_error << "error: failed to get zip crc for overlay" << std::endl;
return nullptr;
}
+ header->overlay_crc_ = *crc;
if (target_apk_path.size() > sizeof(header->target_path_)) {
out_error << "error: target apk path \"" << target_apk_path << "\" longer that maximum size "
@@ -358,15 +359,14 @@
const auto end = overlay_pkg->end();
for (auto iter = overlay_pkg->begin(); iter != end; ++iter) {
const ResourceId overlay_resid = *iter;
- bool lookup_ok;
- std::string name;
- std::tie(lookup_ok, name) = utils::ResToTypeEntryName(overlay_asset_manager, overlay_resid);
- if (!lookup_ok) {
+ Result<std::string> name = utils::ResToTypeEntryName(overlay_asset_manager, overlay_resid);
+ if (!name) {
continue;
}
// prepend "<package>:" to turn name into "<package>:<type>/<name>"
- name = base::StringPrintf("%s:%s", target_pkg->GetPackageName().c_str(), name.c_str());
- const ResourceId target_resid = NameToResid(target_asset_manager, name);
+ const std::string full_name =
+ base::StringPrintf("%s:%s", target_pkg->GetPackageName().c_str(), name->c_str());
+ const ResourceId target_resid = NameToResid(target_asset_manager, full_name);
if (target_resid == 0) {
continue;
}
diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
index 492e6f0..fb3bc5b 100644
--- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
@@ -15,7 +15,6 @@
*/
#include <string>
-#include <utility>
#include "android-base/macros.h"
#include "android-base/stringprintf.h"
@@ -23,6 +22,7 @@
#include "idmap2/PrettyPrintVisitor.h"
#include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
namespace android {
namespace idmap2 {
@@ -63,11 +63,9 @@
stream_ << base::StringPrintf("0x%08x -> 0x%08x", target_resid, overlay_resid);
if (target_package_loaded) {
- bool lookup_ok;
- std::string name;
- std::tie(lookup_ok, name) = utils::ResToTypeEntryName(target_am_, target_resid);
- if (lookup_ok) {
- stream_ << " " << name;
+ Result<std::string> name = utils::ResToTypeEntryName(target_am_, target_resid);
+ if (name) {
+ stream_ << " " << *name;
}
}
stream_ << std::endl;
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
index 57cfc8e..7c24445 100644
--- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -16,7 +16,6 @@
#include <cstdarg>
#include <string>
-#include <utility>
#include "android-base/macros.h"
#include "android-base/stringprintf.h"
@@ -24,6 +23,7 @@
#include "idmap2/RawPrintVisitor.h"
#include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
using android::ApkAssets;
@@ -75,14 +75,13 @@
const ResourceId target_resid =
RESID(last_seen_package_id_, te.GetTargetTypeId(), te.GetEntryOffset() + i);
const ResourceId overlay_resid = RESID(last_seen_package_id_, te.GetOverlayTypeId(), entry);
- bool lookup_ok = false;
- std::string name;
+ Result<std::string> name;
if (target_package_loaded) {
- std::tie(lookup_ok, name) = utils::ResToTypeEntryName(target_am_, target_resid);
+ name = utils::ResToTypeEntryName(target_am_, target_resid);
}
- if (lookup_ok) {
+ if (name) {
print(static_cast<uint32_t>(entry), "0x%08x -> 0x%08x %s", target_resid, overlay_resid,
- name.c_str());
+ name->c_str());
} else {
print(static_cast<uint32_t>(entry), "0x%08x -> 0x%08x", target_resid, overlay_resid);
}
diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp
index e98f843..5c89783 100644
--- a/cmds/idmap2/libidmap2/ResourceUtils.cpp
+++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp
@@ -15,12 +15,12 @@
*/
#include <string>
-#include <utility>
#include "androidfw/StringPiece.h"
#include "androidfw/Util.h"
#include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
using android::StringPiece16;
using android::util::Utf16ToUtf8;
@@ -29,11 +29,10 @@
namespace idmap2 {
namespace utils {
-std::pair<bool, std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am,
- ResourceId resid) {
+Result<std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am, ResourceId resid) {
AssetManager2::ResourceName name;
if (!am.GetResourceName(resid, &name)) {
- return std::make_pair(false, "");
+ return {};
}
std::string out;
if (name.type != nullptr) {
@@ -47,7 +46,7 @@
} else {
out += Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len));
}
- return std::make_pair(true, out);
+ return {out};
}
} // namespace utils
diff --git a/cmds/idmap2/libidmap2/ZipFile.cpp b/cmds/idmap2/libidmap2/ZipFile.cpp
index 3f2079a..9fb611d 100644
--- a/cmds/idmap2/libidmap2/ZipFile.cpp
+++ b/cmds/idmap2/libidmap2/ZipFile.cpp
@@ -16,8 +16,8 @@
#include <memory>
#include <string>
-#include <utility>
+#include "idmap2/Result.h"
#include "idmap2/ZipFile.h"
namespace android {
@@ -57,10 +57,10 @@
return chunk;
}
-std::pair<bool, uint32_t> ZipFile::Crc(const std::string& entryPath) const {
+Result<uint32_t> ZipFile::Crc(const std::string& entryPath) const {
::ZipEntry entry;
int32_t status = ::FindEntry(handle_, ::ZipString(entryPath.c_str()), &entry);
- return std::make_pair(status == 0, entry.crc32);
+ return status == 0 ? Result<uint32_t>(entry.crc32) : kResultError;
}
} // namespace idmap2
diff --git a/cmds/idmap2/tests/ResourceUtilsTests.cpp b/cmds/idmap2/tests/ResourceUtilsTests.cpp
index 0547fa0..7f60d75 100644
--- a/cmds/idmap2/tests/ResourceUtilsTests.cpp
+++ b/cmds/idmap2/tests/ResourceUtilsTests.cpp
@@ -16,13 +16,13 @@
#include <memory>
#include <string>
-#include <utility>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "androidfw/ApkAssets.h"
#include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
#include "TestHelpers.h"
@@ -52,17 +52,14 @@
};
TEST_F(ResourceUtilsTests, ResToTypeEntryName) {
- bool lookup_ok;
- std::string name;
- std::tie(lookup_ok, name) = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000u);
- ASSERT_TRUE(lookup_ok);
- ASSERT_EQ(name, "integer/int1");
+ Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000u);
+ ASSERT_TRUE(name);
+ ASSERT_EQ(*name, "integer/int1");
}
TEST_F(ResourceUtilsTests, ResToTypeEntryNameNoSuchResourceId) {
- bool lookup_ok;
- std::tie(lookup_ok, std::ignore) = utils::ResToTypeEntryName(GetAssetManager(), 0x7f123456u);
- ASSERT_FALSE(lookup_ok);
+ Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f123456u);
+ ASSERT_FALSE(name);
}
} // namespace idmap2
diff --git a/cmds/idmap2/tests/ZipFileTests.cpp b/cmds/idmap2/tests/ZipFileTests.cpp
index a504d31..6e4a501 100644
--- a/cmds/idmap2/tests/ZipFileTests.cpp
+++ b/cmds/idmap2/tests/ZipFileTests.cpp
@@ -16,8 +16,8 @@
#include <cstdio> // fclose
#include <string>
-#include <utility>
+#include "idmap2/Result.h"
#include "idmap2/ZipFile.h"
#include "gmock/gmock.h"
@@ -44,14 +44,12 @@
auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
ASSERT_THAT(zip, NotNull());
- bool status;
- uint32_t crc;
- std::tie(status, crc) = zip->Crc("AndroidManifest.xml");
- ASSERT_TRUE(status);
- ASSERT_EQ(crc, 0x762f3d24);
+ Result<uint32_t> crc = zip->Crc("AndroidManifest.xml");
+ ASSERT_TRUE(crc);
+ ASSERT_EQ(*crc, 0x762f3d24);
- std::tie(status, std::ignore) = zip->Crc("does-not-exist");
- ASSERT_FALSE(status);
+ Result<uint32_t> crc2 = zip->Crc("does-not-exist");
+ ASSERT_FALSE(crc2);
}
TEST(ZipFileTests, Uncompress) {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 5899e0cf..0c05be1 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -171,6 +171,7 @@
DocsUIUserActionReported docs_ui_user_action_reported = 112;
WifiEnabledStateChanged wifi_enabled_state_changed = 113;
WifiRunningStateChanged wifi_running_state_changed = 114;
+ AppCompacted app_compacted = 115;
}
// Pulled events will start at field 10000.
@@ -3649,3 +3650,65 @@
message DocsUIInvalidScopedAccessRequestReported {
optional android.stats.docsui.InvalidScopedAccess type = 1;
}
+
+/**
+ * Logs when an app's memory is compacted.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ */
+message AppCompacted {
+ // The pid of the process being compacted.
+ optional int32 pid = 1;
+
+ // The name of the process being compacted.
+ optional string process_name = 2;
+
+ // The type of compaction.
+ enum Action {
+ UNKNOWN = 0;
+ SOME = 1;
+ FULL = 2;
+ }
+ optional Action action = 3 [default = UNKNOWN];
+
+ // Total RSS in kilobytes consumed by the process prior to compaction.
+ optional int64 before_rss_total_kilobytes = 4;
+
+ // File RSS in kilobytes consumed by the process prior to compaction.
+ optional int64 before_rss_file_kilobytes = 5;
+
+ // Anonymous RSS in kilobytes consumed by the process prior to compaction.
+ optional int64 before_rss_anon_kilobytes = 6;
+
+ // Swap in kilobytes consumed by the process prior to compaction.
+ optional int64 before_swap_kilobytes = 7;
+
+ // Total RSS in kilobytes consumed by the process after compaction.
+ optional int64 after_rss_total_kilobytes = 8;
+
+ // File RSS in kilobytes consumed by the process after compaction.
+ optional int64 after_rss_file_kilobytes = 9;
+
+ // Anonymous RSS in kilobytes consumed by the process after compaction.
+ optional int64 after_rss_anon_kilobytes = 10;
+
+ // Swap in kilobytes consumed by the process after compaction.
+ optional int64 after_swap_kilobytes = 11;
+
+ // The time taken to perform compaction in milliseconds.
+ optional int64 time_to_compact_millis = 12;
+
+ // The last compaction action performed for this app.
+ optional Action last_action = 13;
+
+ // The last time that compaction was attempted on this process in seconds
+ // since boot.
+ optional int64 last_compact_timestamp = 14;
+
+ // The oom_adj at the time of compaction.
+ optional int32 oom_adj = 15;
+
+ // The process state at the time of compaction.
+ optional android.app.ProcessStateEnum process_state = 16 [default = PROCESS_STATE_UNKNOWN];
+}
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 13579d2..7cc57c1 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -154,7 +154,7 @@
flushIfNeededLocked(dumpTimeNs);
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+ protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
if (mPastBuckets.empty()) {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 0425671..69bafc3 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -463,7 +463,7 @@
flushIfNeededLocked(dumpTimeNs);
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+ protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
if (mPastBuckets.empty()) {
VLOG(" Duration metric, empty return");
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index ea125d0..c53c4ce 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -110,7 +110,7 @@
std::set<string> *str_set,
ProtoOutputStream* protoOutput) {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+ protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
if (mProto->size() <= 0) {
return;
}
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 98a33f5..ec60244 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -194,7 +194,7 @@
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+ protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
if (mPastBuckets.empty()) {
return;
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 7475b53..cf56e2d 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -192,7 +192,7 @@
flushIfNeededLocked(dumpTimeNs);
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+ protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
if (mPastBuckets.empty() && mSkippedBuckets.empty()) {
return;
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 237f8b9..d52be44 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -279,7 +279,10 @@
// Dump report again. There should be no data since we erased it.
processor->onDumpReport(cfgKey, 5, true, true /* DO erase data. */, ADB_DUMP, &bytes);
output.ParseFromArray(bytes.data(), bytes.size());
- bool noData = (output.reports_size() == 0) || (output.reports(0).metrics_size() == 0);
+ // We don't care whether statsd has a report, as long as it has no count metrics in it.
+ bool noData = output.reports_size() == 0
+ || output.reports(0).metrics_size() == 0
+ || output.reports(0).metrics(0).count_metrics().data_size() == 0;
EXPECT_TRUE(noData);
}
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
index 8464b8df..91d6490 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
@@ -62,7 +62,7 @@
if (process.waitFor() == 0) {
logger.fine("Adb command successful.");
} else {
- logger.severe("Abnormal adb shell cmd termination for: " + String.join(",", commands));
+ logger.severe("Abnormal adb shell termination for: " + String.join(",", commands));
throw new RuntimeException("Error running adb command: " + err.toString());
}
}
@@ -118,6 +118,52 @@
logger.addHandler(handler);
}
+ /**
+ * Attempt to determine whether tool will work with this statsd, i.e. whether statsd is
+ * minCodename or higher.
+ * Algorithm: true if (sdk >= minSdk) || (sdk == minSdk-1 && codeName.startsWith(minCodeName))
+ * If all else fails, assume it will work (letting future commands deal with any errors).
+ */
+ public static boolean isAcceptableStatsd(Logger logger, int minSdk, String minCodename) {
+ BufferedReader in = null;
+ try {
+ File outFileSdk = File.createTempFile("shelltools_sdk", "tmp");
+ outFileSdk.deleteOnExit();
+ runCommand(outFileSdk, logger,
+ "adb", "shell", "getprop", "ro.build.version.sdk");
+ in = new BufferedReader(new InputStreamReader(new FileInputStream(outFileSdk)));
+ // If NullPointerException/NumberFormatException/etc., just catch and return true.
+ int sdk = Integer.parseInt(in.readLine().trim());
+ if (sdk >= minSdk) {
+ return true;
+ } else if (sdk == minSdk - 1) { // Could be minSdk-1, or could be minSdk development.
+ in.close();
+ File outFileCode = File.createTempFile("shelltools_codename", "tmp");
+ outFileCode.deleteOnExit();
+ runCommand(outFileCode, logger,
+ "adb", "shell", "getprop", "ro.build.version.codename");
+ in = new BufferedReader(new InputStreamReader(new FileInputStream(outFileCode)));
+ return in.readLine().startsWith(minCodename);
+ } else {
+ return false;
+ }
+ } catch (Exception e) {
+ logger.fine("Could not determine whether statsd version is compatibile "
+ + "with tool: " + e.toString());
+ } finally {
+ try {
+ if (in != null) {
+ in.close();
+ }
+ } catch (IOException e) {
+ logger.fine("Could not close temporary file: " + e.toString());
+ }
+ }
+ // Could not determine whether statsd is acceptable version.
+ // Just assume it is; if it isn't, we'll just get future errors via adb and deal with them.
+ return true;
+ }
+
public static class LocalToolsFormatter extends Formatter {
public String format(LogRecord record) {
return record.getMessage() + "\n";
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
index 67fb906..2eb4660 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
@@ -37,6 +37,9 @@
public class LocalDrive {
private static final boolean DEBUG = false;
+ public static final int MIN_SDK = 29;
+ public static final String MIN_CODENAME = "Q";
+
public static final long DEFAULT_CONFIG_ID = 56789;
public static final String BINARY_FLAG = "--binary";
@@ -46,7 +49,7 @@
public static final String HELP_STRING =
"Usage:\n\n" +
- "statsd_local upload CONFIG_FILE [CONFIG_ID] [--binary]\n" +
+ "statsd_localdrive upload CONFIG_FILE [CONFIG_ID] [--binary]\n" +
" Uploads the given statsd config file (in binary or human-readable-text format).\n" +
" If a config with this id already exists, removes it first.\n" +
" CONFIG_FILE Location of config file on host.\n" +
@@ -56,12 +59,12 @@
// Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID
"\n" +
- "statsd_local update CONFIG_FILE [CONFIG_ID] [--binary]\n" +
+ "statsd_localdrive update CONFIG_FILE [CONFIG_ID] [--binary]\n" +
" Same as upload, but does not remove the old config first (if it already exists).\n" +
// Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID
"\n" +
- "statsd_local get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]\n" +
+ "statsd_localdrive get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]\n" +
" Prints the output statslog data (in binary or human-readable-text format).\n" +
" CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
" --binary Output should be in binary, instead of default human-readable text.\n" +
@@ -72,13 +75,13 @@
// --include_current_bucket --proto
"\n" +
- "statsd_local remove [CONFIG_ID]\n" +
+ "statsd_localdrive remove [CONFIG_ID]\n" +
" Removes the config.\n" +
" CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
// Equivalent to: adb shell cmd stats config remove SHELL_UID CONFIG_ID
"\n" +
- "statsd_local clear [CONFIG_ID]\n" +
+ "statsd_localdrive clear [CONFIG_ID]\n" +
" Clears the data associated with the config.\n" +
" CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
// Similar to: adb shell cmd stats dump-report SHELL_UID CONFIG_ID
@@ -92,6 +95,12 @@
public static void main(String[] args) {
Utils.setUpLogger(sLogger, DEBUG);
+ if (!Utils.isAcceptableStatsd(sLogger, MIN_SDK, MIN_CODENAME)) {
+ sLogger.severe("LocalDrive only works with statsd versions for Android "
+ + MIN_CODENAME + " or higher.");
+ return;
+ }
+
if (args.length > 0) {
switch (args[0]) {
case "clear":
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1b45d17..497e193c 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -6152,7 +6152,7 @@
try {
synchronized (getGetProviderLock(auth, userId)) {
holder = ActivityManager.getService().getContentProvider(
- getApplicationThread(), auth, userId, stable);
+ getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 6905cb5..a278423 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -41,8 +41,10 @@
import android.provider.Settings;
import android.util.ArrayMap;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsActiveCallback;
import com.android.internal.app.IAppOpsCallback;
+import com.android.internal.app.IAppOpsNotedCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.util.Preconditions;
@@ -86,10 +88,20 @@
*/
final Context mContext;
+
@UnsupportedAppUsage
final IAppOpsService mService;
- final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers = new ArrayMap<>();
- final ArrayMap<OnOpActiveChangedListener, IAppOpsActiveCallback> mActiveWatchers =
+
+ @GuardedBy("mModeWatchers")
+ private final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers =
+ new ArrayMap<>();
+
+ @GuardedBy("mActiveWatchers")
+ private final ArrayMap<OnOpActiveChangedListener, IAppOpsActiveCallback> mActiveWatchers =
+ new ArrayMap<>();
+
+ @GuardedBy("mNotedWatchers")
+ private final ArrayMap<OnOpNotedListener, IAppOpsNotedCallback> mNotedWatchers =
new ArrayMap<>();
static IBinder sToken;
@@ -2471,6 +2483,23 @@
}
/**
+ * Callback for notification of an op being noted.
+ *
+ * @hide
+ */
+ public interface OnOpNotedListener {
+ /**
+ * Called when an op was noted.
+ *
+ * @param code The op code.
+ * @param uid The UID performing the operation.
+ * @param packageName The package performing the operation.
+ * @param result The result of the note.
+ */
+ void onOpNoted(String code, int uid, String packageName, int result);
+ }
+
+ /**
* Callback for notification of changes to operation state.
* This allows you to see the raw op codes instead of strings.
* @hide
@@ -2819,7 +2848,7 @@
*/
public void stopWatchingMode(OnOpChangedListener callback) {
synchronized (mModeWatchers) {
- IAppOpsCallback cb = mModeWatchers.get(callback);
+ IAppOpsCallback cb = mModeWatchers.remove(callback);
if (cb != null) {
try {
mService.stopWatchingMode(cb);
@@ -2893,7 +2922,7 @@
@TestApi
public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) {
synchronized (mActiveWatchers) {
- final IAppOpsActiveCallback cb = mActiveWatchers.get(callback);
+ final IAppOpsActiveCallback cb = mActiveWatchers.remove(callback);
if (cb != null) {
try {
mService.stopWatchingActive(cb);
@@ -2904,6 +2933,74 @@
}
}
+ /**
+ * Start watching for noted app ops. An app op may be immediate or long running.
+ * Immediate ops are noted while long running ones are started and stopped. This
+ * method allows registering a listener to be notified when an app op is noted. If
+ * an op is being noted by any package you will get a callback. To change the
+ * watched ops for a registered callback you need to unregister and register it again.
+ *
+ * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
+ * you can watch changes only for your UID.
+ *
+ * @param ops The ops to watch.
+ * @param callback Where to report changes.
+ *
+ * @see #startWatchingActive(int[], OnOpActiveChangedListener)
+ * @see #stopWatchingNoted(OnOpNotedListener)
+ * @see #noteOp(String, int, String)
+ *
+ * @hide
+ */
+ @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
+ public void startWatchingNoted(@NonNull String[] ops, @NonNull OnOpNotedListener callback) {
+ IAppOpsNotedCallback cb;
+ synchronized (mNotedWatchers) {
+ cb = mNotedWatchers.get(callback);
+ if (cb != null) {
+ return;
+ }
+ cb = new IAppOpsNotedCallback.Stub() {
+ @Override
+ public void opNoted(int op, int uid, String packageName, int mode) {
+ callback.onOpNoted(sOpToString[op], uid, packageName, mode);
+ }
+ };
+ mNotedWatchers.put(callback, cb);
+ }
+ try {
+ final int[] opCodes = new int[ops.length];
+ for (int i = 0; i < opCodes.length; i++) {
+ opCodes[i] = strOpToOp(ops[i]);
+ }
+ mService.startWatchingNoted(opCodes, cb);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Stop watching for noted app ops. An app op may be immediate or long running.
+ * Unregistering a non-registered callback has no effect.
+ *
+ * @see #startWatchingNoted(String[], OnOpNotedListener)
+ * @see #noteOp(String, int, String)
+ *
+ * @hide
+ */
+ public void stopWatchingNoted(@NonNull OnOpNotedListener callback) {
+ synchronized (mNotedWatchers) {
+ final IAppOpsNotedCallback cb = mNotedWatchers.get(callback);
+ if (cb != null) {
+ try {
+ mService.stopWatchingNoted(cb);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+
private String buildSecurityExceptionMsg(int op, int uid, String packageName) {
return packageName + " from uid " + uid + " not allowed to perform " + sOpNames[op];
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 88fb025..fb519b6 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -124,7 +124,7 @@
int ignoreWindowingMode);
void moveTaskToFront(int task, int flags, in Bundle options);
int getTaskForActivity(in IBinder token, in boolean onlyRoot);
- ContentProviderHolder getContentProvider(in IApplicationThread caller,
+ ContentProviderHolder getContentProvider(in IApplicationThread caller, in String callingPackage,
in String name, int userId, boolean stable);
void publishContentProviders(in IApplicationThread caller,
in List<ContentProviderHolder> providers);
diff --git a/core/java/android/net/InetAddresses.java b/core/java/android/net/InetAddresses.java
new file mode 100644
index 0000000..8e6c69a
--- /dev/null
+++ b/core/java/android/net/InetAddresses.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import libcore.net.InetAddressUtils;
+
+import java.net.InetAddress;
+
+/**
+ * Utility methods for {@link InetAddress} implementations.
+ */
+public class InetAddresses {
+
+ private InetAddresses() {}
+
+ /**
+ * Checks to see if the {@code address} is a numeric address (such as {@code "192.0.2.1"} or
+ * {@code "2001:db8::1:2"}).
+ *
+ * <p>A numeric address is either an IPv4 address containing exactly 4 decimal numbers or an
+ * IPv6 numeric address. IPv4 addresses that consist of either hexadecimal or octal digits or
+ * do not have exactly 4 numbers are not treated as numeric.
+ *
+ * <p>This method will never do a DNS lookup.
+ *
+ * @param address the address to parse.
+ * @return true if the supplied address is numeric, false otherwise.
+ */
+ public static boolean isNumericAddress(String address) {
+ return InetAddressUtils.isNumericAddress(address);
+ }
+
+ /**
+ * Returns an InetAddress corresponding to the given numeric address (such
+ * as {@code "192.168.0.1"} or {@code "2001:4860:800d::68"}).
+ *
+ * <p>See {@link #isNumericAddress(String)} (String)} for a definition as to what constitutes a
+ * numeric address.
+ *
+ * <p>This method will never do a DNS lookup.
+ *
+ * @param address the address to parse, must be numeric.
+ * @return an {@link InetAddress} instance corresponding to the address.
+ * @throws IllegalArgumentException if {@code address} is not a numeric address.
+ */
+ public static InetAddress parseNumericAddress(String address) {
+ return InetAddressUtils.parseNumericAddress(address);
+ }
+}
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index c431e40e..4eab49c 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Parcel;
import android.system.Os;
import android.util.Log;
@@ -299,8 +300,10 @@
* @param addrString
* @return the InetAddress
* @hide
+ * @deprecated Use {@link InetAddresses#parseNumericAddress(String)}, if possible.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @Deprecated
public static InetAddress numericToInetAddress(String addrString)
throws IllegalArgumentException {
return InetAddress.parseNumericAddress(addrString);
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index be8cf0e..fdd7488 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -390,7 +390,7 @@
/**
* Setup a new VPN.
*/
- void createVirtualNetwork(int netId, boolean hasDNS, boolean secure);
+ void createVirtualNetwork(int netId, boolean secure);
/**
* Remove a network.
diff --git a/core/java/android/os/NativeHandle.java b/core/java/android/os/NativeHandle.java
index fbecc8e..f7ffc37 100644
--- a/core/java/android/os/NativeHandle.java
+++ b/core/java/android/os/NativeHandle.java
@@ -16,6 +16,8 @@
package android.os;
+import static android.system.OsConstants.F_DUPFD_CLOEXEC;
+
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.system.ErrnoException;
@@ -108,7 +110,10 @@
FileDescriptor[] fds = new FileDescriptor[mFds.length];
try {
for (int i = 0; i < mFds.length; i++) {
- fds[i] = Os.dup(mFds[i]);
+ FileDescriptor newFd = new FileDescriptor();
+ int fdint = Os.fcntlInt(mFds[i], F_DUPFD_CLOEXEC, 0);
+ newFd.setInt$(fdint);
+ fds[i] = newFd;
}
} catch (ErrnoException e) {
e.rethrowAsIOException();
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 6de1ff4..63912ec 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -17,7 +17,11 @@
package android.os;
import static android.system.OsConstants.AF_UNIX;
+import static android.system.OsConstants.F_DUPFD;
+import static android.system.OsConstants.F_DUPFD_CLOEXEC;
+import static android.system.OsConstants.O_CLOEXEC;
import static android.system.OsConstants.SEEK_SET;
+import static android.system.OsConstants.SOCK_CLOEXEC;
import static android.system.OsConstants.SOCK_SEQPACKET;
import static android.system.OsConstants.SOCK_STREAM;
import static android.system.OsConstants.S_IROTH;
@@ -37,6 +41,7 @@
import android.util.Log;
import dalvik.system.CloseGuard;
+import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
import libcore.io.Memory;
@@ -293,7 +298,7 @@
}
private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException {
- final int flags = FileUtils.translateModePfdToPosix(mode);
+ final int flags = FileUtils.translateModePfdToPosix(mode) | ifAtLeastQ(O_CLOEXEC);
int realMode = S_IRWXU | S_IRWXG;
if ((mode & MODE_WORLD_READABLE) != 0) realMode |= S_IROTH;
@@ -315,7 +320,9 @@
*/
public static ParcelFileDescriptor dup(FileDescriptor orig) throws IOException {
try {
- final FileDescriptor fd = Os.dup(orig);
+ final FileDescriptor fd = new FileDescriptor();
+ int intfd = Os.fcntlInt(orig, (isAtLeastQ() ? F_DUPFD_CLOEXEC : F_DUPFD), 0);
+ fd.setInt$(intfd);
return new ParcelFileDescriptor(fd);
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
@@ -351,7 +358,9 @@
original.setInt$(fd);
try {
- final FileDescriptor dup = Os.dup(original);
+ final FileDescriptor dup = new FileDescriptor();
+ int intfd = Os.fcntlInt(original, (isAtLeastQ() ? F_DUPFD_CLOEXEC : F_DUPFD), 0);
+ dup.setInt$(intfd);
return new ParcelFileDescriptor(dup);
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
@@ -413,7 +422,7 @@
*/
public static ParcelFileDescriptor[] createPipe() throws IOException {
try {
- final FileDescriptor[] fds = Os.pipe();
+ final FileDescriptor[] fds = Os.pipe2(ifAtLeastQ(O_CLOEXEC));
return new ParcelFileDescriptor[] {
new ParcelFileDescriptor(fds[0]),
new ParcelFileDescriptor(fds[1]) };
@@ -435,7 +444,7 @@
public static ParcelFileDescriptor[] createReliablePipe() throws IOException {
try {
final FileDescriptor[] comm = createCommSocketPair();
- final FileDescriptor[] fds = Os.pipe();
+ final FileDescriptor[] fds = Os.pipe2(ifAtLeastQ(O_CLOEXEC));
return new ParcelFileDescriptor[] {
new ParcelFileDescriptor(fds[0], comm[0]),
new ParcelFileDescriptor(fds[1], comm[1]) };
@@ -459,7 +468,7 @@
try {
final FileDescriptor fd0 = new FileDescriptor();
final FileDescriptor fd1 = new FileDescriptor();
- Os.socketpair(AF_UNIX, type, 0, fd0, fd1);
+ Os.socketpair(AF_UNIX, type | ifAtLeastQ(SOCK_CLOEXEC), 0, fd0, fd1);
return new ParcelFileDescriptor[] {
new ParcelFileDescriptor(fd0),
new ParcelFileDescriptor(fd1) };
@@ -489,7 +498,7 @@
final FileDescriptor[] comm = createCommSocketPair();
final FileDescriptor fd0 = new FileDescriptor();
final FileDescriptor fd1 = new FileDescriptor();
- Os.socketpair(AF_UNIX, type, 0, fd0, fd1);
+ Os.socketpair(AF_UNIX, type | ifAtLeastQ(SOCK_CLOEXEC), 0, fd0, fd1);
return new ParcelFileDescriptor[] {
new ParcelFileDescriptor(fd0, comm[0]),
new ParcelFileDescriptor(fd1, comm[1]) };
@@ -505,7 +514,7 @@
// across multiple IO operations.
final FileDescriptor comm1 = new FileDescriptor();
final FileDescriptor comm2 = new FileDescriptor();
- Os.socketpair(AF_UNIX, SOCK_SEQPACKET, 0, comm1, comm2);
+ Os.socketpair(AF_UNIX, SOCK_SEQPACKET | ifAtLeastQ(SOCK_CLOEXEC), 0, comm1, comm2);
IoUtils.setBlocking(comm1, false);
IoUtils.setBlocking(comm2, false);
return new FileDescriptor[] { comm1, comm2 };
@@ -1111,4 +1120,12 @@
return "{" + status + ": " + msg + "}";
}
}
+
+ private static boolean isAtLeastQ() {
+ return (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q);
+ }
+
+ private static int ifAtLeastQ(int value) {
+ return isAtLeastQ() ? value : 0;
+ }
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 8b1d3c6..9594a71 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -132,6 +132,8 @@
public static final String PROP_VIRTUAL_DISK = "persist.sys.virtual_disk";
/** {@hide} */
public static final String PROP_ISOLATED_STORAGE = "persist.sys.isolated_storage";
+ /** {@hide} */
+ public static final String PROP_ISOLATED_STORAGE_SNAPSHOT = "sys.isolated_storage_snapshot";
/** {@hide} */
public static final String PROP_FORCE_AUDIO = "persist.fw.force_audio";
@@ -1540,7 +1542,9 @@
/** {@hide} */
@TestApi
public static boolean hasIsolatedStorage() {
- return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false);
+ // Prefer to use snapshot for current boot when available
+ return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE_SNAPSHOT,
+ SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false));
}
/**
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index f38f740..457453f 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -1002,6 +1002,7 @@
public static final int MICRO_KIND = 3;
public static final Point MINI_SIZE = new Point(512, 384);
+ public static final Point FULL_SCREEN_SIZE = new Point(1024, 786);
public static final Point MICRO_SIZE = new Point(96, 96);
}
@@ -1127,6 +1128,8 @@
final Point size;
if (kind == ThumbnailConstants.MICRO_KIND) {
size = ThumbnailConstants.MICRO_SIZE;
+ } else if (kind == ThumbnailConstants.FULL_SCREEN_KIND) {
+ size = ThumbnailConstants.FULL_SCREEN_SIZE;
} else if (kind == ThumbnailConstants.MINI_KIND) {
size = ThumbnailConstants.MINI_SIZE;
} else {
diff --git a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
index df58f52..028180d 100644
--- a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
+++ b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.content.pm.ParceledListSlice;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.contentcapture.ContentCaptureEvent;
@@ -31,10 +32,10 @@
@SystemApi
public final class ContentCaptureEventsRequest implements Parcelable {
- private final List<ContentCaptureEvent> mEvents;
+ private final ParceledListSlice<ContentCaptureEvent> mEvents;
/** @hide */
- public ContentCaptureEventsRequest(@NonNull List<ContentCaptureEvent> events) {
+ public ContentCaptureEventsRequest(@NonNull ParceledListSlice<ContentCaptureEvent> events) {
mEvents = events;
}
@@ -43,7 +44,7 @@
*/
@NonNull
public List<ContentCaptureEvent> getEvents() {
- return mEvents;
+ return mEvents.getList();
}
@Override
@@ -53,7 +54,7 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeTypedList(mEvents, flags);
+ parcel.writeParcelable(mEvents, flags);
}
public static final Parcelable.Creator<ContentCaptureEventsRequest> CREATOR =
@@ -61,8 +62,7 @@
@Override
public ContentCaptureEventsRequest createFromParcel(Parcel parcel) {
- return new ContentCaptureEventsRequest(parcel
- .createTypedArrayList(ContentCaptureEvent.CREATOR));
+ return new ContentCaptureEventsRequest(parcel.readParcelable(null));
}
@Override
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 58848fc..953ccf1 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -24,15 +24,27 @@
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
+import android.content.pm.ParceledListSlice;
+import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.util.ArrayMap;
import android.util.Log;
+import android.util.Slog;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.ContentCaptureEvent;
+import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.ContentCaptureSession;
import android.view.contentcapture.ContentCaptureSessionId;
+import android.view.contentcapture.IContentCaptureDirectManager;
+import com.android.internal.os.IResultReceiver;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.List;
import java.util.Set;
@@ -63,29 +75,16 @@
private Handler mHandler;
- private final IContentCaptureService mInterface = new IContentCaptureService.Stub() {
+ /**
+ * Binder that receives calls from the system server.
+ */
+ private final IContentCaptureService mServerInterface = new IContentCaptureService.Stub() {
@Override
- public void onSessionLifecycle(ContentCaptureContext context, String sessionId)
- throws RemoteException {
- if (context != null) {
- mHandler.sendMessage(
- obtainMessage(ContentCaptureService::handleOnCreateSession,
- ContentCaptureService.this, context, sessionId));
- } else {
- mHandler.sendMessage(
- obtainMessage(ContentCaptureService::handleOnDestroySession,
- ContentCaptureService.this, sessionId));
- }
- }
-
- @Override
- public void onContentCaptureEventsRequest(String sessionId,
- ContentCaptureEventsRequest request) {
- mHandler.sendMessage(
- obtainMessage(ContentCaptureService::handleOnContentCaptureEventsRequest,
- ContentCaptureService.this, sessionId, request));
-
+ public void onSessionStarted(ContentCaptureContext context, String sessionId, int uid,
+ IResultReceiver clientReceiver) {
+ mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnCreateSession,
+ ContentCaptureService.this, context, sessionId, uid, clientReceiver));
}
@Override
@@ -94,8 +93,36 @@
obtainMessage(ContentCaptureService::handleOnActivitySnapshot,
ContentCaptureService.this, sessionId, snapshotData));
}
+
+ @Override
+ public void onSessionFinished(String sessionId) {
+ mHandler.sendMessage(obtainMessage(ContentCaptureService::handleFinishSession,
+ ContentCaptureService.this, sessionId));
+ }
};
+ /**
+ * Binder that receives calls from the app.
+ */
+ private final IContentCaptureDirectManager mClientInterface =
+ new IContentCaptureDirectManager.Stub() {
+
+ @Override
+ public void sendEvents(String sessionId,
+ @SuppressWarnings("rawtypes") ParceledListSlice events) {
+ mHandler.sendMessage(obtainMessage(ContentCaptureService::handleSendEvents,
+ ContentCaptureService.this, sessionId, Binder.getCallingUid(), events));
+ }
+ };
+
+ /**
+ * List of sessions per UID.
+ *
+ * <p>This map is populated when an session is started, which is called by the system server
+ * and can be trusted. Then subsequent calls made by the app are verified against this map.
+ */
+ private final ArrayMap<String, Integer> mSessionsByUid = new ArrayMap<>();
+
@CallSuper
@Override
public void onCreate() {
@@ -107,7 +134,7 @@
@Override
public final IBinder onBind(Intent intent) {
if (SERVICE_INTERFACE.equals(intent.getAction())) {
- return mInterface.asBinder();
+ return mServerInterface.asBinder();
}
Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
return null;
@@ -196,8 +223,10 @@
* @param sessionId the session's Id
* @param request the events
*/
- public abstract void onContentCaptureEventsRequest(@NonNull ContentCaptureSessionId sessionId,
- @NonNull ContentCaptureEventsRequest request);
+ public void onContentCaptureEventsRequest(@NonNull ContentCaptureSessionId sessionId,
+ @NonNull ContentCaptureEventsRequest request) {
+ if (VERBOSE) Log.v(TAG, "onContentCaptureEventsRequest(id=" + sessionId + ")");
+ }
/**
* Notifies the service of {@link SnapshotData snapshot data} associated with a session.
@@ -212,10 +241,22 @@
* Destroys the content capture session.
*
* @param sessionId the id of the session to destroy
- */
+ * */
public void onDestroyContentCaptureSession(@NonNull ContentCaptureSessionId sessionId) {
- if (VERBOSE) {
- Log.v(TAG, "onDestroyContentCaptureSession(id=" + sessionId + ")");
+ if (VERBOSE) Log.v(TAG, "onDestroyContentCaptureSession(id=" + sessionId + ")");
+ }
+
+ @Override
+ @CallSuper
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ final int size = mSessionsByUid.size();
+ pw.print("Number sessions: "); pw.println(size);
+ if (size > 0) {
+ final String prefix = " ";
+ for (int i = 0; i < size; i++) {
+ pw.print(prefix); pw.print(mSessionsByUid.keyAt(i));
+ pw.print(": uid="); pw.println(mSessionsByUid.valueAt(i));
+ }
}
}
@@ -223,13 +264,19 @@
// so we don't need to create a temporary InteractionSessionId for each event.
private void handleOnCreateSession(@NonNull ContentCaptureContext context,
- @NonNull String sessionId) {
+ @NonNull String sessionId, int uid, IResultReceiver clientReceiver) {
+ mSessionsByUid.put(sessionId, uid);
onCreateContentCaptureSession(context, new ContentCaptureSessionId(sessionId));
+ setClientState(clientReceiver, ContentCaptureSession.STATE_ACTIVE,
+ mClientInterface.asBinder());
}
- private void handleOnContentCaptureEventsRequest(@NonNull String sessionId,
- @NonNull ContentCaptureEventsRequest request) {
- onContentCaptureEventsRequest(new ContentCaptureSessionId(sessionId), request);
+ private void handleSendEvents(@NonNull String sessionId, int uid,
+ @NonNull ParceledListSlice<ContentCaptureEvent> events) {
+ if (handleIsRightCallerFor(sessionId, uid)) {
+ onContentCaptureEventsRequest(new ContentCaptureSessionId(sessionId),
+ new ContentCaptureEventsRequest(events));
+ }
}
private void handleOnActivitySnapshot(@NonNull String sessionId,
@@ -237,7 +284,52 @@
onActivitySnapshot(new ContentCaptureSessionId(sessionId), snapshotData);
}
- private void handleOnDestroySession(@NonNull String sessionId) {
+ private void handleFinishSession(@NonNull String sessionId) {
+ mSessionsByUid.remove(sessionId);
onDestroyContentCaptureSession(new ContentCaptureSessionId(sessionId));
}
+
+ /**
+ * Checks if the given {@code uid} owns the session.
+ */
+ private boolean handleIsRightCallerFor(@NonNull String sessionId, int uid) {
+ final Integer rightUid = mSessionsByUid.get(sessionId);
+ if (rightUid == null) {
+ if (VERBOSE) Log.v(TAG, "No session for " + sessionId);
+ // Just ignore, as the session could have finished
+ return false;
+ }
+ if (rightUid != uid) {
+ Log.e(TAG, "invalid call from UID " + uid + ": session " + sessionId + " belongs to "
+ + rightUid);
+ //TODO(b/111276913): log metrics as this could be a malicious app forging a sessionId
+ return false;
+ }
+ return true;
+
+ }
+
+ /**
+ * Sends the state of the {@link ContentCaptureManager} in the cleint app.
+ *
+ * @param clientReceiver receiver in the client app.
+ * @param binder handle to the {@code IContentCaptureDirectManager} object that resides in the
+ * service.
+ * @hide
+ */
+ public static void setClientState(@NonNull IResultReceiver clientReceiver,
+ int sessionStatus, @Nullable IBinder binder) {
+ try {
+ final Bundle extras;
+ if (binder != null) {
+ extras = new Bundle();
+ extras.putBinder(ContentCaptureSession.EXTRA_BINDER, binder);
+ } else {
+ extras = null;
+ }
+ clientReceiver.send(sessionStatus, extras);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error async reporting result to client: " + e);
+ }
+ }
}
diff --git a/core/java/android/service/contentcapture/IContentCaptureService.aidl b/core/java/android/service/contentcapture/IContentCaptureService.aidl
index 8167be9..20e8e99 100644
--- a/core/java/android/service/contentcapture/IContentCaptureService.aidl
+++ b/core/java/android/service/contentcapture/IContentCaptureService.aidl
@@ -16,10 +16,11 @@
package android.service.contentcapture;
-import android.service.contentcapture.ContentCaptureEventsRequest;
import android.service.contentcapture.SnapshotData;
import android.view.contentcapture.ContentCaptureContext;
+import com.android.internal.os.IResultReceiver;
+
import java.util.List;
/**
@@ -28,11 +29,8 @@
* @hide
*/
oneway interface IContentCaptureService {
-
- // Called when session is created (context not null) or destroyed (context null)
- void onSessionLifecycle(in ContentCaptureContext context, String sessionId);
-
- void onContentCaptureEventsRequest(String sessionId, in ContentCaptureEventsRequest request);
-
+ void onSessionStarted(in ContentCaptureContext context, String sessionId, int uid,
+ in IResultReceiver clientReceiver);
+ void onSessionFinished(String sessionId);
void onActivitySnapshot(String sessionId, in SnapshotData snapshotData);
}
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index ada7853..60eeeea 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -201,22 +201,16 @@
int bottom = top + childHeight;
int layoutLeft = left;
int layoutRight = right;
- if (child == mExpandButton && mShowExpandButtonAtEnd) {
- layoutRight = end - mContentEndMargin;
- end = layoutLeft = layoutRight - child.getMeasuredWidth();
- }
- if (child == mProfileBadge) {
- int paddingEnd = getPaddingEnd();
- if (mShowWorkBadgeAtEnd) {
- paddingEnd = mContentEndMargin;
+ if ((child == mExpandButton && mShowExpandButtonAtEnd)
+ || child == mProfileBadge
+ || child == mAppOps) {
+ if (end == getMeasuredWidth()) {
+ layoutRight = end - mContentEndMargin;
+ } else {
+ layoutRight = end - params.getMarginEnd();
}
- layoutRight = end - paddingEnd;
- end = layoutLeft = layoutRight - child.getMeasuredWidth();
- }
- if (child == mAppOps) {
- int paddingEnd = mContentEndMargin;
- layoutRight = end - paddingEnd;
- end = layoutLeft = layoutRight - child.getMeasuredWidth();
+ layoutLeft = layoutRight - child.getMeasuredWidth();
+ end = layoutLeft - params.getMarginStart();
}
if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
int ltrLeft = layoutLeft;
diff --git a/core/java/android/view/TouchDelegate.java b/core/java/android/view/TouchDelegate.java
index bef9f07..2ea95e9 100644
--- a/core/java/android/view/TouchDelegate.java
+++ b/core/java/android/view/TouchDelegate.java
@@ -157,6 +157,11 @@
* Forward hover events to the delegate view if the event is within the bounds
* specified in the constructor and touch exploration is enabled.
*
+ * <p>This method is provided for accessibility purposes so touch exploration, which is
+ * commonly used by screen readers, can properly place accessibility focus on views that
+ * use touch delegates. Therefore, touch exploration must be enabled for hover events
+ * to be dispatched through the delegate.</p>
+ *
* @param event The hover event to forward
* @return True if the event was consumed by the delegate, false otherwise.
*
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 632955d..f411cf7 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -27,9 +27,11 @@
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.ParceledListSlice;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
@@ -45,6 +47,8 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -100,6 +104,13 @@
public static final int STATE_DISABLED = 3;
/**
+ * Session is disabled because its id already existed on server.
+ *
+ * @hide
+ */
+ public static final int STATE_DISABLED_DUPLICATED_ID = 4;
+
+ /**
* Handler message used to flush the buffer.
*/
private static final int MSG_FLUSH = 1;
@@ -116,6 +127,13 @@
// TODO(b/121044064): use settings
private static final int FLUSHING_FREQUENCY_MS = 5_000;
+
+ /**
+ * Name of the {@link IResultReceiver} extra used to pass the binder interface to the service.
+ * @hide
+ */
+ public static final String EXTRA_BINDER = "binder";
+
private final CloseGuard mCloseGuard = CloseGuard.get();
@NonNull
@@ -127,8 +145,21 @@
@NonNull
private final Handler mHandler;
+ /**
+ * Interface to the system_server binder object - it's only used to start the session (and
+ * notify when the session is finished).
+ */
@Nullable
- private final IContentCaptureManager mService;
+ private final IContentCaptureManager mSystemServerInterface;
+
+ /**
+ * Direct interface to the service binder object - it's used to send the events, including the
+ * last ones (when the session is finished)
+ */
+ @Nullable
+ private IContentCaptureDirectManager mDirectServiceInterface;
+ @Nullable
+ private DeathRecipient mDirectServiceVulture;
@Nullable
private final String mId = UUID.randomUUID().toString();
@@ -165,11 +196,11 @@
/** @hide */
protected ContentCaptureSession(@NonNull Context context, @NonNull Handler handler,
- @Nullable IContentCaptureManager service, @NonNull AtomicBoolean disabled,
+ @Nullable IContentCaptureManager systemServerInterface, @NonNull AtomicBoolean disabled,
@Nullable ContentCaptureContext clientContext) {
mContext = context;
mHandler = handler;
- mService = service;
+ mSystemServerInterface = systemServerInterface;
mDisabled = disabled;
mClientContext = clientContext;
mCloseGuard.open("destroy");
@@ -227,6 +258,8 @@
Log.v(TAG, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId);
}
+ flush();
+
mHandler.sendMessage(obtainMessage(ContentCaptureSession::handleDestroySession, this));
mCloseGuard.close();
}
@@ -267,11 +300,20 @@
final int flags = 0; // TODO(b/111276913): get proper flags
try {
- mService.startSession(mContext.getUserId(), mApplicationToken, componentName,
- mId, mClientContext, flags, new IResultReceiver.Stub() {
+ mSystemServerInterface.startSession(mContext.getUserId(), mApplicationToken,
+ componentName, mId, mClientContext, flags, new IResultReceiver.Stub() {
@Override
public void send(int resultCode, Bundle resultData) {
- handleSessionStarted(resultCode);
+ IBinder binder = null;
+ if (resultData != null) {
+ binder = resultData.getBinder(EXTRA_BINDER);
+ if (binder == null) {
+ Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
+ handleResetState();
+ return;
+ }
+ }
+ handleSessionStarted(resultCode, binder);
}
});
} catch (RemoteException e) {
@@ -280,12 +322,38 @@
}
}
- private void handleSessionStarted(int resultCode) {
+ /**
+ * Callback from {@code system_server} after call to
+ * {@link IContentCaptureManager#startSession(int, IBinder, ComponentName, String,
+ * ContentCaptureContext, int, IResultReceiver)}.
+ *
+ * @param resultCode session state
+ * @param binder handle to {@link IContentCaptureDirectManager}
+ */
+ private void handleSessionStarted(int resultCode, @Nullable IBinder binder) {
mState = resultCode;
- mDisabled.set(mState == STATE_DISABLED);
+ if (binder != null) {
+ mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder);
+ mDirectServiceVulture = () -> {
+ Log.w(TAG, "Destroying session " + mId + " because service died");
+ destroy();
+ };
+ try {
+ binder.linkToDeath(mDirectServiceVulture, 0);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to link to death on " + binder + ": " + e);
+ }
+ }
+ if (resultCode == STATE_DISABLED || resultCode == STATE_DISABLED_DUPLICATED_ID) {
+ mDisabled.set(true);
+ handleResetSession(/* resetState= */ false);
+ } else {
+ mDisabled.set(false);
+ }
if (VERBOSE) {
Log.v(TAG, "handleSessionStarted() result: code=" + resultCode + ", id=" + mId
- + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get());
+ + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()
+ + ", binder=" + binder);
}
}
@@ -307,7 +375,7 @@
final boolean bufferEvent = numberEvents < MAX_BUFFER_SIZE;
if (bufferEvent && !forceFlush) {
- handleScheduleFlush();
+ handleScheduleFlush(/* checkExisting= */ true);
return;
}
@@ -331,8 +399,8 @@
handleForceFlush();
}
- private void handleScheduleFlush() {
- if (mHandler.hasMessages(MSG_FLUSH)) {
+ private void handleScheduleFlush(boolean checkExisting) {
+ if (checkExisting && mHandler.hasMessages(MSG_FLUSH)) {
// "Renew" the flush message by removing the previous one
mHandler.removeMessages(MSG_FLUSH);
}
@@ -356,52 +424,80 @@
private void handleForceFlush() {
if (mEvents == null) return;
+ if (mDirectServiceInterface == null) {
+ Log.w(TAG, "handleForceFlush(): client not available yet");
+ if (!mHandler.hasMessages(MSG_FLUSH)) {
+ handleScheduleFlush(/* checkExisting= */ false);
+ }
+ return;
+ }
+
final int numberEvents = mEvents.size();
try {
if (DEBUG) {
Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName());
}
mHandler.removeMessages(MSG_FLUSH);
- mService.sendEvents(mContext.getUserId(), mId, mEvents);
- // TODO(b/111276913): decide whether we should clear or set it to null, as each has
- // its own advantages: clearing will save extra allocations while the session is
- // active, while setting to null would save memory if there's no more event coming.
- mEvents.clear();
+
+ final ParceledListSlice<ContentCaptureEvent> events = handleClearEvents();
+ mDirectServiceInterface.sendEvents(mId, events);
} catch (RemoteException e) {
Log.w(TAG, "Error sending " + numberEvents + " for " + getActivityDebugName()
+ ": " + e);
}
}
+ /**
+ * Resets the buffer and return a {@link ParceledListSlice} with the previous events.
+ */
+ @NonNull
+ private ParceledListSlice<ContentCaptureEvent> handleClearEvents() {
+ // NOTE: we must save a reference to the current mEvents and then set it to to null,
+ // otherwise clearing it would clear it in the receiving side if the service is also local.
+ final List<ContentCaptureEvent> events = mEvents == null
+ ? Collections.emptyList()
+ : mEvents;
+ mEvents = null;
+ return new ParceledListSlice<>(events);
+ }
+
private void handleDestroySession() {
//TODO(b/111276913): right now both the ContentEvents and lifecycle sessions are sent
// to system_server, so it's ok to call both in sequence here. But once we split
// them so the events are sent directly to the service, we need to make sure they're
// sent in order.
- try {
- if (DEBUG) {
- Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
- + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
- + getActivityDebugName());
- }
-
- mService.finishSession(mContext.getUserId(), mId, mEvents);
- } catch (RemoteException e) {
- Log.e(TAG, "Error destroying session " + mId + " for " + getActivityDebugName()
- + ": " + e);
- } finally {
- handleResetState();
+ if (DEBUG) {
+ Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
+ + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
+ + getActivityDebugName());
}
+
+ try {
+ mSystemServerInterface.finishSession(mContext.getUserId(), mId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error destroying system-service session " + mId + " for "
+ + getActivityDebugName() + ": " + e);
+ }
+ }
+
+ private void handleResetState() {
+ handleResetSession(/* resetState= */ true);
}
// TODO(b/111276913): once we support multiple sessions, we might need to move some of these
// clearings out.
- private void handleResetState() {
- mState = STATE_UNKNOWN;
+ private void handleResetSession(boolean resetState) {
+ if (resetState) {
+ mState = STATE_UNKNOWN;
+ }
mContentCaptureSessionId = null;
mApplicationToken = null;
mComponentName = null;
mEvents = null;
+ if (mDirectServiceInterface != null) {
+ mDirectServiceInterface.asBinder().unlinkToDeath(mDirectServiceVulture, 0);
+ }
+ mDirectServiceInterface = null;
mHandler.removeMessages(MSG_FLUSH);
}
@@ -492,15 +588,20 @@
}
private boolean isContentCaptureEnabled() {
- return mService != null && !mDisabled.get();
+ return mSystemServerInterface != null && !mDisabled.get();
}
void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
pw.print(prefix); pw.print("id: "); pw.println(mId);
pw.print(prefix); pw.print("mContext: "); pw.println(mContext);
pw.print(prefix); pw.print("user: "); pw.println(mContext.getUserId());
- if (mService != null) {
- pw.print(prefix); pw.print("mService: "); pw.println(mService);
+ if (mSystemServerInterface != null) {
+ pw.print(prefix); pw.print("mSystemServerInterface: ");
+ pw.println(mSystemServerInterface);
+ }
+ if (mDirectServiceInterface != null) {
+ pw.print(prefix); pw.print("mDirectServiceInterface: ");
+ pw.println(mDirectServiceInterface);
}
if (mClientContext != null) {
// NOTE: we don't dump clientContent because it could have PII
@@ -563,6 +664,8 @@
return "ACTIVE";
case STATE_DISABLED:
return "DISABLED";
+ case STATE_DISABLED_DUPLICATED_ID:
+ return "DISABLED_DUPLICATED_ID";
default:
return "INVALID:" + state;
}
diff --git a/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl b/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl
new file mode 100644
index 0000000..145fc16
--- /dev/null
+++ b/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.contentcapture;
+
+import android.content.pm.ParceledListSlice;
+import android.view.contentcapture.ContentCaptureEvent;
+
+/**
+ * Interface between an app (ContentCaptureManager / ContentCaptureSession) and the app providing
+ * the ContentCaptureService implementation.
+ *
+ * @hide
+ */
+oneway interface IContentCaptureDirectManager {
+ void sendEvents(in String sessionId, in ParceledListSlice events);
+}
diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
index 2002c5c..cbd3701 100644
--- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl
+++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
@@ -26,12 +26,14 @@
import java.util.List;
/**
- * {@hide}
- */
+ * Interface between an app (ContentCaptureManager / ContentCaptureSession) and the system-server
+ * implementation service (ContentCaptureManagerService).
+ *
+ * @hide
+ */
oneway interface IContentCaptureManager {
void startSession(int userId, IBinder activityToken, in ComponentName componentName,
String sessionId, in ContentCaptureContext clientContext, int flags,
in IResultReceiver result);
- void finishSession(int userId, String sessionId, in List<ContentCaptureEvent> events);
- void sendEvents(int userId, in String sessionId, in List<ContentCaptureEvent> events);
+ void finishSession(int userId, String sessionId);
}
diff --git a/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl b/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl
new file mode 100644
index 0000000..fa5c30a
--- /dev/null
+++ b/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+// Iterface to observe op note/checks of ops
+oneway interface IAppOpsNotedCallback {
+ void opNoted(int op, int uid, String packageName, int mode);
+}
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 049103b..e571656 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -21,6 +21,7 @@
import android.os.Bundle;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsActiveCallback;
+import com.android.internal.app.IAppOpsNotedCallback;
interface IAppOpsService {
// These first methods are also called by native code, so must
@@ -61,4 +62,7 @@
boolean isOperationActive(int code, int uid, String packageName);
void startWatchingModeWithFlags(int op, String packageName, int flags, IAppOpsCallback callback);
+
+ void startWatchingNoted(in int[] ops, IAppOpsNotedCallback callback);
+ void stopWatchingNoted(IAppOpsNotedCallback callback);
}
diff --git a/core/java/com/android/internal/app/procstats/AssociationState.java b/core/java/com/android/internal/app/procstats/AssociationState.java
index 4da3391..2df5158 100644
--- a/core/java/com/android/internal/app/procstats/AssociationState.java
+++ b/core/java/com/android/internal/app/procstats/AssociationState.java
@@ -17,6 +17,7 @@
package com.android.internal.app.procstats;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -192,9 +193,16 @@
*/
String mProcess;
- SourceKey(int uid, String process) {
+ /**
+ * Optional package name, or null; consider this final. Not final just to avoid a
+ * temporary object during lookup.
+ */
+ @Nullable String mPackage;
+
+ SourceKey(int uid, String process, String pkg) {
mUid = uid;
mProcess = process;
+ mPackage = pkg;
}
public boolean equals(Object o) {
@@ -202,12 +210,14 @@
return false;
}
SourceKey s = (SourceKey) o;
- return s.mUid == mUid && Objects.equals(s.mProcess, mProcess);
+ return s.mUid == mUid && Objects.equals(s.mProcess, mProcess)
+ && Objects.equals(s.mPackage, mPackage);
}
@Override
public int hashCode() {
- return Integer.hashCode(mUid) ^ (mProcess == null ? 0 : mProcess.hashCode());
+ return Integer.hashCode(mUid) ^ (mProcess == null ? 0 : mProcess.hashCode())
+ ^ (mPackage == null ? 0 : (mPackage.hashCode() * 33));
}
@Override
@@ -217,6 +227,8 @@
UserHandle.formatUid(sb, mUid);
sb.append(' ');
sb.append(mProcess);
+ sb.append(' ');
+ sb.append(mPackage);
sb.append('}');
return sb.toString();
}
@@ -227,7 +239,7 @@
*/
private final ArrayMap<SourceKey, SourceState> mSources = new ArrayMap<>();
- private final SourceKey mTmpSourceKey = new SourceKey(0, null);
+ private final SourceKey mTmpSourceKey = new SourceKey(0, null, null);
private ProcessState mProc;
@@ -266,12 +278,13 @@
mProc = proc;
}
- public SourceState startSource(int uid, String processName) {
+ public SourceState startSource(int uid, String processName, String packageName) {
mTmpSourceKey.mUid = uid;
mTmpSourceKey.mProcess = processName;
+ mTmpSourceKey.mPackage = packageName;
SourceState src = mSources.get(mTmpSourceKey);
if (src == null) {
- SourceKey key = new SourceKey(uid, processName);
+ SourceKey key = new SourceKey(uid, processName, packageName);
src = new SourceState(key);
mSources.put(key, src);
}
@@ -379,6 +392,7 @@
final SourceState src = mSources.valueAt(isrc);
out.writeInt(key.mUid);
stats.writeCommonString(out, key.mProcess);
+ stats.writeCommonString(out, key.mPackage);
out.writeInt(src.mCount);
out.writeLong(src.mDuration);
out.writeInt(src.mActiveCount);
@@ -405,7 +419,8 @@
for (int isrc = 0; isrc < NSRC; isrc++) {
final int uid = in.readInt();
final String procName = stats.readCommonString(in, parcelVersion);
- final SourceKey key = new SourceKey(uid, procName);
+ final String pkgName = stats.readCommonString(in, parcelVersion);
+ final SourceKey key = new SourceKey(uid, procName, pkgName);
final SourceState src = new SourceState(key);
src.mCount = in.readInt();
src.mDuration = in.readLong();
@@ -445,10 +460,11 @@
}
}
- public boolean hasProcess(String procName) {
+ public boolean hasProcessOrPackage(String procName) {
final int NSRC = mSources.size();
for (int isrc = 0; isrc < NSRC; isrc++) {
- if (mSources.keyAt(isrc).mProcess.equals(procName)) {
+ final SourceKey key = mSources.keyAt(isrc);
+ if (procName.equals(key.mProcess) || procName.equals(key.mPackage)) {
return true;
}
}
@@ -466,14 +482,20 @@
for (int isrc = 0; isrc < NSRC; isrc++) {
final SourceKey key = mSources.keyAt(isrc);
final SourceState src = mSources.valueAt(isrc);
- if (reqPackage != null && !reqPackage.equals(key.mProcess)) {
+ if (reqPackage != null && !reqPackage.equals(key.mProcess)
+ && !reqPackage.equals(key.mPackage)) {
continue;
}
pw.print(prefixInner);
pw.print("<- ");
pw.print(key.mProcess);
- pw.print(" / ");
+ pw.print("/");
UserHandle.formatUid(pw, key.mUid);
+ if (key.mPackage != null) {
+ pw.print(" (");
+ pw.print(key.mPackage);
+ pw.print(")");
+ }
pw.println(":");
pw.print(prefixInner);
pw.print(" Total count ");
@@ -683,6 +705,7 @@
final SourceState src = mSources.valueAt(isrc);
final long sourceToken = proto.start(PackageAssociationProcessStatsProto.SOURCES);
proto.write(PackageAssociationSourceProcessStatsProto.PROCESS_NAME, key.mProcess);
+ proto.write(PackageAssociationSourceProcessStatsProto.PACKAGE_NAME, key.mPackage);
proto.write(PackageAssociationSourceProcessStatsProto.PROCESS_UID, key.mUid);
proto.write(PackageAssociationSourceProcessStatsProto.TOTAL_COUNT, src.mCount);
long duration = src.mDuration;
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 9ee583a..9b9b771 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -178,7 +178,7 @@
{"proc", "pkg-proc", "pkg-svc", "pkg-asc", "pkg-all", "all"};
// Current version of the parcel format.
- private static final int PARCEL_VERSION = 34;
+ private static final int PARCEL_VERSION = 35;
// In-memory Parcel magic number, used to detect attempts to unmarshall bad data
private static final int MAGIC = 0x50535454;
@@ -1490,7 +1490,7 @@
// package, so that if so we print those.
for (int iasc = 0; iasc < NASCS; iasc++) {
AssociationState asc = pkgState.mAssociations.valueAt(iasc);
- if (asc.hasProcess(reqPackage)) {
+ if (asc.hasProcessOrPackage(reqPackage)) {
onlyAssociations = true;
break;
}
@@ -1591,7 +1591,7 @@
for (int iasc = 0; iasc < NASCS; iasc++) {
AssociationState asc = pkgState.mAssociations.valueAt(iasc);
if (!pkgMatch && !reqPackage.equals(asc.getProcessName())) {
- if (!onlyAssociations || !asc.hasProcess(reqPackage)) {
+ if (!onlyAssociations || !asc.hasProcessOrPackage(reqPackage)) {
continue;
}
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 5118e5f..9087dd2 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -67,7 +67,8 @@
in NotificationVisibility[] noLongerVisibleKeys);
void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded);
void onNotificationDirectReplied(String key);
- void onNotificationSmartRepliesAdded(in String key, in int replyCount);
+ void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount, int smartActionCount,
+ boolean generatedByAsssistant);
void onNotificationSmartReplySent(in String key, in int replyIndex, in CharSequence reply, boolean generatedByAssistant);
void onNotificationSettingsViewed(String key);
void setSystemUiVisibility(int displayId, int vis, int mask, String cause);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 43f8d00..21fa75e 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -274,6 +274,7 @@
"libicuuc",
"libmedia",
"libmediametrics",
+ "libmeminfo",
"libaudioclient",
"libjpeg",
"libusbhost",
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index c32de0a..12ca78a 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -725,9 +725,10 @@
return NULL;
}
- // Map the pixels in place and take ownership of the ashmem region.
- nativeBitmap = sk_sp<Bitmap>(GraphicsJNI::mapAshmemBitmap(env, bitmap.get(),
- dupFd, const_cast<void*>(blob.data()), size, !isMutable));
+ // Map the pixels in place and take ownership of the ashmem region. We must also respect the
+ // rowBytes value already set on the bitmap instead of attempting to compute our own.
+ nativeBitmap = Bitmap::createFrom(bitmap->info(), bitmap->rowBytes(), dupFd,
+ const_cast<void*>(blob.data()), size, !isMutable);
if (!nativeBitmap) {
close(dupFd);
blob.release();
@@ -1097,21 +1098,20 @@
SkBitmap src;
hwuiBitmap.getSkBitmap(&src);
- SkBitmap result;
- HeapAllocator allocator;
- if (!bitmapCopyTo(&result, hwuiBitmap.info().colorType(), src, &allocator)) {
+ if (src.pixelRef() == nullptr) {
doThrowRE(env, "Could not copy a hardware bitmap.");
return NULL;
}
- return createBitmap(env, allocator.getStorageObjAndReset(), getPremulBitmapCreateFlags(false));
+
+ sk_sp<Bitmap> bitmap = Bitmap::createFrom(src.info(), *src.pixelRef());
+ return createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(false));
}
static jobject Bitmap_createHardwareBitmap(JNIEnv* env, jobject, jobject graphicBuffer) {
sp<GraphicBuffer> buffer(graphicBufferForJavaObject(env, graphicBuffer));
- // Bitmap::createFrom currently assumes SRGB color space for RGBA images.
// To support any color space, we need to pass an additional ColorSpace argument to
// java Bitmap.createHardwareBitmap.
- sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer);
+ sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, SkColorSpace::MakeSRGB());
if (!bitmap.get()) {
ALOGW("failed to create hardware bitmap from graphic buffer");
return NULL;
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 26af15e..67d0c8a 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -424,36 +424,6 @@
///////////////////////////////////////////////////////////////////////////////
-android::Bitmap* GraphicsJNI::mapAshmemBitmap(JNIEnv* env, SkBitmap* bitmap,
- int fd, void* addr, size_t size, bool readOnly) {
- const SkImageInfo& info = bitmap->info();
- if (info.colorType() == kUnknown_SkColorType) {
- doThrowIAE(env, "unknown bitmap configuration");
- return nullptr;
- }
-
- if (!addr) {
- // Map existing ashmem region if not already mapped.
- int flags = readOnly ? (PROT_READ) : (PROT_READ | PROT_WRITE);
- size = ashmem_get_size_region(fd);
- addr = mmap(NULL, size, flags, MAP_SHARED, fd, 0);
- if (addr == MAP_FAILED) {
- return nullptr;
- }
- }
-
- // we must respect the rowBytes value already set on the bitmap instead of
- // attempting to compute our own.
- const size_t rowBytes = bitmap->rowBytes();
-
- auto wrapper = new android::Bitmap(addr, fd, size, info, rowBytes);
- wrapper->getSkBitmap(bitmap);
- if (readOnly) {
- bitmap->pixelRef()->setImmutable();
- }
- return wrapper;
-}
-
SkColorSpaceTransferFn GraphicsJNI::getNativeTransferParameters(JNIEnv* env, jobject transferParams) {
SkColorSpaceTransferFn p;
p.fA = (float) env->GetDoubleField(transferParams, gTransferParams_aFieldID);
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index cee3c46..b0bd683 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -85,9 +85,6 @@
static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
- static android::Bitmap* mapAshmemBitmap(JNIEnv* env, SkBitmap* bitmap,
- int fd, void* addr, size_t size, bool readOnly);
-
/**
* Given a bitmap we natively allocate a memory block to store the contents
* of that bitmap. The memory is then attached to the bitmap via an
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index fa1da4b..888dab1 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -32,6 +32,7 @@
#include <atomic>
#include <iomanip>
#include <string>
+#include <vector>
#include <debuggerd/client.h>
#include <log/log.h>
@@ -41,6 +42,7 @@
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedUtfChars.h>
#include "jni.h"
+#include <meminfo/sysmeminfo.h>
#include <memtrack/memtrack.h>
#include <memunreachable/memunreachable.h>
#include "android_os_Debug.h"
@@ -712,6 +714,8 @@
return vmalloc_allocated_size;
}
+// The 1:1 mapping of MEMINFO_* enums here must match with the constants from
+// Debug.java.
enum {
MEMINFO_TOTAL,
MEMINFO_FREE,
@@ -731,138 +735,43 @@
MEMINFO_COUNT
};
-static long long get_zram_mem_used()
-{
-#define ZRAM_SYSFS "/sys/block/zram0/"
- UniqueFile mm_stat_file = MakeUniqueFile(ZRAM_SYSFS "mm_stat", "re");
- if (mm_stat_file) {
- long long mem_used_total = 0;
-
- int matched = fscanf(mm_stat_file.get(), "%*d %*d %lld %*d %*d %*d %*d", &mem_used_total);
- if (matched != 1)
- ALOGW("failed to parse " ZRAM_SYSFS "mm_stat");
-
- return mem_used_total;
- }
-
- UniqueFile mem_used_total_file = MakeUniqueFile(ZRAM_SYSFS "mem_used_total", "re");
- if (mem_used_total_file) {
- long long mem_used_total = 0;
-
- int matched = fscanf(mem_used_total_file.get(), "%lld", &mem_used_total);
- if (matched != 1)
- ALOGW("failed to parse " ZRAM_SYSFS "mem_used_total");
-
- return mem_used_total;
- }
-
- return 0;
-}
-
static void android_os_Debug_getMemInfo(JNIEnv *env, jobject clazz, jlongArray out)
{
- char buffer[4096];
- size_t numFound = 0;
-
if (out == NULL) {
jniThrowNullPointerException(env, "out == null");
return;
}
- int fd = open("/proc/meminfo", O_RDONLY | O_CLOEXEC);
-
- if (fd < 0) {
- ALOGW("Unable to open /proc/meminfo: %s\n", strerror(errno));
+ int outLen = env->GetArrayLength(out);
+ if (outLen < MEMINFO_COUNT) {
+ jniThrowRuntimeException(env, "outLen < MEMINFO_COUNT");
return;
}
- int len = read(fd, buffer, sizeof(buffer)-1);
- close(fd);
-
- if (len < 0) {
- ALOGW("Empty /proc/meminfo");
+ // Read system memory info including ZRAM. The values are stored in the vector
+ // in the same order as MEMINFO_* enum
+ std::vector<uint64_t> mem(MEMINFO_COUNT);
+ std::vector<std::string> tags(::android::meminfo::SysMemInfo::kDefaultSysMemInfoTags);
+ tags.insert(tags.begin() + MEMINFO_ZRAM_TOTAL, "Zram:");
+ ::android::meminfo::SysMemInfo smi;
+ if (!smi.ReadMemInfo(tags, &mem)) {
+ jniThrowRuntimeException(env, "SysMemInfo read failed");
return;
}
- buffer[len] = 0;
- static const char* const tags[] = {
- "MemTotal:",
- "MemFree:",
- "Buffers:",
- "Cached:",
- "Shmem:",
- "Slab:",
- "SReclaimable:",
- "SUnreclaim:",
- "SwapTotal:",
- "SwapFree:",
- "ZRam:",
- "Mapped:",
- "VmallocUsed:",
- "PageTables:",
- "KernelStack:",
- NULL
- };
- static const int tagsLen[] = {
- 9,
- 8,
- 8,
- 7,
- 6,
- 5,
- 13,
- 11,
- 10,
- 9,
- 5,
- 7,
- 12,
- 11,
- 12,
- 0
- };
- long mem[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-
- char* p = buffer;
- while (*p && numFound < (sizeof(tagsLen) / sizeof(tagsLen[0]))) {
- int i = 0;
- while (tags[i]) {
- if (strncmp(p, tags[i], tagsLen[i]) == 0) {
- p += tagsLen[i];
- while (*p == ' ') p++;
- char* num = p;
- while (*p >= '0' && *p <= '9') p++;
- if (*p != 0) {
- *p = 0;
- p++;
- }
- mem[i] = atoll(num);
- numFound++;
- break;
- }
- i++;
- }
- while (*p && *p != '\n') {
- p++;
- }
- if (*p) p++;
- }
-
- mem[MEMINFO_ZRAM_TOTAL] = get_zram_mem_used() / 1024;
- // Recompute Vmalloc Used since the value in meminfo
- // doesn't account for I/O remapping which doesn't use RAM.
- mem[MEMINFO_VMALLOC_USED] = get_allocated_vmalloc_memory() / 1024;
-
- int maxNum = env->GetArrayLength(out);
- if (maxNum > MEMINFO_COUNT) {
- maxNum = MEMINFO_COUNT;
- }
jlong* outArray = env->GetLongArrayElements(out, 0);
if (outArray != NULL) {
- for (int i=0; i<maxNum; i++) {
+ outLen = MEMINFO_COUNT;
+ for (int i = 0; i < outLen; i++) {
+ // TODO: move get_allocated_vmalloc_memory() to libmeminfo
+ if (i == MEMINFO_VMALLOC_USED) {
+ outArray[i] = get_allocated_vmalloc_memory() / 1024;
+ continue;
+ }
outArray[i] = mem[i];
}
}
+
env->ReleaseLongArrayElements(out, outArray, 0);
}
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 102a0b7..0c1a8aa 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -25,8 +25,12 @@
#include <cutils/sched_policy.h>
#include <utils/String8.h>
#include <utils/Vector.h>
+#include <meminfo/sysmeminfo.h>
#include <processgroup/processgroup.h>
+#include <string>
+#include <vector>
+
#include "core_jni_helpers.h"
#include "android_util_Binder.h"
@@ -39,9 +43,11 @@
#include <inttypes.h>
#include <pwd.h>
#include <signal.h>
+#include <string.h>
#include <sys/errno.h>
#include <sys/resource.h>
#include <sys/stat.h>
+#include <sys/sysinfo.h>
#include <sys/types.h>
#include <unistd.h>
@@ -603,66 +609,34 @@
return *((const jint*)v1) - *((const jint*)v2);
}
-static jlong getFreeMemoryImpl(const char* const sums[], const size_t sumsLen[], size_t num)
-{
- int fd = open("/proc/meminfo", O_RDONLY | O_CLOEXEC);
-
- if (fd < 0) {
- ALOGW("Unable to open /proc/meminfo");
- return -1;
- }
-
- char buffer[2048];
- const int len = read(fd, buffer, sizeof(buffer)-1);
- close(fd);
-
- if (len < 0) {
- ALOGW("Unable to read /proc/meminfo");
- return -1;
- }
- buffer[len] = 0;
-
- size_t numFound = 0;
- jlong mem = 0;
-
- char* p = buffer;
- while (*p && numFound < num) {
- int i = 0;
- while (sums[i]) {
- if (strncmp(p, sums[i], sumsLen[i]) == 0) {
- p += sumsLen[i];
- while (*p == ' ') p++;
- char* num = p;
- while (*p >= '0' && *p <= '9') p++;
- if (*p != 0) {
- *p = 0;
- p++;
- if (*p == 0) p--;
- }
- mem += atoll(num) * 1024;
- numFound++;
- break;
- }
- i++;
- }
- p++;
- }
-
- return numFound > 0 ? mem : -1;
-}
-
static jlong android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
{
- static const char* const sums[] = { "MemFree:", "Cached:", NULL };
- static const size_t sumsLen[] = { strlen("MemFree:"), strlen("Cached:"), 0 };
- return getFreeMemoryImpl(sums, sumsLen, 2);
+ static const std::vector<std::string> memFreeTags = {
+ ::android::meminfo::SysMemInfo::kMemFree,
+ ::android::meminfo::SysMemInfo::kMemCached,
+ };
+ std::vector<uint64_t> mem(memFreeTags.size());
+ ::android::meminfo::SysMemInfo smi;
+
+ if (!smi.ReadMemInfo(memFreeTags, &mem)) {
+ jniThrowRuntimeException(env, "SysMemInfo read failed to get Free Memory");
+ return -1L;
+ }
+
+ jlong sum = 0;
+ std::for_each(mem.begin(), mem.end(), [&](uint64_t val) { sum += val; });
+ return sum * 1024;
}
static jlong android_os_Process_getTotalMemory(JNIEnv* env, jobject clazz)
{
- static const char* const sums[] = { "MemTotal:", NULL };
- static const size_t sumsLen[] = { strlen("MemTotal:"), 0 };
- return getFreeMemoryImpl(sums, sumsLen, 1);
+ struct sysinfo si;
+ if (sysinfo(&si) == -1) {
+ ALOGE("sysinfo failed: %s", strerror(errno));
+ return -1;
+ }
+
+ return si.totalram;
}
void android_os_Process_readProcLines(JNIEnv* env, jobject clazz, jstring fileStr,
diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
index e0fd1a6..40a133b 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -168,6 +168,11 @@
canvas->drawCircle(xProp, yProp, radiusProp, paintProp);
}
+static void android_view_DisplayListCanvas_drawWebViewFunctor(jlong canvasPtr, jint functor) {
+ Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+ canvas->drawWebViewFunctor(functor);
+}
+
// ----------------------------------------------------------------------------
// JNI Glue
// ----------------------------------------------------------------------------
@@ -192,6 +197,7 @@
{ "nDrawTextureLayer", "(JJ)V", (void*) android_view_DisplayListCanvas_drawTextureLayer },
{ "nDrawCircle", "(JJJJJ)V", (void*) android_view_DisplayListCanvas_drawCircleProps },
{ "nDrawRoundRect", "(JJJJJJJJ)V",(void*) android_view_DisplayListCanvas_drawRoundRectProps },
+ { "nDrawWebViewFunctor", "(JI)V", (void*) android_view_DisplayListCanvas_drawWebViewFunctor },
};
int register_android_view_DisplayListCanvas(JNIEnv* env) {
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 702741e..5a8ab3c 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -31,8 +31,6 @@
#include <gui/BufferQueue.h>
#include <gui/Surface.h>
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
#include <private/EGL/cache.h>
#include <utils/Looper.h>
@@ -58,6 +56,7 @@
#include <renderthread/RenderTask.h>
#include <renderthread/RenderThread.h>
#include <pipeline/skia/ShaderCache.h>
+#include <utils/Color.h>
namespace android {
@@ -1011,10 +1010,9 @@
buffer->getWidth(), buffer->getHeight(), width, height);
// Continue I guess?
}
- sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer);
- // Bitmap::createFrom currently can only attach to a GraphicBuffer with PIXEL_FORMAT_RGBA_8888
- // format and SRGB color space.
- // To support any color space, we could extract it from BufferItem and pass it to Bitmap.
+
+ sk_sp<SkColorSpace> cs = uirenderer::DataSpaceToColorSpace(bufferItem.mDataSpace);
+ sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, cs);
return bitmap::createBitmap(env, bitmap.release(),
android::bitmap::kBitmapCreateFlag_Premultiplied);
}
diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto
index ce19ce3..71ebcc1 100644
--- a/core/proto/android/service/procstats.proto
+++ b/core/proto/android/service/procstats.proto
@@ -186,7 +186,7 @@
repeated PackageServiceOperationStatsProto operation_stats = 2;
}
-// Next Tag: 7
+// Next Tag: 8
message PackageAssociationSourceProcessStatsProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -194,6 +194,8 @@
optional int32 process_uid = 1;
// Process name.
optional string process_name = 2;
+ // Package name.
+ optional string package_name = 7;
// Total count of the times this association appeared.
optional int32 total_count = 3;
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 5ba1cf2..0f53549 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -111,6 +111,7 @@
android:background="@null"
android:layout_width="@dimen/notification_header_expand_icon_size"
android:layout_height="@dimen/notification_header_expand_icon_size"
+ android:layout_marginStart="4dp"
android:paddingTop="@dimen/notification_expand_button_padding_top"
android:visibility="gone"
android:contentDescription="@string/expand_button_content_description_collapsed"
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 6f75d90..799d9d8 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2978,6 +2978,11 @@
<public name="config_mediaMetadataBitmapMaxSize" />
</public-group>
+ <public-group type="color" first-id="0x0106001c">
+ <!-- @hide @SystemApi -->
+ <public name="system_notification_accent_color" />
+ </public-group>
+
<!-- ===============================================================
DO NOT ADD UN-GROUPED ITEMS HERE
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
index 96ab977..5a86885 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
@@ -278,13 +278,12 @@
}
}
- final String no = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, " +
- "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " +
- "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip " +
- "ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit " +
- "esse cillum dolore eu fugiat nulla pariatur. " +
- "Excepteur sint occaecat cupidatat non proident, " +
- "sunt in culpa qui officia deserunt mollit anim id est laborum.";
+ final String no = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
+ + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
+ + "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo "
+ + "consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse "
+ + "cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non "
+ + "proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
final String so = "Lorem ipsum: single overlay.";
final String mo = "Lorem ipsum: multiple overlays.";
diff --git a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
index 6d5276f..27986cc 100644
--- a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
+++ b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
@@ -1,17 +1,17 @@
/*
* Copyright (C) 2018 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
+ * 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
+ * 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.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package com.android.server.om.hosttest;
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java b/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
index a174d77..86a8679 100644
--- a/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
@@ -1,17 +1,17 @@
/*
* Copyright (C) 2018 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
+ * 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
+ * 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.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package com.android.server.om.hosttest.update_overlay_test;
diff --git a/data/etc/Android.mk b/data/etc/Android.mk
index 61ef426..ff8c4f1 100644
--- a/data/etc/Android.mk
+++ b/data/etc/Android.mk
@@ -54,6 +54,7 @@
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_RELATIVE_PATH := permissions
LOCAL_MODULE_STEM := com.android.settings.xml
+LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := com.android.settings.xml
include $(BUILD_PREBUILT)
@@ -63,6 +64,7 @@
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_RELATIVE_PATH := permissions
LOCAL_MODULE_STEM := com.android.systemui.xml
+LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := com.android.systemui.xml
include $(BUILD_PREBUILT)
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index af570b3..c216425 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -297,6 +297,8 @@
<permission name="android.permission.POWER_SAVER" />
<permission name="android.permission.READ_FRAME_BUFFER"/>
<permission name="android.permission.READ_LOWPAN_CREDENTIAL"/>
+ <!-- Needed for test only -->
+ <permission name="android.permission.READ_PRECISE_PHONE_STATE" />
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
<permission name="android.permission.REAL_GET_TASKS"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java
index 67ad404..515532f 100644
--- a/graphics/java/android/graphics/RecordingCanvas.java
+++ b/graphics/java/android/graphics/RecordingCanvas.java
@@ -189,6 +189,14 @@
nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunctor, releasedCallback);
}
+ /**
+ * Calls the provided functor that was created via WebViewFunctor_create()
+ * @hide
+ */
+ public void drawWebViewFunctor(int functor) {
+ nDrawWebViewFunctor(mNativeCanvasWrapper, functor);
+ }
+
///////////////////////////////////////////////////////////////////////////
// Display list
///////////////////////////////////////////////////////////////////////////
@@ -303,4 +311,6 @@
@CriticalNative
private static native void nDrawRoundRect(long renderer, long propLeft, long propTop,
long propRight, long propBottom, long propRx, long propRy, long propPaint);
+ @CriticalNative
+ private static native void nDrawWebViewFunctor(long canvas, int functor);
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 6585bfc..7e69e3a 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -9,6 +9,8 @@
"hwui_lto",
],
+ cpp_std: "experimental",
+
cflags: [
"-DEGL_EGLEXT_PROTOTYPES",
"-DGL_GLEXT_PROTOTYPES",
@@ -226,6 +228,7 @@
"RenderProperties.cpp",
"SkiaCanvas.cpp",
"TreeInfo.cpp",
+ "WebViewFunctorManager.cpp",
"VectorDrawable.cpp",
"protos/graphicsstats.proto",
],
@@ -330,6 +333,7 @@
"tests/unit/TypefaceTests.cpp",
"tests/unit/VectorDrawableTests.cpp",
"tests/unit/VectorDrawableAtlasTests.cpp",
+ "tests/unit/WebViewFunctorManagerTests.cpp",
],
}
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index a97c12c..b9860ad 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -253,7 +253,8 @@
eglDestroySyncKHR(display, fence);
}
- return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info(), Bitmap::computePalette(bitmap)));
+ return Bitmap::createFrom(buffer.get(), bitmap.refColorSpace(), bitmap.alphaType(),
+ Bitmap::computePalette(bitmap));
}
} // namespace android::uirenderer
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 00ce28a..1ff7ffe 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -288,7 +288,10 @@
mDisplayList = mStagingDisplayList;
mStagingDisplayList = nullptr;
if (mDisplayList) {
- mDisplayList->syncContents();
+ WebViewSyncData syncData {
+ .applyForceDark = info && !info->disableForceDark
+ };
+ mDisplayList->syncContents(syncData);
handleForceDark(info);
}
}
diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp
new file mode 100644
index 0000000..20e77b4
--- /dev/null
+++ b/libs/hwui/WebViewFunctorManager.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2018 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 "WebViewFunctorManager.h"
+
+#include <private/hwui/WebViewFunctor.h>
+#include "Properties.h"
+
+#include <log/log.h>
+#include <utils/Trace.h>
+#include <atomic>
+
+namespace android::uirenderer {
+
+RenderMode WebViewFunctor_queryPlatformRenderMode() {
+ auto pipelineType = Properties::getRenderPipelineType();
+ switch (pipelineType) {
+ case RenderPipelineType::SkiaGL:
+ return RenderMode::OpenGL_ES;
+ case RenderPipelineType::SkiaVulkan:
+ return RenderMode::Vulkan;
+ default:
+ LOG_ALWAYS_FATAL("Unknown render pipeline type: %d", (int)pipelineType);
+ }
+}
+
+int WebViewFunctor_create(const WebViewFunctorCallbacks& prototype, RenderMode functorMode) {
+ if (functorMode != RenderMode::OpenGL_ES && functorMode != RenderMode::Vulkan) {
+ ALOGW("Unknown rendermode %d", (int)functorMode);
+ return -1;
+ }
+ if (functorMode == RenderMode::Vulkan &&
+ WebViewFunctor_queryPlatformRenderMode() != RenderMode::Vulkan) {
+ ALOGW("Unable to map from GLES platform to a vulkan functor");
+ return -1;
+ }
+ return WebViewFunctorManager::instance().createFunctor(prototype, functorMode);
+}
+
+void WebViewFunctor_release(int functor) {
+ WebViewFunctorManager::instance().releaseFunctor(functor);
+}
+
+static std::atomic_int sNextId{1};
+
+WebViewFunctor::WebViewFunctor(const WebViewFunctorCallbacks& callbacks, RenderMode functorMode) {
+ mFunctor = sNextId++;
+ mCallbacks = callbacks;
+ mMode = functorMode;
+}
+
+WebViewFunctor::~WebViewFunctor() {
+ destroyContext();
+
+ ATRACE_NAME("WebViewFunctor::onDestroy");
+ mCallbacks.onDestroyed(mFunctor);
+}
+
+void WebViewFunctor::sync(const WebViewSyncData& syncData) const {
+ ATRACE_NAME("WebViewFunctor::sync");
+ mCallbacks.onSync(mFunctor, syncData);
+}
+
+void WebViewFunctor::drawGl(const DrawGlInfo& drawInfo) {
+ ATRACE_NAME("WebViewFunctor::drawGl");
+ if (!mHasContext) {
+ mHasContext = true;
+ }
+ mCallbacks.gles.draw(mFunctor, drawInfo);
+}
+
+void WebViewFunctor::destroyContext() {
+ if (mHasContext) {
+ mHasContext = false;
+ ATRACE_NAME("WebViewFunctor::onContextDestroyed");
+ mCallbacks.onContextDestroyed(mFunctor);
+ }
+}
+
+WebViewFunctorManager& WebViewFunctorManager::instance() {
+ static WebViewFunctorManager sInstance;
+ return sInstance;
+}
+
+int WebViewFunctorManager::createFunctor(const WebViewFunctorCallbacks& callbacks,
+ RenderMode functorMode) {
+ auto object = std::make_unique<WebViewFunctor>(callbacks, functorMode);
+ int id = object->id();
+ auto handle = object->createHandle();
+ {
+ std::lock_guard _lock{mLock};
+ mActiveFunctors.push_back(std::move(handle));
+ mFunctors.push_back(std::move(object));
+ }
+ return id;
+}
+
+void WebViewFunctorManager::releaseFunctor(int functor) {
+ sp<WebViewFunctor::Handle> toRelease;
+ {
+ std::lock_guard _lock{mLock};
+ for (auto iter = mActiveFunctors.begin(); iter != mActiveFunctors.end(); iter++) {
+ if ((*iter)->id() == functor) {
+ toRelease = std::move(*iter);
+ mActiveFunctors.erase(iter);
+ break;
+ }
+ }
+ }
+}
+
+void WebViewFunctorManager::onContextDestroyed() {
+ // WARNING: SKETCHY
+ // Because we know that we always remove from mFunctors on RenderThread, the same
+ // thread that always invokes onContextDestroyed, we know that the functor pointers
+ // will remain valid without the lock held.
+ // However, we won't block new functors from being added in the meantime.
+ mLock.lock();
+ const size_t size = mFunctors.size();
+ WebViewFunctor* toDestroyContext[size];
+ for (size_t i = 0; i < size; i++) {
+ toDestroyContext[i] = mFunctors[i].get();
+ }
+ mLock.unlock();
+ for (size_t i = 0; i < size; i++) {
+ toDestroyContext[i]->destroyContext();
+ }
+}
+
+void WebViewFunctorManager::destroyFunctor(int functor) {
+ std::unique_ptr<WebViewFunctor> toRelease;
+ {
+ std::lock_guard _lock{mLock};
+ for (auto iter = mFunctors.begin(); iter != mFunctors.end(); iter++) {
+ if ((*iter)->id() == functor) {
+ toRelease = std::move(*iter);
+ mFunctors.erase(iter);
+ break;
+ }
+ }
+ }
+}
+
+sp<WebViewFunctor::Handle> WebViewFunctorManager::handleFor(int functor) {
+ std::lock_guard _lock{mLock};
+ for (auto& iter : mActiveFunctors) {
+ if (iter->id() == functor) {
+ return iter;
+ }
+ }
+ return nullptr;
+}
+
+} // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h
new file mode 100644
index 0000000..2a621dd
--- /dev/null
+++ b/libs/hwui/WebViewFunctorManager.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 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 <private/hwui/WebViewFunctor.h>
+#include <renderthread/RenderProxy.h>
+
+#include <utils/LightRefBase.h>
+#include <mutex>
+#include <vector>
+
+namespace android::uirenderer {
+
+class WebViewFunctorManager;
+
+class WebViewFunctor {
+public:
+ WebViewFunctor(const WebViewFunctorCallbacks& callbacks, RenderMode functorMode);
+ ~WebViewFunctor();
+
+ class Handle : public LightRefBase<Handle> {
+ public:
+ ~Handle() { renderthread::RenderProxy::destroyFunctor(id()); }
+
+ int id() const { return mReference.id(); }
+
+ void sync(const WebViewSyncData& syncData) const { mReference.sync(syncData); }
+
+ void drawGl(const DrawGlInfo& drawInfo) const { mReference.drawGl(drawInfo); }
+
+ private:
+ friend class WebViewFunctor;
+
+ Handle(WebViewFunctor& ref) : mReference(ref) {}
+
+ WebViewFunctor& mReference;
+ };
+
+ int id() const { return mFunctor; }
+ void sync(const WebViewSyncData& syncData) const;
+ void drawGl(const DrawGlInfo& drawInfo);
+ void destroyContext();
+
+ sp<Handle> createHandle() {
+ LOG_ALWAYS_FATAL_IF(mCreatedHandle);
+ mCreatedHandle = true;
+ return sp<Handle>{new Handle(*this)};
+ }
+
+private:
+ WebViewFunctorCallbacks mCallbacks;
+ int mFunctor;
+ RenderMode mMode;
+ bool mHasContext = false;
+ bool mCreatedHandle = false;
+};
+
+class WebViewFunctorManager {
+public:
+ static WebViewFunctorManager& instance();
+
+ int createFunctor(const WebViewFunctorCallbacks& callbacks, RenderMode functorMode);
+ void releaseFunctor(int functor);
+ void onContextDestroyed();
+ void destroyFunctor(int functor);
+
+ sp<WebViewFunctor::Handle> handleFor(int functor);
+
+private:
+ WebViewFunctorManager() = default;
+ ~WebViewFunctorManager() = default;
+
+ std::mutex mLock;
+ std::vector<std::unique_ptr<WebViewFunctor>> mFunctors;
+ std::vector<sp<WebViewFunctor::Handle>> mActiveFunctors;
+};
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 6c77f9e..6e0258c 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -75,31 +75,6 @@
return allocateBitmap(bitmap, &Bitmap::allocateAshmemBitmap);
}
-static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) {
- void* addr = calloc(size, 1);
- if (!addr) {
- return nullptr;
- }
- return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes));
-}
-
-sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(SkBitmap& bitmap) {
- return uirenderer::HardwareBitmapUploader::allocateHardwareBitmap(bitmap);
-}
-
-sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap) {
- return allocateBitmap(bitmap, &android::allocateHeapBitmap);
-}
-
-sk_sp<Bitmap> Bitmap::allocateHeapBitmap(const SkImageInfo& info) {
- size_t size;
- if (!computeAllocationSize(info.minRowBytes(), info.height(), &size)) {
- LOG_ALWAYS_FATAL("trying to allocate too large bitmap");
- return nullptr;
- }
- return android::allocateHeapBitmap(size, info, info.minRowBytes());
-}
-
sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) {
// Create new ashmem region with read/write priv
int fd = ashmem_create_region("bitmap", size);
@@ -121,6 +96,31 @@
return sk_sp<Bitmap>(new Bitmap(addr, fd, size, info, rowBytes));
}
+sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(const SkBitmap& bitmap) {
+ return uirenderer::HardwareBitmapUploader::allocateHardwareBitmap(bitmap);
+}
+
+sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap) {
+ return allocateBitmap(bitmap, &Bitmap::allocateHeapBitmap);
+}
+
+sk_sp<Bitmap> Bitmap::allocateHeapBitmap(const SkImageInfo& info) {
+ size_t size;
+ if (!computeAllocationSize(info.minRowBytes(), info.height(), &size)) {
+ LOG_ALWAYS_FATAL("trying to allocate too large bitmap");
+ return nullptr;
+ }
+ return allocateHeapBitmap(size, info, info.minRowBytes());
+}
+
+sk_sp<Bitmap> Bitmap::allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes) {
+ void* addr = calloc(size, 1);
+ if (!addr) {
+ return nullptr;
+ }
+ return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes));
+}
+
void FreePixelRef(void* addr, void* context) {
auto pixelRef = (SkPixelRef*)context;
pixelRef->unref();
@@ -132,17 +132,38 @@
pixelRef.rowBytes()));
}
-sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer) {
- return createFrom(graphicBuffer, SkColorSpace::MakeSRGB());
+
+sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer, sk_sp<SkColorSpace> colorSpace,
+ SkAlphaType alphaType, BitmapPalette palette) {
+ // As we will be effectively texture-sampling the buffer (using either EGL or Vulkan), we can
+ // view the format as RGBA8888.
+ SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(),
+ kRGBA_8888_SkColorType, alphaType, colorSpace);
+ return sk_sp<Bitmap>(new Bitmap(graphicBuffer.get(), info, palette));
}
-sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer, sk_sp<SkColorSpace> colorSpace) {
- // As we will be effectively texture-sampling the buffer (using either EGL or Vulkan), we can
- // view the colorspace as RGBA8888.
- SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(),
- kRGBA_8888_SkColorType, kPremul_SkAlphaType,
- colorSpace);
- return sk_sp<Bitmap>(new Bitmap(graphicBuffer.get(), info));
+sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, size_t rowBytes, int fd, void* addr,
+ size_t size, bool readOnly) {
+ if (info.colorType() == kUnknown_SkColorType) {
+ LOG_ALWAYS_FATAL("unknown bitmap configuration");
+ return nullptr;
+ }
+
+ if (!addr) {
+ // Map existing ashmem region if not already mapped.
+ int flags = readOnly ? (PROT_READ) : (PROT_READ | PROT_WRITE);
+ size = ashmem_get_size_region(fd);
+ addr = mmap(NULL, size, flags, MAP_SHARED, fd, 0);
+ if (addr == MAP_FAILED) {
+ return nullptr;
+ }
+ }
+
+ sk_sp<Bitmap> bitmap(new Bitmap(addr, fd, size, info, rowBytes));
+ if (readOnly) {
+ bitmap->setImmutable();
+ }
+ return bitmap;
}
void Bitmap::setColorSpace(sk_sp<SkColorSpace> colorSpace) {
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index d446377..2138040 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -54,28 +54,31 @@
class ANDROID_API Bitmap : public SkPixelRef {
public:
+ /* The allocate factories not only construct the Bitmap object but also allocate the
+ * backing store whose type is determined by the specific method that is called.
+ *
+ * The factories that accept SkBitmap* as a param will modify those params by
+ * installing the returned bitmap as their SkPixelRef.
+ *
+ * The factories that accept const SkBitmap& as a param will copy the contents of the
+ * provided bitmap into the newly allocated buffer.
+ */
+ static sk_sp<Bitmap> allocateAshmemBitmap(SkBitmap* bitmap);
+ static sk_sp<Bitmap> allocateHardwareBitmap(const SkBitmap& bitmap);
static sk_sp<Bitmap> allocateHeapBitmap(SkBitmap* bitmap);
static sk_sp<Bitmap> allocateHeapBitmap(const SkImageInfo& info);
- static sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& bitmap);
-
- static sk_sp<Bitmap> allocateAshmemBitmap(SkBitmap* bitmap);
- static sk_sp<Bitmap> allocateAshmemBitmap(size_t allocSize, const SkImageInfo& info,
- size_t rowBytes);
-
- static sk_sp<Bitmap> createFrom(sp<GraphicBuffer> graphicBuffer);
+ /* The createFrom factories construct a new Bitmap object by wrapping the already allocated
+ * memory that is provided as an input param.
+ */
static sk_sp<Bitmap> createFrom(sp<GraphicBuffer> graphicBuffer,
- sk_sp<SkColorSpace> colorSpace);
-
+ sk_sp<SkColorSpace> colorSpace,
+ SkAlphaType alphaType = kPremul_SkAlphaType,
+ BitmapPalette palette = BitmapPalette::Unknown);
+ static sk_sp<Bitmap> createFrom(const SkImageInfo& info, size_t rowBytes, int fd, void* addr,
+ size_t size, bool readOnly);
static sk_sp<Bitmap> createFrom(const SkImageInfo&, SkPixelRef&);
- Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes);
- Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info,
- size_t rowBytes);
- Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes);
- Bitmap(GraphicBuffer* buffer, const SkImageInfo& info,
- BitmapPalette palette = BitmapPalette::Unknown);
-
int rowBytesAsPixels() const { return rowBytes() >> mInfo.shiftPerPixel(); }
void reconfigure(const SkImageInfo& info, size_t rowBytes);
@@ -123,6 +126,15 @@
}
private:
+ static sk_sp<Bitmap> allocateAshmemBitmap(size_t size, const SkImageInfo& i, size_t rowBytes);
+ static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& i, size_t rowBytes);
+
+ Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes);
+ Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info,
+ size_t rowBytes);
+ Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes);
+ Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette);
+
virtual ~Bitmap();
void* getStorage() const;
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index a5f21d8..71814c3 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -178,6 +178,9 @@
virtual void drawRenderNode(uirenderer::RenderNode* renderNode) = 0;
virtual void callDrawGLFunction(Functor* functor,
uirenderer::GlFunctorLifecycleListener* listener) = 0;
+ virtual void drawWebViewFunctor(int /*functor*/) {
+ LOG_ALWAYS_FATAL("Not supported");
+ }
// ----------------------------------------------------------------------------
// Canvas state operations
diff --git a/libs/hwui/pipeline/skia/FunctorDrawable.h b/libs/hwui/pipeline/skia/FunctorDrawable.h
index af3a056..cf2f93b 100644
--- a/libs/hwui/pipeline/skia/FunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/FunctorDrawable.h
@@ -21,7 +21,9 @@
#include <SkCanvas.h>
#include <SkDrawable.h>
+#include <WebViewFunctorManager.h>
#include <utils/Functor.h>
+#include <variant>
namespace android {
namespace uirenderer {
@@ -35,17 +37,43 @@
class FunctorDrawable : public SkDrawable {
public:
FunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
- : mFunctor(functor), mListener(listener), mBounds(canvas->getLocalClipBounds()) {}
+ : mBounds(canvas->getLocalClipBounds())
+ , mAnyFunctor(std::in_place_type<LegacyFunctor>, functor, listener) {}
+
+ FunctorDrawable(int functor, SkCanvas* canvas)
+ : mBounds(canvas->getLocalClipBounds())
+ , mAnyFunctor(std::in_place_type<NewFunctor>, functor) {}
+
virtual ~FunctorDrawable() {}
- virtual void syncFunctor() const = 0;
+ virtual void syncFunctor(const WebViewSyncData& data) const {
+ if (mAnyFunctor.index() == 0) {
+ std::get<0>(mAnyFunctor).handle->sync(data);
+ } else {
+ (*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeSync, nullptr);
+ }
+ }
protected:
virtual SkRect onGetBounds() override { return mBounds; }
- Functor* mFunctor;
- sp<GlFunctorLifecycleListener> mListener;
const SkRect mBounds;
+
+ struct LegacyFunctor {
+ explicit LegacyFunctor(Functor* functor, GlFunctorLifecycleListener* listener)
+ : functor(functor), listener(listener) {}
+ Functor* functor;
+ sp<GlFunctorLifecycleListener> listener;
+ };
+
+ struct NewFunctor {
+ explicit NewFunctor(int functor) {
+ handle = WebViewFunctorManager::instance().handleFor(functor);
+ }
+ sp<WebViewFunctor::Handle> handle;
+ };
+
+ std::variant<NewFunctor, LegacyFunctor> mAnyFunctor;
};
} // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index 4a87e75..240efb4 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -17,29 +17,28 @@
#include "GLFunctorDrawable.h"
#include <GrContext.h>
#include <private/hwui/DrawGlInfo.h>
+#include "FunctorDrawable.h"
#include "GlFunctorLifecycleListener.h"
+#include "GrBackendSurface.h"
+#include "GrRenderTarget.h"
+#include "GrRenderTargetContext.h"
#include "RenderNode.h"
#include "SkAndroidFrameworkUtils.h"
#include "SkClipStack.h"
#include "SkRect.h"
-#include "GrBackendSurface.h"
-#include "GrRenderTarget.h"
-#include "GrRenderTargetContext.h"
namespace android {
namespace uirenderer {
namespace skiapipeline {
GLFunctorDrawable::~GLFunctorDrawable() {
- if (mListener.get() != nullptr) {
- mListener->onGlFunctorReleased(mFunctor);
+ if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
+ if (lp->listener) {
+ lp->listener->onGlFunctorReleased(lp->functor);
+ }
}
}
-void GLFunctorDrawable::syncFunctor() const {
- (*mFunctor)(DrawGlInfo::kModeSync, nullptr);
-}
-
static void setScissor(int viewportHeight, const SkIRect& clip) {
SkASSERT(!clip.isEmpty());
// transform to Y-flipped GL space, and prevent negatives
@@ -49,14 +48,14 @@
}
static bool GetFboDetails(SkCanvas* canvas, GLuint* outFboID, SkISize* outFboSize) {
- GrRenderTargetContext *renderTargetContext =
+ GrRenderTargetContext* renderTargetContext =
canvas->internal_private_accessTopLayerRenderTargetContext();
if (!renderTargetContext) {
ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw");
return false;
}
- GrRenderTarget *renderTarget = renderTargetContext->accessRenderTarget();
+ GrRenderTarget* renderTarget = renderTargetContext->accessRenderTarget();
if (!renderTarget) {
ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw");
return false;
@@ -94,16 +93,16 @@
sk_sp<SkSurface> tmpSurface;
// we are in a state where there is an unclipped saveLayer
if (fboID != 0 && !surfaceBounds.contains(clipBounds)) {
-
// create an offscreen layer and clear it
- SkImageInfo surfaceInfo = canvas->imageInfo().makeWH(clipBounds.width(), clipBounds.height());
- tmpSurface = SkSurface::MakeRenderTarget(canvas->getGrContext(), SkBudgeted::kYes,
- surfaceInfo);
+ SkImageInfo surfaceInfo =
+ canvas->imageInfo().makeWH(clipBounds.width(), clipBounds.height());
+ tmpSurface =
+ SkSurface::MakeRenderTarget(canvas->getGrContext(), SkBudgeted::kYes, surfaceInfo);
tmpSurface->getCanvas()->clear(SK_ColorTRANSPARENT);
GrGLFramebufferInfo fboInfo;
if (!tmpSurface->getBackendRenderTarget(SkSurface::kFlushWrite_BackendHandleAccess)
- .getGLFramebufferInfo(&fboInfo)) {
+ .getGLFramebufferInfo(&fboInfo)) {
ALOGW("Unable to extract renderTarget info from offscreen canvas; aborting GLFunctor");
return;
}
@@ -144,7 +143,7 @@
bool clearStencilAfterFunctor = false;
if (CC_UNLIKELY(clipRegion.isComplex())) {
// clear the stencil
- //TODO: move stencil clear and canvas flush to SkAndroidFrameworkUtils::clipWithStencil
+ // TODO: move stencil clear and canvas flush to SkAndroidFrameworkUtils::clipWithStencil
glDisable(GL_SCISSOR_TEST);
glStencilMask(0x1);
glClearStencil(0);
@@ -163,7 +162,7 @@
// GL ops get inserted here if previous flush is missing, which could dirty the stencil
bool stencilWritten = SkAndroidFrameworkUtils::clipWithStencil(tmpCanvas);
- tmpCanvas->flush(); //need this flush for the single op that draws into the stencil
+ tmpCanvas->flush(); // need this flush for the single op that draws into the stencil
// ensure that the framebuffer that the webview will render into is bound before after we
// draw into the stencil
@@ -188,7 +187,11 @@
setScissor(info.height, clipRegion.getBounds());
}
- (*mFunctor)(DrawGlInfo::kModeDraw, &info);
+ if (mAnyFunctor.index() == 0) {
+ std::get<0>(mAnyFunctor).handle->drawGl(info);
+ } else {
+ (*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeDraw, &info);
+ }
if (clearStencilAfterFunctor) {
// clear stencil buffer as it may be used by Skia
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
index 215979c..2ea4f67 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
@@ -31,11 +31,9 @@
*/
class GLFunctorDrawable : public FunctorDrawable {
public:
- GLFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
- : FunctorDrawable(functor, listener, canvas) {}
- virtual ~GLFunctorDrawable();
+ using FunctorDrawable::FunctorDrawable;
- void syncFunctor() const override;
+ virtual ~GLFunctorDrawable();
protected:
void onDraw(SkCanvas* canvas) override;
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index f08ac17..eed1942 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include <utils/MathUtils.h>
#include "LayerDrawable.h"
+#include <utils/MathUtils.h>
#include "GrBackendSurface.h"
#include "SkColorFilter.h"
@@ -44,10 +44,9 @@
if (!matrix.isScaleTranslate()) return true;
// We only care about meaningful scale here
- bool noScale = MathUtils::isOne(matrix.getScaleX())
- && MathUtils::isOne(matrix.getScaleY());
- bool pixelAligned = SkScalarIsInt(matrix.getTranslateX())
- && SkScalarIsInt(matrix.getTranslateY());
+ bool noScale = MathUtils::isOne(matrix.getScaleX()) && MathUtils::isOne(matrix.getScaleY());
+ bool pixelAligned =
+ SkScalarIsInt(matrix.getTranslateX()) && SkScalarIsInt(matrix.getTranslateY());
return !(noScale && pixelAligned);
}
@@ -120,11 +119,12 @@
// Integer translation is defined as when src rect and dst rect align fractionally.
// Skia TextureOp has the above logic build-in, but not NonAAFillRectOp. TextureOp works
// only for SrcOver blending and without color filter (readback uses Src blending).
- bool isIntegerTranslate = isBasicallyTranslate(totalMatrix)
- && SkScalarFraction(skiaDestRect.fLeft + totalMatrix[SkMatrix::kMTransX])
- == SkScalarFraction(skiaSrcRect.fLeft)
- && SkScalarFraction(skiaDestRect.fTop + totalMatrix[SkMatrix::kMTransY])
- == SkScalarFraction(skiaSrcRect.fTop);
+ bool isIntegerTranslate =
+ isBasicallyTranslate(totalMatrix) &&
+ SkScalarFraction(skiaDestRect.fLeft + totalMatrix[SkMatrix::kMTransX]) ==
+ SkScalarFraction(skiaSrcRect.fLeft) &&
+ SkScalarFraction(skiaDestRect.fTop + totalMatrix[SkMatrix::kMTransY]) ==
+ SkScalarFraction(skiaSrcRect.fTop);
if (layer->getForceFilter() || !isIntegerTranslate) {
paint.setFilterQuality(kLow_SkFilterQuality);
}
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h
index 95dc6d0..7cd515a 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.h
+++ b/libs/hwui/pipeline/skia/LayerDrawable.h
@@ -32,8 +32,8 @@
public:
explicit LayerDrawable(DeferredLayerUpdater* layerUpdater) : mLayerUpdater(layerUpdater) {}
- static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
- const SkRect* srcRect, const SkRect* dstRect, bool useLayerTransform);
+ static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer, const SkRect* srcRect,
+ const SkRect* dstRect, bool useLayerTransform);
protected:
virtual SkRect onGetBounds() override {
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 4494cb0..df1537e 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -127,6 +127,7 @@
mNode.markDrawEnd(mCanvas);
}
}
+
private:
SkCanvas& mCanvas;
RenderNode& mNode;
@@ -140,7 +141,7 @@
// ensures that we paint the layer even if it is not currently visible in the
// event that the properties change and it becomes visible.
if ((mProjectedDisplayList == nullptr && !renderNode->isRenderable()) ||
- (renderNode->nothingToDraw() && mComposeLayer)) {
+ (renderNode->nothingToDraw() && mComposeLayer)) {
return;
}
@@ -234,8 +235,8 @@
// we need to restrict the portion of the surface drawn to the size of the renderNode.
SkASSERT(renderNode->getLayerSurface()->width() >= bounds.width());
SkASSERT(renderNode->getLayerSurface()->height() >= bounds.height());
- canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(),
- bounds, bounds, &paint);
+ canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(), bounds,
+ bounds, &paint);
if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true;
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index 073b481..562a3b2 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -15,11 +15,11 @@
*/
#include "ShaderCache.h"
-#include <algorithm>
#include <log/log.h>
-#include <thread>
-#include <array>
#include <openssl/sha.h>
+#include <algorithm>
+#include <array>
+#include <thread>
#include "FileBlobCache.h"
#include "Properties.h"
#include "utils/TraceUtils.h"
@@ -44,8 +44,7 @@
}
bool ShaderCache::validateCache(const void* identity, ssize_t size) {
- if (nullptr == identity && size == 0)
- return true;
+ if (nullptr == identity && size == 0) return true;
if (nullptr == identity || size < 0) {
if (CC_UNLIKELY(Properties::debugLevel & kDebugCaches)) {
@@ -66,8 +65,7 @@
auto key = sIDKey;
auto loaded = mBlobCache->get(&key, sizeof(key), hash.data(), hash.size());
- if (loaded && std::equal(hash.begin(), hash.end(), mIDHash.begin()))
- return true;
+ if (loaded && std::equal(hash.begin(), hash.end(), mIDHash.begin())) return true;
if (CC_UNLIKELY(Properties::debugLevel & kDebugCaches)) {
ALOGW("ShaderCache::validateCache cache validation fails");
@@ -119,7 +117,7 @@
int maxTries = 3;
while (valueSize > mObservedBlobValueSize && maxTries > 0) {
mObservedBlobValueSize = std::min(valueSize, maxValueSize);
- void *newValueBuffer = realloc(valueBuffer, mObservedBlobValueSize);
+ void* newValueBuffer = realloc(valueBuffer, mObservedBlobValueSize);
if (!newValueBuffer) {
free(valueBuffer);
return nullptr;
@@ -133,7 +131,7 @@
return nullptr;
}
if (valueSize > mObservedBlobValueSize) {
- ALOGE("ShaderCache::load value size is too big %d", (int) valueSize);
+ ALOGE("ShaderCache::load value size is too big %d", (int)valueSize);
free(valueBuffer);
return nullptr;
}
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
index 82804cf..d41aadb 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.h
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -16,12 +16,12 @@
#pragma once
+#include <GrContextOptions.h>
#include <cutils/compiler.h>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
-#include <GrContextOptions.h>
namespace android {
@@ -52,7 +52,7 @@
* the initialized state the load and store methods will return without
* performing any cache operations.
*/
- virtual void initShaderDiskCache(const void *identity, ssize_t size);
+ virtual void initShaderDiskCache(const void* identity, ssize_t size);
virtual void initShaderDiskCache() { initShaderDiskCache(nullptr, 0); }
@@ -153,7 +153,7 @@
/**
* "mObservedBlobValueSize" is the maximum value size observed by the cache reading function.
*/
- size_t mObservedBlobValueSize = 20*1024;
+ size_t mObservedBlobValueSize = 20 * 1024;
/**
* The time in seconds to wait before saving newly inserted cache entries.
@@ -176,7 +176,7 @@
*/
static constexpr uint8_t sIDKey = 0;
- friend class ShaderCacheTestUtils; //used for unit testing
+ friend class ShaderCacheTestUtils; // used for unit testing
};
} /* namespace skiapipeline */
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index ac6f6a3..230065c 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -27,9 +27,9 @@
namespace uirenderer {
namespace skiapipeline {
-void SkiaDisplayList::syncContents() {
+void SkiaDisplayList::syncContents(const WebViewSyncData& data) {
for (auto& functor : mChildFunctors) {
- functor->syncFunctor();
+ functor->syncFunctor(data);
}
for (auto& animatedImage : mAnimatedImages) {
animatedImage->syncProperties();
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 45f3a4c..3219ad1 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -16,11 +16,11 @@
#pragma once
-#include "hwui/AnimatedImageDrawable.h"
#include "FunctorDrawable.h"
#include "RecordingCanvas.h"
#include "RenderNodeDrawable.h"
#include "TreeInfo.h"
+#include "hwui/AnimatedImageDrawable.h"
#include "utils/LinearAllocator.h"
#include <deque>
@@ -109,7 +109,7 @@
* NOTE: This function can be folded into RenderNode when we no longer need
* to subclass from DisplayList
*/
- void syncContents();
+ void syncContents(const WebViewSyncData& data);
/**
* ONLY to be called by RenderNode::prepareTree in order to prepare this
diff --git a/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp b/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp
index ea578cb..e48ecf4 100644
--- a/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp
+++ b/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp
@@ -21,16 +21,16 @@
namespace skiapipeline {
SkiaMemoryTracer::SkiaMemoryTracer(std::vector<ResourcePair> resourceMap, bool itemizeType)
- : mResourceMap(resourceMap)
- , mItemizeType(itemizeType)
- , mTotalSize("bytes", 0)
- , mPurgeableSize("bytes", 0) {}
+ : mResourceMap(resourceMap)
+ , mItemizeType(itemizeType)
+ , mTotalSize("bytes", 0)
+ , mPurgeableSize("bytes", 0) {}
SkiaMemoryTracer::SkiaMemoryTracer(const char* categoryKey, bool itemizeType)
- : mCategoryKey(categoryKey)
- , mItemizeType(itemizeType)
- , mTotalSize("bytes", 0)
- , mPurgeableSize("bytes", 0) {}
+ : mCategoryKey(categoryKey)
+ , mItemizeType(itemizeType)
+ , mTotalSize("bytes", 0)
+ , mPurgeableSize("bytes", 0) {}
const char* SkiaMemoryTracer::mapName(const char* resourceName) {
for (auto& resource : mResourceMap) {
@@ -42,7 +42,7 @@
}
void SkiaMemoryTracer::processElement() {
- if(!mCurrentElement.empty()) {
+ if (!mCurrentElement.empty()) {
// Only count elements that contain "size", other values just provide metadata.
auto sizeResult = mCurrentValues.find("size");
if (sizeResult != mCurrentValues.end()) {
@@ -136,8 +136,8 @@
for (const auto& typedValue : namedItem.second) {
TraceValue traceValue = convertUnits(typedValue.second);
const char* entry = (traceValue.count > 1) ? "entries" : "entry";
- log.appendFormat(" %s: %.2f %s (%d %s)\n", typedValue.first,
- traceValue.value, traceValue.units, traceValue.count, entry);
+ log.appendFormat(" %s: %.2f %s (%d %s)\n", typedValue.first, traceValue.value,
+ traceValue.units, traceValue.count, entry);
}
} else {
auto result = namedItem.second.find("size");
diff --git a/libs/hwui/pipeline/skia/SkiaMemoryTracer.h b/libs/hwui/pipeline/skia/SkiaMemoryTracer.h
index abf1f4b..e9a7981 100644
--- a/libs/hwui/pipeline/skia/SkiaMemoryTracer.h
+++ b/libs/hwui/pipeline/skia/SkiaMemoryTracer.h
@@ -50,8 +50,8 @@
}
bool shouldDumpWrappedObjects() const override { return true; }
- void setMemoryBacking(const char*, const char*, const char*) override { }
- void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override { }
+ void setMemoryBacking(const char*, const char*, const char*) override {}
+ void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override {}
private:
struct TraceValue {
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 7f62ab5..2e7850d 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -184,15 +184,15 @@
} else {
String8 cachesOutput;
mRenderThread.cacheManager().dumpMemoryUsage(cachesOutput,
- &mRenderThread.renderState());
+ &mRenderThread.renderState());
ALOGE("%s", cachesOutput.string());
if (errorHandler) {
std::ostringstream err;
err << "Unable to create layer for " << node->getName();
const int maxTextureSize = DeviceInfo::get()->maxTextureSize();
err << ", size " << info.width() << "x" << info.height() << " max size "
- << maxTextureSize << " color type " << (int)info.colorType()
- << " has context " << (int)(mRenderThread.getGrContext() != nullptr);
+ << maxTextureSize << " color type " << (int)info.colorType() << " has context "
+ << (int)(mRenderThread.getGrContext() != nullptr);
errorHandler->onError(err.str());
}
}
@@ -301,8 +301,7 @@
mSavePictureProcessor->savePicture(data, mCapturedFile);
} else {
mSavePictureProcessor->savePicture(
- data,
- mCapturedFile + "_" + std::to_string(mCaptureSequence));
+ data, mCapturedFile + "_" + std::to_string(mCaptureSequence));
}
mCaptureSequence--;
}
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index af58f63..f2906de 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -97,8 +97,7 @@
return mLightCenter;
}
- static void updateLighting(const LightGeometry& lightGeometry,
- const LightInfo& lightInfo) {
+ static void updateLighting(const LightGeometry& lightGeometry, const LightInfo& lightInfo) {
mLightRadius = lightGeometry.radius;
mAmbientShadowAlpha = lightInfo.ambientShadowAlpha;
mSpotShadowAlpha = lightInfo.spotShadowAlpha;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index b56c3ef..6eefed9 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -24,8 +24,8 @@
#include "RenderNode.h"
#include "pipeline/skia/AnimatedDrawables.h"
#include "pipeline/skia/GLFunctorDrawable.h"
-#include "pipeline/skia/VkInteropFunctorDrawable.h"
#include "pipeline/skia/VkFunctorDrawable.h"
+#include "pipeline/skia/VkInteropFunctorDrawable.h"
namespace android {
namespace uirenderer {
@@ -95,8 +95,8 @@
drawDrawable(drawable);
}
if (enableReorder) {
- mCurrentBarrier = mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(
- mDisplayList.get());
+ mCurrentBarrier =
+ mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(mDisplayList.get());
drawDrawable(mCurrentBarrier);
}
}
@@ -127,11 +127,25 @@
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
// TODO(cblume) use VkFunctorDrawable instead of VkInteropFunctorDrawable here when the
// interop is disabled/moved.
- functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(functor,
- listener, asSkCanvas());
+ functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(
+ functor, listener, asSkCanvas());
} else {
- functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, listener,
- asSkCanvas());
+ functorDrawable =
+ mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, listener, asSkCanvas());
+ }
+ mDisplayList->mChildFunctors.push_back(functorDrawable);
+ drawDrawable(functorDrawable);
+}
+
+void SkiaRecordingCanvas::drawWebViewFunctor(int functor) {
+ FunctorDrawable* functorDrawable;
+ if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+ // TODO(cblume) use VkFunctorDrawable instead of VkInteropFunctorDrawable here when the
+ // interop is disabled.
+ functorDrawable =
+ mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(functor, asSkCanvas());
+ } else {
+ functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, asSkCanvas());
}
mDisplayList->mChildFunctors.push_back(functorDrawable);
drawDrawable(functorDrawable);
@@ -167,7 +181,7 @@
if (colorSpaceFilter) {
if (tmpPaint.getColorFilter()) {
tmpPaint.setColorFilter(SkColorFilter::MakeComposeFilter(
- tmpPaint.refColorFilter(), std::move(colorSpaceFilter)));
+ tmpPaint.refColorFilter(), std::move(colorSpaceFilter)));
} else {
tmpPaint.setColorFilter(std::move(colorSpaceFilter));
}
@@ -248,8 +262,7 @@
filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality);
}
sk_sp<SkImage> image = bitmap.makeImage();
- mRecorder.drawImageLattice(image, lattice, dst,
- filterPaint(std::move(filteredPaint)),
+ mRecorder.drawImageLattice(image, lattice, dst, filterPaint(std::move(filteredPaint)),
bitmap.palette());
if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
mDisplayList->mMutableImages.push_back(image.get());
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index d6107a9..afeccea 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -74,6 +74,7 @@
virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override;
virtual void callDrawGLFunction(Functor* functor,
uirenderer::GlFunctorLifecycleListener* listener) override;
+ void drawWebViewFunctor(int functor) override;
private:
RecordingCanvas mRecorder;
diff --git a/libs/hwui/pipeline/skia/SkiaUtils.h b/libs/hwui/pipeline/skia/SkiaUtils.h
index 8344469..fa7f1fe 100644
--- a/libs/hwui/pipeline/skia/SkiaUtils.h
+++ b/libs/hwui/pipeline/skia/SkiaUtils.h
@@ -22,7 +22,7 @@
static inline SkRect SkRectMakeLargest() {
const SkScalar v = SK_ScalarMax;
- return { -v, -v, v, v };
+ return {-v, -v, v, v};
};
} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 65ae0dd..1d3a244 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -20,9 +20,9 @@
#include "Readback.h"
#include "SkiaPipeline.h"
#include "SkiaProfileRenderer.h"
+#include "VkInteropFunctorDrawable.h"
#include "renderstate/RenderState.h"
#include "renderthread/Frame.h"
-#include "VkInteropFunctorDrawable.h"
#include <SkSurface.h>
#include <SkTypes.h>
@@ -158,7 +158,7 @@
ALOGW("SkiaVulkanPipeline::allocateHardwareBitmap() failed in GraphicBuffer.create()");
return nullptr;
}
- return sk_sp<Bitmap>(new Bitmap(buffer.get(), skBitmap.info()));
+ return Bitmap::createFrom(buffer, skBitmap.refColorSpace());
}
} /* namespace skiapipeline */
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
index 71ad5e1..156f74a 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -17,23 +17,21 @@
#include "VkFunctorDrawable.h"
#include <private/hwui/DrawVkInfo.h>
-#include "thread/ThreadBase.h"
-#include "utils/TimeUtils.h"
#include <GrBackendDrawableInfo.h>
-#include <thread>
+#include <SkImage.h>
#include <utils/Color.h>
#include <utils/Trace.h>
#include <utils/TraceUtils.h>
-#include <SkImage.h>
#include <vk/GrVkTypes.h>
+#include <thread>
+#include "thread/ThreadBase.h"
+#include "utils/TimeUtils.h"
namespace android {
namespace uirenderer {
namespace skiapipeline {
-VkFunctorDrawHandler::VkFunctorDrawHandler(Functor *functor)
- : INHERITED()
- , mFunctor(functor) {}
+VkFunctorDrawHandler::VkFunctorDrawHandler(Functor* functor) : INHERITED(), mFunctor(functor) {}
VkFunctorDrawHandler::~VkFunctorDrawHandler() {
// TODO(cblume) Fill in the DrawVkInfo parameters.
@@ -55,14 +53,12 @@
(*mFunctor)(DrawVkInfo::kModeComposite, &draw_vk_info);
}
-VkFunctorDrawable::VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener,
- SkCanvas* canvas)
- : FunctorDrawable(functor, listener, canvas) {}
-
-VkFunctorDrawable::~VkFunctorDrawable() = default;
-
-void VkFunctorDrawable::syncFunctor() const {
- (*mFunctor)(DrawVkInfo::kModeSync, nullptr);
+VkFunctorDrawable::~VkFunctorDrawable() {
+ if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
+ if (lp->listener) {
+ lp->listener->onGlFunctorReleased(lp->functor);
+ }
+ }
}
void VkFunctorDrawable::onDraw(SkCanvas* /*canvas*/) {
@@ -71,12 +67,17 @@
}
std::unique_ptr<FunctorDrawable::GpuDrawHandler> VkFunctorDrawable::onSnapGpuDrawHandler(
- GrBackendApi backendApi, const SkMatrix& matrix) {
+ GrBackendApi backendApi, const SkMatrix& matrix) {
if (backendApi != GrBackendApi::kVulkan) {
return nullptr;
}
- std::unique_ptr<VkFunctorDrawHandler> draw(new VkFunctorDrawHandler(mFunctor));
- return std::move(draw);
+ std::unique_ptr<VkFunctorDrawHandler> draw;
+ if (mAnyFunctor.index() == 0) {
+ LOG_ALWAYS_FATAL("Not implemented");
+ return nullptr;
+ } else {
+ return std::make_unique<VkFunctorDrawHandler>(std::get<1>(mAnyFunctor).functor);
+ }
}
} // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.h b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
index 5cd1314..d6fefc1 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
@@ -18,9 +18,9 @@
#include "FunctorDrawable.h"
-#include <utils/RefBase.h>
-#include <ui/GraphicBuffer.h>
#include <SkImageInfo.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/RefBase.h>
namespace android {
namespace uirenderer {
@@ -36,6 +36,7 @@
~VkFunctorDrawHandler() override;
void draw(const GrBackendDrawableInfo& info) override;
+
private:
typedef GpuDrawHandler INHERITED;
@@ -48,17 +49,15 @@
*/
class VkFunctorDrawable : public FunctorDrawable {
public:
- VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener,
- SkCanvas* canvas);
- ~VkFunctorDrawable() override;
+ using FunctorDrawable::FunctorDrawable;
- void syncFunctor() const override;
+ ~VkFunctorDrawable() override;
protected:
// SkDrawable functions:
void onDraw(SkCanvas* canvas) override;
- std::unique_ptr<FunctorDrawable::GpuDrawHandler> onSnapGpuDrawHandler(GrBackendApi backendApi,
- const SkMatrix& matrix) override;
+ std::unique_ptr<FunctorDrawable::GpuDrawHandler> onSnapGpuDrawHandler(
+ GrBackendApi backendApi, const SkMatrix& matrix) override;
};
} // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index 8228550..a5faae7 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -17,13 +17,13 @@
#include "VkInteropFunctorDrawable.h"
#include <private/hwui/DrawGlInfo.h>
-#include "renderthread/EglManager.h"
-#include "thread/ThreadBase.h"
-#include "utils/TimeUtils.h"
-#include <thread>
#include <utils/Color.h>
#include <utils/Trace.h>
#include <utils/TraceUtils.h>
+#include <thread>
+#include "renderthread/EglManager.h"
+#include "thread/ThreadBase.h"
+#include "utils/TimeUtils.h"
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
@@ -44,6 +44,7 @@
class ScopedDrawRequest {
public:
ScopedDrawRequest() { beginDraw(); }
+
private:
void beginDraw() {
std::lock_guard _lock{sLock};
@@ -57,9 +58,7 @@
}
if (!sEglManager.hasEglContext()) {
- sGLDrawThread->queue().runSync([]() {
- sEglManager.initialize();
- });
+ sGLDrawThread->queue().runSync([]() { sEglManager.initialize(); });
}
}
};
@@ -93,14 +92,14 @@
if (!mFrameBuffer.get() || mFBInfo != surfaceInfo) {
// Buffer will be used as an OpenGL ES render target.
mFrameBuffer = new GraphicBuffer(
- //TODO: try to reduce the size of the buffer: possibly by using clip bounds.
- static_cast<uint32_t>(surfaceInfo.width()),
- static_cast<uint32_t>(surfaceInfo.height()),
- ColorTypeToPixelFormat(surfaceInfo.colorType()),
- GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
- GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_HW_RENDER,
- std::string("VkInteropFunctorDrawable::onDraw pid [") + std::to_string(getpid()) +
- "]");
+ // TODO: try to reduce the size of the buffer: possibly by using clip bounds.
+ static_cast<uint32_t>(surfaceInfo.width()),
+ static_cast<uint32_t>(surfaceInfo.height()),
+ ColorTypeToPixelFormat(surfaceInfo.colorType()),
+ GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
+ GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_HW_RENDER,
+ std::string("VkInteropFunctorDrawable::onDraw pid [") + std::to_string(getpid()) +
+ "]");
status_t error = mFrameBuffer->initCheck();
if (error < 0) {
ALOGW("VkInteropFunctorDrawable::onDraw() failed in GraphicBuffer.create()");
@@ -110,16 +109,15 @@
mFBInfo = surfaceInfo;
}
- //TODO: Synchronization is needed on mFrameBuffer to guarantee that the previous Vulkan
- //TODO: draw command has completed.
- //TODO: A simple but inefficient way is to flush and issue a QueueWaitIdle call. See
- //TODO: GrVkGpu::destroyResources() for example.
+ // TODO: Synchronization is needed on mFrameBuffer to guarantee that the previous Vulkan
+ // TODO: draw command has completed.
+ // TODO: A simple but inefficient way is to flush and issue a QueueWaitIdle call. See
+ // TODO: GrVkGpu::destroyResources() for example.
bool success = sGLDrawThread->queue().runSync([&]() -> bool {
ATRACE_FORMAT("WebViewDraw_%dx%d", mFBInfo.width(), mFBInfo.height());
EGLDisplay display = sEglManager.eglDisplay();
- LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY,
- "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
- uirenderer::renderthread::EglManager::eglErrorString());
+ LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
+ uirenderer::renderthread::EglManager::eglErrorString());
// We use an EGLImage to access the content of the GraphicBuffer
// The EGL image is later bound to a 2D texture
EGLClientBuffer clientBuffer = (EGLClientBuffer)mFrameBuffer->getNativeBuffer();
@@ -154,10 +152,10 @@
AutoGLFramebuffer glFb;
// Bind texture to the frame buffer.
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- glTexture.mTexture, 0);
+ glTexture.mTexture, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
ALOGE("Failed framebuffer check for created target buffer: %s",
- GLUtils::getGLFramebufferError());
+ GLUtils::getGLFramebufferError());
return false;
}
@@ -166,19 +164,22 @@
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
- (*mFunctor)(DrawGlInfo::kModeDraw, &info);
+ if (mAnyFunctor.index() == 0) {
+ std::get<0>(mAnyFunctor).handle->drawGl(info);
+ } else {
+ (*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeDraw, &info);
+ }
EGLSyncKHR glDrawFinishedFence =
eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
LOG_ALWAYS_FATAL_IF(glDrawFinishedFence == EGL_NO_SYNC_KHR,
- "Could not create sync fence %#x", eglGetError());
+ "Could not create sync fence %#x", eglGetError());
glFlush();
// TODO: export EGLSyncKHR in file descr
// TODO: import file desc in Vulkan Semaphore
// TODO: instead block the GPU: probably by using external Vulkan semaphore.
// Block the CPU until the glFlush finish.
- EGLint waitStatus = eglClientWaitSyncKHR(display, glDrawFinishedFence, 0,
- FENCE_TIMEOUT);
+ EGLint waitStatus = eglClientWaitSyncKHR(display, glDrawFinishedFence, 0, FENCE_TIMEOUT);
LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
"Failed to wait for the fence %#x", eglGetError());
eglDestroySyncKHR(display, glDrawFinishedFence);
@@ -197,26 +198,25 @@
canvas->resetMatrix();
auto functorImage = SkImage::MakeFromAHardwareBuffer(
- reinterpret_cast<AHardwareBuffer*>(mFrameBuffer.get()), kPremul_SkAlphaType,
- nullptr, kBottomLeft_GrSurfaceOrigin);
+ reinterpret_cast<AHardwareBuffer*>(mFrameBuffer.get()), kPremul_SkAlphaType, nullptr,
+ kBottomLeft_GrSurfaceOrigin);
canvas->drawImage(functorImage, 0, 0, &paint);
canvas->restore();
}
VkInteropFunctorDrawable::~VkInteropFunctorDrawable() {
- if (mListener.get() != nullptr) {
- ScopedDrawRequest _drawRequest{};
- sGLDrawThread->queue().runSync([&]() {
- mListener->onGlFunctorReleased(mFunctor);
- });
+ if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
+ if (lp->listener) {
+ ScopedDrawRequest _drawRequest{};
+ sGLDrawThread->queue().runSync(
+ [&]() { lp->listener->onGlFunctorReleased(lp->functor); });
+ }
}
}
-void VkInteropFunctorDrawable::syncFunctor() const {
+void VkInteropFunctorDrawable::syncFunctor(const WebViewSyncData& data) const {
ScopedDrawRequest _drawRequest{};
- sGLDrawThread->queue().runSync([&]() {
- (*mFunctor)(DrawGlInfo::kModeSync, nullptr);
- });
+ sGLDrawThread->queue().runSync([&]() { FunctorDrawable::syncFunctor(data); });
}
} // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
index 8fe52c5..c47ee11 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
@@ -18,8 +18,8 @@
#include "FunctorDrawable.h"
-#include <utils/RefBase.h>
#include <ui/GraphicBuffer.h>
+#include <utils/RefBase.h>
namespace android {
namespace uirenderer {
@@ -32,20 +32,18 @@
*/
class VkInteropFunctorDrawable : public FunctorDrawable {
public:
- VkInteropFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener,
- SkCanvas* canvas)
- : FunctorDrawable(functor, listener, canvas) {}
+ using FunctorDrawable::FunctorDrawable;
+
virtual ~VkInteropFunctorDrawable();
- void syncFunctor() const override;
-
static void vkInvokeFunctor(Functor* functor);
+ void syncFunctor(const WebViewSyncData& data) const override;
+
protected:
virtual void onDraw(SkCanvas* canvas) override;
private:
-
// Variables below describe/store temporary offscreen buffer used for Vulkan pipeline.
sp<GraphicBuffer> mFrameBuffer;
SkImageInfo mFBInfo;
diff --git a/libs/hwui/private/hwui/WebViewFunctor.h b/libs/hwui/private/hwui/WebViewFunctor.h
new file mode 100644
index 0000000..e5346aa
--- /dev/null
+++ b/libs/hwui/private/hwui/WebViewFunctor.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
+#define FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
+
+#include <private/hwui/DrawGlInfo.h>
+
+namespace android::uirenderer {
+
+enum class RenderMode {
+ OpenGL_ES,
+ Vulkan,
+};
+
+// Static for the lifetime of the process
+RenderMode WebViewFunctor_queryPlatformRenderMode();
+
+struct WebViewSyncData {
+ bool applyForceDark;
+};
+
+struct WebViewFunctorCallbacks {
+ // kModeSync, called on RenderThread
+ void (*onSync)(int functor, const WebViewSyncData& syncData);
+
+ // Called when either the context is destroyed _or_ when the functor's last reference goes
+ // away. Will always be called with an active context and always on renderthread.
+ void (*onContextDestroyed)(int functor);
+
+ // Called when the last reference to the handle goes away and the handle is considered
+ // irrevocably destroyed. Will always be proceeded by a call to onContextDestroyed if
+ // this functor had ever been drawn.
+ void (*onDestroyed)(int functor);
+
+ union {
+ struct {
+ // Called on RenderThread. initialize is guaranteed to happen before this call
+ void (*draw)(int functor, const DrawGlInfo& params);
+ } gles;
+ // TODO: VK support. The current DrawVkInfo is monolithic and needs to be split up for
+ // what params are valid on what callbacks
+ struct {
+ // Called either the first time the functor is used or the first time it's used after
+ // a call to onContextDestroyed.
+ // void (*initialize)(int functor, const InitParams& params);
+ // void (*frameStart)(int functor, /* todo: what params are actually needed for this to
+ // be useful? Is this useful? */)
+ // void (*draw)(int functor, const CompositeParams& params /* todo: rename - composite
+ // almost always means something else, and we aren't compositing */);
+ // void (*frameEnd)(int functor, const PostCompositeParams& params /* todo: same as
+ // CompositeParams - rename */);
+ } vk;
+ };
+};
+
+// Creates a new WebViewFunctor from the given prototype. The prototype is copied after
+// this function returns. Caller retains full ownership of it.
+// Returns -1 if the creation fails (such as an unsupported functorMode + platform mode combination)
+int WebViewFunctor_create(const WebViewFunctorCallbacks& prototype, RenderMode functorMode);
+
+// May be called on any thread to signal that the functor should be destroyed.
+// The functor will receive an onDestroyed when the last usage of it is released,
+// and it should be considered alive & active until that point.
+void WebViewFunctor_release(int functor);
+
+} // namespace android::uirenderer
+
+#endif // FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 4e4262c..8e57a3a 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -576,8 +576,7 @@
ATRACE_CALL();
if (level >= TRIM_MEMORY_COMPLETE) {
thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
- thread.destroyGlContext();
- thread.vulkanManager().destroy();
+ thread.destroyRenderingContext();
} else if (level >= TRIM_MEMORY_UI_HIDDEN) {
thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
}
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 085812a0..aa6af23 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -30,6 +30,7 @@
#include "renderthread/RenderThread.h"
#include "utils/Macros.h"
#include "utils/TimeUtils.h"
+#include "WebViewFunctorManager.h"
#include <ui/GraphicBuffer.h>
@@ -143,6 +144,14 @@
}
}
+void RenderProxy::destroyFunctor(int functor) {
+ ATRACE_CALL();
+ RenderThread& thread = RenderThread::getInstance();
+ thread.queue().post([=]() {
+ WebViewFunctorManager::instance().destroyFunctor(functor);
+ });
+}
+
DeferredLayerUpdater* RenderProxy::createTextureLayer() {
return mRenderThread.queue().runSync([this]() -> auto {
return mContext->createTextureLayer();
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index d9b789f..9dc9181 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -82,6 +82,7 @@
ANDROID_API void destroy();
ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion);
+ static void destroyFunctor(int functor);
ANDROID_API DeferredLayerUpdater* createTextureLayer();
ANDROID_API void buildLayer(RenderNode* node);
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 207673c1..c06fadd 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -132,6 +132,7 @@
, mFrameCallbackTaskPending(false)
, mRenderState(nullptr)
, mEglManager(nullptr)
+ , mFunctorManager(WebViewFunctorManager::instance())
, mVkManager(nullptr) {
Properties::load();
start("RenderThread");
@@ -197,11 +198,13 @@
setGrContext(grContext);
}
-void RenderThread::destroyGlContext() {
+void RenderThread::destroyRenderingContext() {
+ mFunctorManager.onContextDestroyed();
if (mEglManager->hasEglContext()) {
setGrContext(nullptr);
mEglManager->destroy();
}
+ vulkanManager().destroy();
}
void RenderThread::dumpGraphicsMemory(int fd) {
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 2384f95..12666b3 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -23,6 +23,7 @@
#include "CacheManager.h"
#include "TimeLord.h"
#include "thread/ThreadBase.h"
+#include "WebViewFunctorManager.h"
#include <GrContext.h>
#include <SkBitmap.h>
@@ -104,7 +105,7 @@
void dumpGraphicsMemory(int fd);
void requireGlContext();
- void destroyGlContext();
+ void destroyRenderingContext();
/**
* isCurrent provides a way to query, if the caller is running on
@@ -151,6 +152,7 @@
TimeLord mTimeLord;
RenderState* mRenderState;
EglManager* mEglManager;
+ WebViewFunctorManager& mFunctorManager;
ProfileDataContainer mGlobalProfileData;
Readback* mReadback = nullptr;
diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp
index 15aec9f..4a2f57e 100644
--- a/libs/hwui/surfacetexture/ImageConsumer.cpp
+++ b/libs/hwui/surfacetexture/ImageConsumer.cpp
@@ -70,7 +70,8 @@
int slot = st.mCurrentTexture;
if (slot != BufferItem::INVALID_BUFFER_SLOT) {
*queueEmpty = true;
- mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace);
+ mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer,
+ st.mCurrentDataSpace);
return mImageSlots[slot].mImage;
}
}
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index f812022..7aa9b82 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -32,6 +32,8 @@
namespace android {
namespace uirenderer {
+std::unordered_map<int, TestUtils::CallCounts> TestUtils::sMockFunctorCounts{};
+
SkColor TestUtils::interpolateColor(float fraction, SkColor start, SkColor end) {
int startA = (start >> 24) & 0xff;
int startR = (start >> 16) & 0xff;
@@ -82,12 +84,10 @@
uint32_t length = strlen(text);
SkPaint glyphPaint(paint);
glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding);
- canvas->drawText(
- utf16.get(), length, // text buffer
- 0, length, // draw range
- 0, length, // context range
- x, y, minikin::Bidi::LTR,
- glyphPaint, nullptr, nullptr /* measured text */);
+ canvas->drawText(utf16.get(), length, // text buffer
+ 0, length, // draw range
+ 0, length, // context range
+ x, y, minikin::Bidi::LTR, glyphPaint, nullptr, nullptr /* measured text */);
}
void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint& paint,
@@ -96,7 +96,7 @@
SkPaint glyphPaint(paint);
glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding);
canvas->drawTextOnPath(utf16.get(), strlen(text), minikin::Bidi::LTR, path, 0, 0, glyphPaint,
- nullptr);
+ nullptr);
}
void TestUtils::TestTask::run() {
@@ -110,11 +110,7 @@
rtCallback(renderThread);
- if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
- renderThread.vulkanManager().destroy();
- } else {
- renderThread.destroyGlContext();
- }
+ renderThread.destroyRenderingContext();
}
std::unique_ptr<uint16_t[]> TestUtils::asciiToUtf16(const char* str) {
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index c5db861d..5ff8993 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -27,6 +27,7 @@
#include <renderstate/RenderState.h>
#include <renderthread/RenderThread.h>
+#include <gtest/gtest.h>
#include <memory>
namespace android {
@@ -201,8 +202,7 @@
static void recordNode(RenderNode& node, std::function<void(Canvas&)> contentCallback) {
std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(
- node.stagingProperties().getWidth(), node.stagingProperties().getHeight(),
- &node));
+ node.stagingProperties().getWidth(), node.stagingProperties().getHeight(), &node));
contentCallback(*canvas.get());
node.setStagingDisplayList(canvas->finishRecording());
}
@@ -267,7 +267,14 @@
renderthread::RenderThread::getInstance().queue().runSync([&]() { task.run(); });
}
+ static void runOnRenderThreadUnmanaged(RtCallback rtCallback) {
+ auto& rt = renderthread::RenderThread::getInstance();
+ rt.queue().runSync([&]() { rtCallback(rt); });
+ }
+
+
static bool isRenderThreadRunning() { return renderthread::RenderThread::hasInstance(); }
+ static pid_t getRenderThreadTid() { return renderthread::RenderThread::getInstance().getTid(); }
static SkColor interpolateColor(float fraction, SkColor start, SkColor end);
@@ -296,7 +303,52 @@
static SkRect getClipBounds(const SkCanvas* canvas);
static SkRect getLocalClipBounds(const SkCanvas* canvas);
+ struct CallCounts {
+ int sync = 0;
+ int contextDestroyed = 0;
+ int destroyed = 0;
+ int glesDraw = 0;
+ };
+
+ static void expectOnRenderThread() { EXPECT_EQ(gettid(), TestUtils::getRenderThreadTid()); }
+
+ static WebViewFunctorCallbacks createMockFunctor(RenderMode mode) {
+ auto callbacks = WebViewFunctorCallbacks{
+ .onSync =
+ [](int functor, const WebViewSyncData& data) {
+ expectOnRenderThread();
+ sMockFunctorCounts[functor].sync++;
+ },
+ .onContextDestroyed =
+ [](int functor) {
+ expectOnRenderThread();
+ sMockFunctorCounts[functor].contextDestroyed++;
+ },
+ .onDestroyed =
+ [](int functor) {
+ expectOnRenderThread();
+ sMockFunctorCounts[functor].destroyed++;
+ },
+ };
+ switch (mode) {
+ case RenderMode::OpenGL_ES:
+ callbacks.gles.draw = [](int functor, const DrawGlInfo& params) {
+ expectOnRenderThread();
+ sMockFunctorCounts[functor].glesDraw++;
+ };
+ break;
+ default:
+ ADD_FAILURE();
+ return WebViewFunctorCallbacks{};
+ }
+ return callbacks;
+ }
+
+ static CallCounts& countsForFunctor(int functor) { return sMockFunctorCounts[functor]; }
+
private:
+ static std::unordered_map<int, CallCounts> sMockFunctorCounts;
+
static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
MarkAndSweepRemoved observer(nullptr);
node->syncProperties();
@@ -306,9 +358,9 @@
}
auto displayList = node->getDisplayList();
if (displayList) {
- for (auto&& childDr : static_cast<skiapipeline::SkiaDisplayList*>(
- const_cast<DisplayList*>(displayList))
- ->mChildNodes) {
+ for (auto&& childDr :
+ static_cast<skiapipeline::SkiaDisplayList*>(const_cast<DisplayList*>(displayList))
+ ->mChildNodes) {
syncHierarchyPropertiesAndDisplayListImpl(childDr.getRenderNode());
}
}
diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
index 448408d..ec81f62 100644
--- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
+++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
@@ -50,7 +50,7 @@
pixels[4000 + 4 * i + 3] = 255;
}
buffer->unlock();
- sk_sp<Bitmap> hardwareBitmap(Bitmap::createFrom(buffer));
+ sk_sp<Bitmap> hardwareBitmap(Bitmap::createFrom(buffer, SkColorSpace::MakeSRGB()));
sk_sp<SkShader> hardwareShader(createBitmapShader(*hardwareBitmap));
SkPoint center;
diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
index a686979..f4c3e13 100644
--- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
+++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
@@ -19,9 +19,9 @@
#include "tests/common/TestUtils.h"
-#include <gtest/gtest.h>
#include <SkBitmap.h>
#include <SkImage.h>
+#include <gtest/gtest.h>
using namespace android;
using namespace android::uirenderer;
diff --git a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
index 08b9679..dac888c 100644
--- a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
+++ b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
@@ -39,7 +39,7 @@
// current thread can spoof being a GPU thread
static void destroyEglContext() {
if (TestUtils::isRenderThreadRunning()) {
- TestUtils::runOnRenderThread([](RenderThread& thread) { thread.destroyGlContext(); });
+ TestUtils::runOnRenderThread([](RenderThread& thread) { thread.destroyRenderingContext(); });
}
}
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 0331581..c813cd9 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -355,9 +355,7 @@
class ProjectionTestCanvas : public SkCanvas {
public:
ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
- void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
- mDrawCounter++;
- }
+ void onDrawRect(const SkRect& rect, const SkPaint& paint) override { mDrawCounter++; }
int getDrawCounter() { return mDrawCounter; }
@@ -370,7 +368,7 @@
[](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
properties.setProjectionReceiver(true);
},
- "B"); // a receiver with an empty display list
+ "B"); // a receiver with an empty display list
auto projectingRipple = TestUtils::createSkiaNode(
0, 0, 100, 100,
@@ -389,14 +387,14 @@
canvas.drawRenderNode(projectingRipple.get());
},
"C");
- auto parent = TestUtils::createSkiaNode(
- 0, 0, 100, 100,
- [&receiverBackground, &child](RenderProperties& properties,
- SkiaRecordingCanvas& canvas) {
- canvas.drawRenderNode(receiverBackground.get());
- canvas.drawRenderNode(child.get());
- },
- "A");
+ auto parent =
+ TestUtils::createSkiaNode(0, 0, 100, 100,
+ [&receiverBackground, &child](RenderProperties& properties,
+ SkiaRecordingCanvas& canvas) {
+ canvas.drawRenderNode(receiverBackground.get());
+ canvas.drawRenderNode(child.get());
+ },
+ "A");
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
@@ -1058,7 +1056,7 @@
public:
FrameTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
- const SkPaint* paint, SrcRectConstraint constraint) override {
+ const SkPaint* paint, SrcRectConstraint constraint) override {
mDrawCounter++;
EXPECT_EQ(kLow_SkFilterQuality, paint->getFilterQuality());
}
@@ -1076,7 +1074,7 @@
FrameTestCanvas canvas;
RenderNodeDrawable drawable(layerNode.get(), &canvas, true);
canvas.drawDrawable(&drawable);
- EXPECT_EQ(1, canvas.mDrawCounter); //make sure the layer was composed
+ EXPECT_EQ(1, canvas.mDrawCounter); // make sure the layer was composed
// clean up layer pointer, so we can safely destruct RenderNode
layerNode->setLayerSurface(nullptr);
@@ -1129,15 +1127,14 @@
getTotalMatrix());
} else {
// Second invocation is preparing the matrix for an elevated RenderNodeDrawable.
- EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y),
- matrix);
- EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y),
- getTotalMatrix());
+ EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y), matrix);
+ EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y), getTotalMatrix());
}
}
protected:
int mDrawCounter = 0;
+
private:
bool mFirstDidConcat = true;
};
@@ -1174,14 +1171,14 @@
public:
VectorDrawableTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
void onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
- const SkPaint* paint, SrcRectConstraint constraint) override {
+ const SkPaint* paint, SrcRectConstraint constraint) override {
const int index = mDrawCounter++;
switch (index) {
case 0:
EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT));
break;
case 1:
- EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH/2, CANVAS_HEIGHT));
+ EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH / 2, CANVAS_HEIGHT));
break;
default:
ADD_FAILURE();
@@ -1191,17 +1188,18 @@
VectorDrawable::Group* group = new VectorDrawable::Group();
sp<VectorDrawableRoot> vectorDrawable(new VectorDrawableRoot(group));
- vectorDrawable->mutateStagingProperties()->setScaledSize(CANVAS_WIDTH/10, CANVAS_HEIGHT/10);
+ vectorDrawable->mutateStagingProperties()->setScaledSize(CANVAS_WIDTH / 10, CANVAS_HEIGHT / 10);
- auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
- [&](RenderProperties& props, SkiaRecordingCanvas& canvas) {
- vectorDrawable->mutateStagingProperties()->setBounds(SkRect::MakeWH(CANVAS_WIDTH,
- CANVAS_HEIGHT));
- canvas.drawVectorDrawable(vectorDrawable.get());
- vectorDrawable->mutateStagingProperties()->setBounds(SkRect::MakeWH(CANVAS_WIDTH/2,
- CANVAS_HEIGHT));
- canvas.drawVectorDrawable(vectorDrawable.get());
- });
+ auto node =
+ TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+ [&](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ vectorDrawable->mutateStagingProperties()->setBounds(
+ SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT));
+ canvas.drawVectorDrawable(vectorDrawable.get());
+ vectorDrawable->mutateStagingProperties()->setBounds(
+ SkRect::MakeWH(CANVAS_WIDTH / 2, CANVAS_HEIGHT));
+ canvas.drawVectorDrawable(vectorDrawable.get());
+ });
VectorDrawableTestCanvas canvas;
RenderNodeDrawable drawable(node.get(), &canvas, true);
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index 75fb0ef..1cd9bd8 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -295,7 +295,8 @@
canvasContext->destroy();
}
-RENDERTHREAD_TEST(RenderNode, prepareTree_HwLayer_AVD_enqueueDamage) {
+// TODO: Is this supposed to work in SkiaGL/SkiaVK?
+RENDERTHREAD_TEST(DISABLED_RenderNode, prepareTree_HwLayer_AVD_enqueueDamage) {
VectorDrawable::Group* group = new VectorDrawable::Group();
sp<VectorDrawableRoot> vectorDrawable(new VectorDrawableRoot(group));
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
index 1433aa0..87981f1 100644
--- a/libs/hwui/tests/unit/ShaderCacheTests.cpp
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-#include <gtest/gtest.h>
-#include <dirent.h>
#include <cutils/properties.h>
-#include <cstdint>
+#include <dirent.h>
#include <errno.h>
+#include <gtest/gtest.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <utils/Log.h>
-#include "pipeline/skia/ShaderCache.h"
+#include <cstdint>
#include "FileBlobCache.h"
+#include "pipeline/skia/ShaderCache.h"
using namespace android::uirenderer::skiapipeline;
@@ -66,7 +66,6 @@
} /* namespace uirenderer */
} /* namespace android */
-
namespace {
std::string getExternalStorageFolder() {
@@ -82,14 +81,12 @@
return false;
}
-inline bool
-checkShader(const sk_sp<SkData>& shader1, const sk_sp<SkData>& shader2) {
- return nullptr != shader1 && nullptr != shader2 && shader1->size() == shader2->size()
- && 0 == memcmp(shader1->data(), shader2->data(), shader1->size());
+inline bool checkShader(const sk_sp<SkData>& shader1, const sk_sp<SkData>& shader2) {
+ return nullptr != shader1 && nullptr != shader2 && shader1->size() == shader2->size() &&
+ 0 == memcmp(shader1->data(), shader2->data(), shader1->size());
}
-inline bool
-checkShader(const sk_sp<SkData>& shader, const char* program) {
+inline bool checkShader(const sk_sp<SkData>& shader, const char* program) {
sk_sp<SkData> shader2 = SkData::MakeWithCString(program);
return checkShader(shader, shader2);
}
@@ -116,32 +113,31 @@
}
}
-
#define GrProgramDescTest(a) (*SkData::MakeWithCString(#a).get())
TEST(ShaderCacheTest, testWriteAndRead) {
if (!folderExist(getExternalStorageFolder())) {
- //don't run the test if external storage folder is not available
+ // don't run the test if external storage folder is not available
return;
}
- std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
- std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
+ std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
+ std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
- //remove any test files from previous test run
+ // remove any test files from previous test run
int deleteFile = remove(cacheFile1.c_str());
ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
std::srand(0);
- //read the cache from a file that does not exist
+ // read the cache from a file that does not exist
ShaderCache::get().setFilename(cacheFile1.c_str());
- ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); //disable deferred save
+ ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); // disable deferred save
ShaderCache::get().initShaderDiskCache();
- //read a key - should not be found since the cache is empty
+ // read a key - should not be found since the cache is empty
sk_sp<SkData> outVS;
ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
- //write to the in-memory cache without storing on disk and verify we read the same values
+ // write to the in-memory cache without storing on disk and verify we read the same values
sk_sp<SkData> inVS;
setShader(inVS, "sassas");
ShaderCache::get().store(GrProgramDescTest(100), *inVS.get());
@@ -152,23 +148,23 @@
ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
ASSERT_TRUE(checkShader(outVS, "someVS"));
- //store content to disk and release in-memory cache
+ // store content to disk and release in-memory cache
ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
- //change to a file that does not exist and verify load fails
+ // change to a file that does not exist and verify load fails
ShaderCache::get().setFilename(cacheFile2.c_str());
ShaderCache::get().initShaderDiskCache();
ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
- //load again content from disk from an existing file and check the data is read correctly
+ // load again content from disk from an existing file and check the data is read correctly
ShaderCache::get().setFilename(cacheFile1.c_str());
ShaderCache::get().initShaderDiskCache();
sk_sp<SkData> outVS2;
ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
ASSERT_TRUE(checkShader(outVS2, "someVS"));
- //change data, store to disk, read back again and verify data has been changed
+ // change data, store to disk, read back again and verify data has been changed
setShader(inVS, "ewData1");
ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
@@ -176,9 +172,8 @@
ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
ASSERT_TRUE(checkShader(outVS2, "ewData1"));
-
- //write and read big data chunk (50K)
- size_t dataSize = 50*1024;
+ // write and read big data chunk (50K)
+ size_t dataSize = 50 * 1024;
std::vector<uint8_t> dataBuffer(dataSize);
genRandomData(dataBuffer);
setShader(inVS, dataBuffer);
@@ -194,31 +189,31 @@
TEST(ShaderCacheTest, testCacheValidation) {
if (!folderExist(getExternalStorageFolder())) {
- //don't run the test if external storage folder is not available
+ // don't run the test if external storage folder is not available
return;
}
- std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
- std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
+ std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
+ std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
- //remove any test files from previous test run
+ // remove any test files from previous test run
int deleteFile = remove(cacheFile1.c_str());
ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
std::srand(0);
- //generate identity and read the cache from a file that does not exist
+ // generate identity and read the cache from a file that does not exist
ShaderCache::get().setFilename(cacheFile1.c_str());
- ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); //disable deferred save
+ ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); // disable deferred save
std::vector<uint8_t> identity(1024);
genRandomData(identity);
- ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
- sizeof(decltype(identity)::value_type));
+ ShaderCache::get().initShaderDiskCache(
+ identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
// generate random content in cache and store to disk
constexpr size_t numBlob(10);
constexpr size_t keySize(1024);
constexpr size_t dataSize(50 * 1024);
- std::vector< std::pair<sk_sp<SkData>, sk_sp<SkData>> > blobVec(numBlob);
+ std::vector<std::pair<sk_sp<SkData>, sk_sp<SkData>>> blobVec(numBlob);
for (auto& blob : blobVec) {
std::vector<uint8_t> keyBuffer(keySize);
std::vector<uint8_t> dataBuffer(dataSize);
@@ -237,47 +232,47 @@
// change to a file that does not exist and verify validation fails
ShaderCache::get().setFilename(cacheFile2.c_str());
ShaderCache::get().initShaderDiskCache();
- ASSERT_FALSE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
+ ASSERT_FALSE(ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity));
ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
// restore the original file and verify validation succeeds
ShaderCache::get().setFilename(cacheFile1.c_str());
- ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
- sizeof(decltype(identity)::value_type));
- ASSERT_TRUE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
+ ShaderCache::get().initShaderDiskCache(
+ identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
+ ASSERT_TRUE(ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity));
for (const auto& blob : blobVec) {
auto outVS = ShaderCache::get().load(*blob.first.get());
- ASSERT_TRUE( checkShader(outVS, blob.second) );
+ ASSERT_TRUE(checkShader(outVS, blob.second));
}
// generate error identity and verify load fails
ShaderCache::get().initShaderDiskCache(identity.data(), -1);
for (const auto& blob : blobVec) {
- ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
+ ASSERT_EQ(ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>());
}
- ShaderCache::get().initShaderDiskCache(nullptr, identity.size() *
- sizeof(decltype(identity)::value_type));
+ ShaderCache::get().initShaderDiskCache(
+ nullptr, identity.size() * sizeof(decltype(identity)::value_type));
for (const auto& blob : blobVec) {
- ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
+ ASSERT_EQ(ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>());
}
// verify the cache validation again after load fails
- ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
- sizeof(decltype(identity)::value_type));
- ASSERT_TRUE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
+ ShaderCache::get().initShaderDiskCache(
+ identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
+ ASSERT_TRUE(ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity));
for (const auto& blob : blobVec) {
auto outVS = ShaderCache::get().load(*blob.first.get());
- ASSERT_TRUE( checkShader(outVS, blob.second) );
+ ASSERT_TRUE(checkShader(outVS, blob.second));
}
// generate another identity and verify load fails
for (auto& data : identity) {
data += std::rand();
}
- ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
- sizeof(decltype(identity)::value_type));
+ ShaderCache::get().initShaderDiskCache(
+ identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
for (const auto& blob : blobVec) {
- ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
+ ASSERT_EQ(ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>());
}
ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 415f9e8..53bf84f 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -100,16 +100,35 @@
GLFunctorDrawable functorDrawable(&functor, nullptr, &dummyCanvas);
skiaDL.mChildFunctors.push_back(&functorDrawable);
+ int functor2 = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
+ RenderMode::OpenGL_ES);
+ auto& counts = TestUtils::countsForFunctor(functor2);
+ skiaDL.mChildFunctors.push_back(
+ skiaDL.allocateDrawable<GLFunctorDrawable>(functor2, &dummyCanvas));
+ WebViewFunctor_release(functor2);
+
SkRect bounds = SkRect::MakeWH(200, 200);
VectorDrawableRoot vectorDrawable(new VectorDrawable::Group());
vectorDrawable.mutateStagingProperties()->setBounds(bounds);
skiaDL.mVectorDrawables.push_back(&vectorDrawable);
// ensure that the functor and vectorDrawable are properly synced
- skiaDL.syncContents();
+ TestUtils::runOnRenderThread([&](auto&) {
+ skiaDL.syncContents(WebViewSyncData{
+ .applyForceDark = false,
+ });
+ });
- ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeSync);
- ASSERT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds);
+ EXPECT_EQ(functor.getLastMode(), DrawGlInfo::kModeSync);
+ EXPECT_EQ(counts.sync, 1);
+ EXPECT_EQ(counts.destroyed, 0);
+ EXPECT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds);
+
+ skiaDL.reset();
+ TestUtils::runOnRenderThread([](auto&) {
+ // Fence
+ });
+ EXPECT_EQ(counts.destroyed, 1);
}
class ContextFactory : public IContextFactory {
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index d16b8be..3c06dab 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -24,10 +24,10 @@
#include "DamageAccumulator.h"
#include "IContextFactory.h"
#include "SkiaCanvas.h"
-#include "pipeline/skia/SkiaUtils.h"
#include "pipeline/skia/SkiaDisplayList.h"
#include "pipeline/skia/SkiaOpenGLPipeline.h"
#include "pipeline/skia/SkiaRecordingCanvas.h"
+#include "pipeline/skia/SkiaUtils.h"
#include "renderthread/CanvasContext.h"
#include "tests/common/TestUtils.h"
@@ -51,8 +51,7 @@
auto surface = SkSurface::MakeRasterN32Premul(1, 1);
surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
- surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
}
@@ -84,8 +83,7 @@
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
// drawFrame will crash if "SkiaPipeline::onPrepareTree" did not clean invalid VD pointer
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
- surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
}
@@ -106,12 +104,10 @@
auto surface = SkSurface::MakeRasterN32Premul(2, 2);
surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds,
- surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds,
- surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned int)SK_ColorTRANSPARENT);
ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
}
@@ -130,8 +126,7 @@
auto surface = SkSurface::MakeRasterN32Premul(2, 2);
surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds,
- surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
ASSERT_EQ(TestUtils::getColor(surface, 1, 0), SK_ColorBLUE);
ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorRED);
@@ -203,38 +198,32 @@
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
// Single draw, should be white.
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
- surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
// 1 Overdraw, should be blue blended onto white.
renderNodes.push_back(whiteNode);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
- surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0d0ff);
// 2 Overdraw, should be green blended onto white
renderNodes.push_back(whiteNode);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
- surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0ffd0);
// 3 Overdraw, should be pink blended onto white.
renderNodes.push_back(whiteNode);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
- surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffffc0c0);
// 4 Overdraw, should be red blended onto white.
renderNodes.push_back(whiteNode);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
- surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080);
// 5 Overdraw, should be red blended onto white.
renderNodes.push_back(whiteNode);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
- surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080);
}
@@ -389,7 +378,6 @@
EXPECT_FALSE(pipeline->isSurfaceReady());
EXPECT_TRUE(pipeline->setSurface((Surface*)0x01, SwapBehavior::kSwap_default, ColorMode::SRGB));
EXPECT_TRUE(pipeline->isSurfaceReady());
- renderThread.destroyGlContext();
+ renderThread.destroyRenderingContext();
EXPECT_FALSE(pipeline->isSurfaceReady());
}
-
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index b645aeb..1a09b1c 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -54,9 +54,9 @@
sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
sk_sp<SkTypeface> typeface(fm->makeFromStream(std::move(fontData)));
LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName);
- std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
- std::move(typeface), data, st.st_size, fileName, 0,
- std::vector<minikin::FontVariation>());
+ std::shared_ptr<minikin::MinikinFont> font =
+ std::make_shared<MinikinFontSkia>(std::move(typeface), data, st.st_size, fileName, 0,
+ std::vector<minikin::FontVariation>());
std::vector<minikin::Font> fonts;
fonts.push_back(minikin::Font::Builder(font).build());
return std::make_shared<minikin::FontFamily>(std::move(fonts));
diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp
index b11eaa9..5db0028 100644
--- a/libs/hwui/tests/unit/VectorDrawableTests.cpp
+++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp
@@ -85,8 +85,10 @@
outPath->rCubicTo(8.0, 8.0, 8.0, 8.0, 8.0, 8.0);
outPath->cubicTo(16.0, 16.0, 9.0, 9.0, 9.0, 9.0);
outPath->rCubicTo(0.0, 0.0, 9.0, 9.0, 9.0, 9.0);
- outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 10.0, 10.0);
- outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 20.0, 20.0);
+ outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 10.0,
+ 10.0);
+ outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 20.0,
+ 20.0);
}},
// Check box VectorDrawable path data
@@ -157,7 +159,8 @@
},
[](SkPath* outPath) {
outPath->moveTo(300.0, 70.0);
- outPath->arcTo(230.0, 230.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCCW_Direction, 301.0, 70.0);
+ outPath->arcTo(230.0, 230.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCCW_Direction,
+ 301.0, 70.0);
outPath->close();
outPath->moveTo(300.0, 70.0);
}},
@@ -236,14 +239,14 @@
};
const StringPath sStringPaths[] = {
- {"3e...3", false}, // Not starting with a verb and ill-formatted float
- {"L.M.F.A.O", false}, // No floats following verbs
- {"m 1 1", true}, // Valid path data
- {"\n \t z", true}, // Valid path data with leading spaces
- {"1-2e34567", false}, // Not starting with a verb and ill-formatted float
- {"f 4 5", false}, // Invalid verb
- {"\r ", false}, // Empty string
- {"L1,0 L1,1 L0,1 z M1000", false} // Not enough floats following verb M.
+ {"3e...3", false}, // Not starting with a verb and ill-formatted float
+ {"L.M.F.A.O", false}, // No floats following verbs
+ {"m 1 1", true}, // Valid path data
+ {"\n \t z", true}, // Valid path data with leading spaces
+ {"1-2e34567", false}, // Not starting with a verb and ill-formatted float
+ {"f 4 5", false}, // Invalid verb
+ {"\r ", false}, // Empty string
+ {"L1,0 L1,1 L0,1 z M1000", false} // Not enough floats following verb M.
};
static bool hasSameVerbs(const PathData& from, const PathData& to) {
diff --git a/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
new file mode 100644
index 0000000..c8169af
--- /dev/null
+++ b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2018 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 <gtest/gtest.h>
+
+#include "WebViewFunctorManager.h"
+#include "private/hwui/WebViewFunctor.h"
+#include "renderthread/RenderProxy.h"
+#include "tests/common/TestUtils.h"
+
+#include <unordered_map>
+
+using namespace android;
+using namespace android::uirenderer;
+
+TEST(WebViewFunctor, createDestroyGLES) {
+ int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
+ RenderMode::OpenGL_ES);
+ ASSERT_NE(-1, functor);
+ WebViewFunctor_release(functor);
+ TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
+ // Empty, don't care
+ });
+ auto& counts = TestUtils::countsForFunctor(functor);
+ // We never initialized, so contextDestroyed == 0
+ EXPECT_EQ(0, counts.contextDestroyed);
+ EXPECT_EQ(1, counts.destroyed);
+}
+
+TEST(WebViewFunctor, createSyncHandleGLES) {
+ int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
+ RenderMode::OpenGL_ES);
+ ASSERT_NE(-1, functor);
+ auto handle = WebViewFunctorManager::instance().handleFor(functor);
+ ASSERT_TRUE(handle);
+ WebViewFunctor_release(functor);
+ EXPECT_FALSE(WebViewFunctorManager::instance().handleFor(functor));
+ TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
+ // fence
+ });
+ auto& counts = TestUtils::countsForFunctor(functor);
+ EXPECT_EQ(0, counts.sync);
+ EXPECT_EQ(0, counts.contextDestroyed);
+ EXPECT_EQ(0, counts.destroyed);
+
+ TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
+ WebViewSyncData syncData;
+ handle->sync(syncData);
+ });
+
+ EXPECT_EQ(1, counts.sync);
+
+ TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
+ WebViewSyncData syncData;
+ handle->sync(syncData);
+ });
+
+ EXPECT_EQ(2, counts.sync);
+
+ handle.clear();
+
+ TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
+ // fence
+ });
+
+ EXPECT_EQ(2, counts.sync);
+ EXPECT_EQ(0, counts.contextDestroyed);
+ EXPECT_EQ(1, counts.destroyed);
+}
+
+TEST(WebViewFunctor, createSyncDrawGLES) {
+ int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
+ RenderMode::OpenGL_ES);
+ ASSERT_NE(-1, functor);
+ auto handle = WebViewFunctorManager::instance().handleFor(functor);
+ ASSERT_TRUE(handle);
+ WebViewFunctor_release(functor);
+ auto& counts = TestUtils::countsForFunctor(functor);
+ for (int i = 0; i < 5; i++) {
+ TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
+ WebViewSyncData syncData;
+ handle->sync(syncData);
+ DrawGlInfo drawInfo;
+ handle->drawGl(drawInfo);
+ handle->drawGl(drawInfo);
+ });
+ }
+ handle.clear();
+ TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
+ // fence
+ });
+ EXPECT_EQ(5, counts.sync);
+ EXPECT_EQ(10, counts.glesDraw);
+ EXPECT_EQ(1, counts.contextDestroyed);
+ EXPECT_EQ(1, counts.destroyed);
+}
+
+TEST(WebViewFunctor, contextDestroyed) {
+ int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
+ RenderMode::OpenGL_ES);
+ ASSERT_NE(-1, functor);
+ auto handle = WebViewFunctorManager::instance().handleFor(functor);
+ ASSERT_TRUE(handle);
+ WebViewFunctor_release(functor);
+ auto& counts = TestUtils::countsForFunctor(functor);
+ TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
+ WebViewSyncData syncData;
+ handle->sync(syncData);
+ DrawGlInfo drawInfo;
+ handle->drawGl(drawInfo);
+ });
+ EXPECT_EQ(1, counts.sync);
+ EXPECT_EQ(1, counts.glesDraw);
+ EXPECT_EQ(0, counts.contextDestroyed);
+ EXPECT_EQ(0, counts.destroyed);
+ TestUtils::runOnRenderThreadUnmanaged([](auto& rt) {
+ rt.destroyRenderingContext();
+ });
+ EXPECT_EQ(1, counts.sync);
+ EXPECT_EQ(1, counts.glesDraw);
+ EXPECT_EQ(1, counts.contextDestroyed);
+ EXPECT_EQ(0, counts.destroyed);
+ TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
+ WebViewSyncData syncData;
+ handle->sync(syncData);
+ DrawGlInfo drawInfo;
+ handle->drawGl(drawInfo);
+ });
+ EXPECT_EQ(2, counts.sync);
+ EXPECT_EQ(2, counts.glesDraw);
+ EXPECT_EQ(1, counts.contextDestroyed);
+ EXPECT_EQ(0, counts.destroyed);
+ handle.clear();
+ TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
+ // fence
+ });
+ EXPECT_EQ(2, counts.sync);
+ EXPECT_EQ(2, counts.glesDraw);
+ EXPECT_EQ(2, counts.contextDestroyed);
+ EXPECT_EQ(1, counts.destroyed);
+}
\ No newline at end of file
diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp
index aecceb3..63d1540 100644
--- a/libs/hwui/tests/unit/main.cpp
+++ b/libs/hwui/tests/unit/main.cpp
@@ -17,14 +17,14 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "Properties.h"
#include "debug/GlesDriver.h"
#include "debug/NullGlesDriver.h"
#include "hwui/Typeface.h"
-#include "Properties.h"
#include "tests/common/LeakChecker.h"
-#include "thread/TaskProcessor.h"
#include "thread/Task.h"
#include "thread/TaskManager.h"
+#include "thread/TaskProcessor.h"
#include <signal.h>
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index 4daccda..4473ce6 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -17,6 +17,7 @@
#define COLOR_H
#include <math.h>
+#include <cutils/compiler.h>
#include <system/graphics.h>
#include <ui/PixelFormat.h>
@@ -117,7 +118,7 @@
android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType);
-sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
+ANDROID_API sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
struct Lab {
float L;
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 00a393a..d656fa3 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -40,8 +40,7 @@
* MediaMetadataRetriever class provides a unified interface for retrieving
* frame and meta data from an input media file.
*/
-public class MediaMetadataRetriever
-{
+public class MediaMetadataRetriever implements AutoCloseable {
static {
System.loadLibrary("media_jni");
native_init();
@@ -672,6 +671,11 @@
@UnsupportedAppUsage
private native byte[] getEmbeddedPicture(int pictureType);
+ @Override
+ public void close() {
+ release();
+ }
+
/**
* Call it when one is done with the object. This method releases the memory
* allocated internally.
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index fd14060..f07076a 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -16,33 +16,53 @@
package android.media;
+import static android.media.MediaMetadataRetriever.METADATA_KEY_DURATION;
+import static android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT;
+import static android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH;
+import static android.media.MediaMetadataRetriever.OPTION_CLOSEST_SYNC;
+import static android.os.Environment.MEDIA_UNKNOWN;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
+import android.graphics.ImageDecoder;
+import android.graphics.ImageDecoder.ImageInfo;
+import android.graphics.ImageDecoder.Source;
import android.graphics.Matrix;
+import android.graphics.Point;
import android.graphics.Rect;
import android.net.Uri;
+import android.os.CancellationSignal;
+import android.os.Environment;
import android.os.ParcelFileDescriptor;
-import android.provider.MediaStore.Images;
+import android.provider.MediaStore.ThumbnailConstants;
import android.util.Log;
+import android.util.Size;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
+import com.android.internal.util.ArrayUtils;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
import java.io.IOException;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.function.ToIntFunction;
/**
- * Thumbnail generation routines for media provider.
+ * Utilities for generating visual thumbnails from files.
*/
-
public class ThumbnailUtils {
private static final String TAG = "ThumbnailUtils";
- /* Maximum pixels size for created bitmap. */
- private static final int MAX_NUM_PIXELS_THUMBNAIL = 512 * 384;
- private static final int MAX_NUM_PIXELS_MICRO_THUMBNAIL = 160 * 120;
- private static final int UNCONSTRAINED = -1;
+ /** @hide */
+ @Deprecated
+ @UnsupportedAppUsage
+ public static final int TARGET_SIZE_MICRO_THUMBNAIL = 96;
/* Options used internally. */
private static final int OPTIONS_NONE = 0x0;
@@ -54,153 +74,252 @@
*/
public static final int OPTIONS_RECYCLE_INPUT = 0x2;
- /**
- * Constant used to indicate the dimension of mini thumbnail.
- * @hide Only used by media framework and media provider internally.
- */
- public static final int TARGET_SIZE_MINI_THUMBNAIL = 320;
+ private static Size convertKind(int kind) {
+ if (kind == ThumbnailConstants.MICRO_KIND) {
+ return Point.convert(ThumbnailConstants.MICRO_SIZE);
+ } else if (kind == ThumbnailConstants.FULL_SCREEN_KIND) {
+ return Point.convert(ThumbnailConstants.FULL_SCREEN_SIZE);
+ } else if (kind == ThumbnailConstants.MINI_KIND) {
+ return Point.convert(ThumbnailConstants.MINI_SIZE);
+ } else {
+ throw new IllegalArgumentException("Unsupported kind: " + kind);
+ }
+ }
+
+ private static class Resizer implements ImageDecoder.OnHeaderDecodedListener {
+ private final Size size;
+ private final CancellationSignal signal;
+
+ public Resizer(Size size, CancellationSignal signal) {
+ this.size = size;
+ this.signal = signal;
+ }
+
+ @Override
+ public void onHeaderDecoded(ImageDecoder decoder, ImageInfo info, Source source) {
+ // One last-ditch check to see if we've been canceled.
+ if (signal != null) signal.throwIfCanceled();
+
+ // We don't know how clients will use the decoded data, so we have
+ // to default to the more flexible "software" option.
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+
+ // We requested a rough thumbnail size, but the remote size may have
+ // returned something giant, so defensively scale down as needed.
+ final int widthSample = info.getSize().getWidth() / size.getWidth();
+ final int heightSample = info.getSize().getHeight() / size.getHeight();
+ final int sample = Math.max(widthSample, heightSample);
+ if (sample > 1) {
+ decoder.setTargetSampleSize(sample);
+ }
+ }
+ }
/**
- * Constant used to indicate the dimension of micro thumbnail.
- * @hide Only used by media framework and media provider internally.
+ * Create a thumbnail for given audio file.
+ *
+ * @param filePath The audio file.
+ * @param kind The desired thumbnail kind, such as
+ * {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND}.
*/
- @UnsupportedAppUsage
- public static final int TARGET_SIZE_MICRO_THUMBNAIL = 96;
+ @Deprecated
+ public static @Nullable Bitmap createAudioThumbnail(@NonNull String filePath, int kind) {
+ try {
+ return createAudioThumbnail(new File(filePath), convertKind(kind), null);
+ } catch (IOException e) {
+ Log.w(TAG, e);
+ return null;
+ }
+ }
/**
- * This method first examines if the thumbnail embedded in EXIF is bigger than our target
- * size. If not, then it'll create a thumbnail from original image. Due to efficiency
- * consideration, we want to let MediaThumbRequest avoid calling this method twice for
- * both kinds, so it only requests for MICRO_KIND and set saveImage to true.
+ * Create a thumbnail for given audio file.
*
- * This method always returns a "square thumbnail" for MICRO_KIND thumbnail.
- *
- * @param filePath the path of image file
- * @param kind could be MINI_KIND or MICRO_KIND
- * @return Bitmap, or null on failures
- *
- * @hide This method is only used by media framework and media provider internally.
+ * @param file The audio file.
+ * @param size The desired thumbnail size.
+ * @throws IOException If any trouble was encountered while generating or
+ * loading the thumbnail, or if
+ * {@link CancellationSignal#cancel()} was invoked.
*/
- @UnsupportedAppUsage
- public static Bitmap createImageThumbnail(String filePath, int kind) {
- boolean wantMini = (kind == Images.Thumbnails.MINI_KIND);
- int targetSize = wantMini
- ? TARGET_SIZE_MINI_THUMBNAIL
- : TARGET_SIZE_MICRO_THUMBNAIL;
- int maxPixels = wantMini
- ? MAX_NUM_PIXELS_THUMBNAIL
- : MAX_NUM_PIXELS_MICRO_THUMBNAIL;
- SizedThumbnailBitmap sizedThumbnailBitmap = new SizedThumbnailBitmap();
- Bitmap bitmap = null;
- String mimeType = MediaFile.getMimeTypeForFile(filePath);
+ public static @NonNull Bitmap createAudioThumbnail(@NonNull File file, @NonNull Size size,
+ @Nullable CancellationSignal signal) throws IOException {
+ // Checkpoint before going deeper
+ if (signal != null) signal.throwIfCanceled();
+
+ final Resizer resizer = new Resizer(size, signal);
+ try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) {
+ retriever.setDataSource(file.getAbsolutePath());
+ final byte[] raw = retriever.getEmbeddedPicture();
+ if (raw != null) {
+ return ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer);
+ }
+ } catch (RuntimeException e) {
+ throw new IOException("Failed to create thumbnail", e);
+ }
+
+ // Only poke around for files on external storage
+ if (MEDIA_UNKNOWN.equals(Environment.getExternalStorageState(file))) {
+ throw new IOException("No embedded album art found");
+ }
+
+ // Ignore "Downloads" or top-level directories
+ final File parent = file.getParentFile();
+ final File grandParent = parent != null ? parent.getParentFile() : null;
+ if (parent != null
+ && parent.getName().equals(Environment.DIRECTORY_DOWNLOADS)) {
+ throw new IOException("No thumbnails in Downloads directories");
+ }
+ if (grandParent != null
+ && MEDIA_UNKNOWN.equals(Environment.getExternalStorageState(grandParent))) {
+ throw new IOException("No thumbnails in top-level directories");
+ }
+
+ // If no embedded image found, look around for best standalone file
+ final File[] found = ArrayUtils
+ .defeatNullable(file.getParentFile().listFiles((dir, name) -> {
+ final String lower = name.toLowerCase();
+ return (lower.endsWith(".jpg") || lower.endsWith(".png"));
+ }));
+
+ final ToIntFunction<File> score = (f) -> {
+ final String lower = f.getName().toLowerCase();
+ if (lower.equals("albumart.jpg")) return 4;
+ if (lower.startsWith("albumart") && lower.endsWith(".jpg")) return 3;
+ if (lower.contains("albumart") && lower.endsWith(".jpg")) return 2;
+ if (lower.endsWith(".jpg")) return 1;
+ return 0;
+ };
+ final Comparator<File> bestScore = (a, b) -> {
+ return score.applyAsInt(a) - score.applyAsInt(b);
+ };
+
+ final File bestFile = Arrays.asList(found).stream().max(bestScore).orElse(null);
+ if (bestFile == null) {
+ throw new IOException("No album art found");
+ }
+
+ // Checkpoint before going deeper
+ if (signal != null) signal.throwIfCanceled();
+
+ return ImageDecoder.decodeBitmap(ImageDecoder.createSource(bestFile), resizer);
+ }
+
+ /**
+ * Create a thumbnail for given image file.
+ *
+ * @param filePath The image file.
+ * @param kind The desired thumbnail kind, such as
+ * {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND}.
+ */
+ @Deprecated
+ public static @Nullable Bitmap createImageThumbnail(@NonNull String filePath, int kind) {
+ try {
+ return createImageThumbnail(new File(filePath), convertKind(kind), null);
+ } catch (IOException e) {
+ Log.w(TAG, e);
+ return null;
+ }
+ }
+
+ /**
+ * Create a thumbnail for given image file.
+ *
+ * @param file The audio file.
+ * @param size The desired thumbnail size.
+ * @throws IOException If any trouble was encountered while generating or
+ * loading the thumbnail, or if
+ * {@link CancellationSignal#cancel()} was invoked.
+ */
+ public static @NonNull Bitmap createImageThumbnail(@NonNull File file, @NonNull Size size,
+ @Nullable CancellationSignal signal) throws IOException {
+ // Checkpoint before going deeper
+ if (signal != null) signal.throwIfCanceled();
+
+ final Resizer resizer = new Resizer(size, signal);
+ final String mimeType = MediaFile.getMimeTypeForFile(file.getName());
if (mimeType.equals("image/heif")
|| mimeType.equals("image/heif-sequence")
|| mimeType.equals("image/heic")
|| mimeType.equals("image/heic-sequence")) {
- bitmap = createThumbnailFromMetadataRetriever(filePath, targetSize, maxPixels);
- } else if (MediaFile.isExifMimeType(mimeType)) {
- createThumbnailFromEXIF(filePath, targetSize, maxPixels, sizedThumbnailBitmap);
- bitmap = sizedThumbnailBitmap.mBitmap;
- }
-
- if (bitmap == null) {
- FileInputStream stream = null;
- try {
- stream = new FileInputStream(filePath);
- FileDescriptor fd = stream.getFD();
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inSampleSize = 1;
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeFileDescriptor(fd, null, options);
- if (options.mCancel || options.outWidth == -1
- || options.outHeight == -1) {
- return null;
- }
- options.inSampleSize = computeSampleSize(
- options, targetSize, maxPixels);
- options.inJustDecodeBounds = false;
-
- options.inDither = false;
- options.inPreferredConfig = Bitmap.Config.ARGB_8888;
- bitmap = BitmapFactory.decodeFileDescriptor(fd, null, options);
- } catch (IOException ex) {
- Log.e(TAG, "", ex);
- } catch (OutOfMemoryError oom) {
- Log.e(TAG, "Unable to decode file " + filePath + ". OutOfMemoryError.", oom);
- } finally {
- try {
- if (stream != null) {
- stream.close();
- }
- } catch (IOException ex) {
- Log.e(TAG, "", ex);
- }
+ try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) {
+ retriever.setDataSource(file.getAbsolutePath());
+ return retriever.getThumbnailImageAtIndex(-1,
+ new MediaMetadataRetriever.BitmapParams(), size.getWidth(),
+ size.getWidth() * size.getHeight());
+ } catch (RuntimeException e) {
+ throw new IOException("Failed to create thumbnail", e);
}
-
+ } else if (MediaFile.isExifMimeType(mimeType)) {
+ final ExifInterface exif = new ExifInterface(file);
+ final byte[] raw = exif.getThumbnailBytes();
+ if (raw != null) {
+ return ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer);
+ }
}
- if (kind == Images.Thumbnails.MICRO_KIND) {
- // now we make it a "square thumbnail" for MICRO_KIND thumbnail
- bitmap = extractThumbnail(bitmap,
- TARGET_SIZE_MICRO_THUMBNAIL,
- TARGET_SIZE_MICRO_THUMBNAIL, OPTIONS_RECYCLE_INPUT);
- }
- return bitmap;
+ // Checkpoint before going deeper
+ if (signal != null) signal.throwIfCanceled();
+
+ return ImageDecoder.decodeBitmap(ImageDecoder.createSource(file), resizer);
}
/**
- * Create a video thumbnail for a video. May return null if the video is
- * corrupt or the format is not supported.
+ * Create a thumbnail for given video file.
*
- * @param filePath the path of video file
- * @param kind could be MINI_KIND or MICRO_KIND
+ * @param filePath The video file.
+ * @param kind The desired thumbnail kind, such as
+ * {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND}.
*/
- public static Bitmap createVideoThumbnail(String filePath, int kind) {
- Bitmap bitmap = null;
- MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+ @Deprecated
+ public static @Nullable Bitmap createVideoThumbnail(@NonNull String filePath, int kind) {
try {
- retriever.setDataSource(filePath);
- // First retrieve album art in metadata if set.
- byte[] embeddedPicture = retriever.getEmbeddedPicture();
- if (embeddedPicture != null && embeddedPicture.length > 0) {
- bitmap = BitmapFactory.decodeByteArray(embeddedPicture, 0, embeddedPicture.length);
- }
- // Fall back to first frame of the video.
- if (bitmap == null) {
- bitmap = retriever.getFrameAtTime(-1);
- }
- } catch (IllegalArgumentException ex) {
- // Assume this is a corrupt video file
- } catch (RuntimeException ex) {
- // Assume this is a corrupt video file.
- } finally {
- try {
- retriever.release();
- } catch (RuntimeException ex) {
- // Ignore failures while cleaning up.
- }
+ return createVideoThumbnail(new File(filePath), convertKind(kind), null);
+ } catch (IOException e) {
+ Log.w(TAG, e);
+ return null;
}
+ }
- if (bitmap == null) return null;
+ /**
+ * Create a thumbnail for given video file.
+ *
+ * @param file The video file.
+ * @param size The desired thumbnail size.
+ * @throws IOException If any trouble was encountered while generating or
+ * loading the thumbnail, or if
+ * {@link CancellationSignal#cancel()} was invoked.
+ */
+ public static @NonNull Bitmap createVideoThumbnail(@NonNull File file, @NonNull Size size,
+ @Nullable CancellationSignal signal) throws IOException {
+ // Checkpoint before going deeper
+ if (signal != null) signal.throwIfCanceled();
- if (kind == Images.Thumbnails.MINI_KIND) {
- // Scale down the bitmap if it's too large.
- int width = bitmap.getWidth();
- int height = bitmap.getHeight();
- int max = Math.max(width, height);
- if (max > 512) {
- float scale = 512f / max;
- int w = Math.round(scale * width);
- int h = Math.round(scale * height);
- bitmap = Bitmap.createScaledBitmap(bitmap, w, h, true);
+ final Resizer resizer = new Resizer(size, signal);
+ try (MediaMetadataRetriever mmr = new MediaMetadataRetriever()) {
+ mmr.setDataSource(file.getAbsolutePath());
+
+ // Try to retrieve thumbnail from metadata
+ final byte[] raw = mmr.getEmbeddedPicture();
+ if (raw != null) {
+ return ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer);
}
- } else if (kind == Images.Thumbnails.MICRO_KIND) {
- bitmap = extractThumbnail(bitmap,
- TARGET_SIZE_MICRO_THUMBNAIL,
- TARGET_SIZE_MICRO_THUMBNAIL,
- OPTIONS_RECYCLE_INPUT);
+
+ // Fall back to middle of video
+ final int width = Integer.parseInt(mmr.extractMetadata(METADATA_KEY_VIDEO_WIDTH));
+ final int height = Integer.parseInt(mmr.extractMetadata(METADATA_KEY_VIDEO_HEIGHT));
+ final long duration = Long.parseLong(mmr.extractMetadata(METADATA_KEY_DURATION));
+
+ // If we're okay with something larger than native format, just
+ // return a frame without up-scaling it
+ if (size.getWidth() > width && size.getHeight() > height) {
+ return mmr.getFrameAtTime(duration / 2, OPTION_CLOSEST_SYNC);
+ } else {
+ return mmr.getScaledFrameAtTime(duration / 2, OPTION_CLOSEST_SYNC,
+ size.getWidth(), size.getHeight());
+ }
+ } catch (RuntimeException e) {
+ throw new IOException("Failed to create thumbnail", e);
}
- return bitmap;
}
/**
@@ -242,122 +361,27 @@
return thumbnail;
}
- /*
- * Compute the sample size as a function of minSideLength
- * and maxNumOfPixels.
- * minSideLength is used to specify that minimal width or height of a
- * bitmap.
- * maxNumOfPixels is used to specify the maximal size in pixels that is
- * tolerable in terms of memory usage.
- *
- * The function returns a sample size based on the constraints.
- * Both size and minSideLength can be passed in as IImage.UNCONSTRAINED,
- * which indicates no care of the corresponding constraint.
- * The functions prefers returning a sample size that
- * generates a smaller bitmap, unless minSideLength = IImage.UNCONSTRAINED.
- *
- * Also, the function rounds up the sample size to a power of 2 or multiple
- * of 8 because BitmapFactory only honors sample size this way.
- * For example, BitmapFactory downsamples an image by 2 even though the
- * request is 3. So we round up the sample size to avoid OOM.
- */
+ @Deprecated
@UnsupportedAppUsage
private static int computeSampleSize(BitmapFactory.Options options,
int minSideLength, int maxNumOfPixels) {
- int initialSize = computeInitialSampleSize(options, minSideLength,
- maxNumOfPixels);
-
- int roundedSize;
- if (initialSize <= 8 ) {
- roundedSize = 1;
- while (roundedSize < initialSize) {
- roundedSize <<= 1;
- }
- } else {
- roundedSize = (initialSize + 7) / 8 * 8;
- }
-
- return roundedSize;
+ return 1;
}
+ @Deprecated
@UnsupportedAppUsage
private static int computeInitialSampleSize(BitmapFactory.Options options,
int minSideLength, int maxNumOfPixels) {
- double w = options.outWidth;
- double h = options.outHeight;
-
- int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 :
- (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
- int upperBound = (minSideLength == UNCONSTRAINED) ? 128 :
- (int) Math.min(Math.floor(w / minSideLength),
- Math.floor(h / minSideLength));
-
- if (upperBound < lowerBound) {
- // return the larger one when there is no overlapping zone.
- return lowerBound;
- }
-
- if ((maxNumOfPixels == UNCONSTRAINED) &&
- (minSideLength == UNCONSTRAINED)) {
- return 1;
- } else if (minSideLength == UNCONSTRAINED) {
- return lowerBound;
- } else {
- return upperBound;
- }
+ return 1;
}
- /**
- * Make a bitmap from a given Uri, minimal side length, and maximum number of pixels.
- * The image data will be read from specified pfd if it's not null, otherwise
- * a new input stream will be created using specified ContentResolver.
- *
- * Clients are allowed to pass their own BitmapFactory.Options used for bitmap decoding. A
- * new BitmapFactory.Options will be created if options is null.
- */
- private static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels,
- Uri uri, ContentResolver cr, ParcelFileDescriptor pfd,
- BitmapFactory.Options options) {
- Bitmap b = null;
- try {
- if (pfd == null) pfd = makeInputStream(uri, cr);
- if (pfd == null) return null;
- if (options == null) options = new BitmapFactory.Options();
-
- FileDescriptor fd = pfd.getFileDescriptor();
- options.inSampleSize = 1;
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeFileDescriptor(fd, null, options);
- if (options.mCancel || options.outWidth == -1
- || options.outHeight == -1) {
- return null;
- }
- options.inSampleSize = computeSampleSize(
- options, minSideLength, maxNumOfPixels);
- options.inJustDecodeBounds = false;
-
- options.inDither = false;
- options.inPreferredConfig = Bitmap.Config.ARGB_8888;
- b = BitmapFactory.decodeFileDescriptor(fd, null, options);
- } catch (OutOfMemoryError ex) {
- Log.e(TAG, "Got oom exception ", ex);
- return null;
- } finally {
- closeSilently(pfd);
- }
- return b;
- }
-
+ @Deprecated
@UnsupportedAppUsage
private static void closeSilently(ParcelFileDescriptor c) {
- if (c == null) return;
- try {
- c.close();
- } catch (Throwable t) {
- // do nothing
- }
+ IoUtils.closeQuietly(c);
}
+ @Deprecated
@UnsupportedAppUsage
private static ParcelFileDescriptor makeInputStream(
Uri uri, ContentResolver cr) {
@@ -371,6 +395,7 @@
/**
* Transform source Bitmap to targeted width and height.
*/
+ @Deprecated
@UnsupportedAppUsage
private static Bitmap transform(Matrix scaler,
Bitmap source,
@@ -468,14 +493,7 @@
return b2;
}
- /**
- * SizedThumbnailBitmap contains the bitmap, which is downsampled either from
- * the thumbnail in exif or the full image.
- * mThumbnailData, mThumbnailWidth and mThumbnailHeight are set together only if mThumbnail
- * is not null.
- *
- * The width/height of the sized bitmap may be different from mThumbnailWidth/mThumbnailHeight.
- */
+ @Deprecated
private static class SizedThumbnailBitmap {
public byte[] mThumbnailData;
public Bitmap mBitmap;
@@ -483,81 +501,9 @@
public int mThumbnailHeight;
}
- /**
- * Creates a bitmap by either downsampling from the thumbnail in EXIF or the full image.
- * The functions returns a SizedThumbnailBitmap,
- * which contains a downsampled bitmap and the thumbnail data in EXIF if exists.
- */
+ @Deprecated
@UnsupportedAppUsage
private static void createThumbnailFromEXIF(String filePath, int targetSize,
int maxPixels, SizedThumbnailBitmap sizedThumbBitmap) {
- if (filePath == null) return;
-
- ExifInterface exif = null;
- byte [] thumbData = null;
- try {
- exif = new ExifInterface(filePath);
- thumbData = exif.getThumbnail();
- } catch (IOException ex) {
- Log.w(TAG, ex);
- }
-
- BitmapFactory.Options fullOptions = new BitmapFactory.Options();
- BitmapFactory.Options exifOptions = new BitmapFactory.Options();
- int exifThumbWidth = 0;
- int fullThumbWidth = 0;
-
- // Compute exifThumbWidth.
- if (thumbData != null) {
- exifOptions.inJustDecodeBounds = true;
- BitmapFactory.decodeByteArray(thumbData, 0, thumbData.length, exifOptions);
- exifOptions.inSampleSize = computeSampleSize(exifOptions, targetSize, maxPixels);
- exifThumbWidth = exifOptions.outWidth / exifOptions.inSampleSize;
- }
-
- // Compute fullThumbWidth.
- fullOptions.inJustDecodeBounds = true;
- BitmapFactory.decodeFile(filePath, fullOptions);
- fullOptions.inSampleSize = computeSampleSize(fullOptions, targetSize, maxPixels);
- fullThumbWidth = fullOptions.outWidth / fullOptions.inSampleSize;
-
- // Choose the larger thumbnail as the returning sizedThumbBitmap.
- if (thumbData != null && exifThumbWidth >= fullThumbWidth) {
- int width = exifOptions.outWidth;
- int height = exifOptions.outHeight;
- exifOptions.inJustDecodeBounds = false;
- sizedThumbBitmap.mBitmap = BitmapFactory.decodeByteArray(thumbData, 0,
- thumbData.length, exifOptions);
- if (sizedThumbBitmap.mBitmap != null) {
- sizedThumbBitmap.mThumbnailData = thumbData;
- sizedThumbBitmap.mThumbnailWidth = width;
- sizedThumbBitmap.mThumbnailHeight = height;
- }
- } else {
- fullOptions.inJustDecodeBounds = false;
- sizedThumbBitmap.mBitmap = BitmapFactory.decodeFile(filePath, fullOptions);
- }
- }
-
- private static Bitmap createThumbnailFromMetadataRetriever(
- String filePath, int targetSize, int maxPixels) {
- if (filePath == null) {
- return null;
- }
- Bitmap thumbnail = null;
- MediaMetadataRetriever retriever = new MediaMetadataRetriever();
- try {
- retriever.setDataSource(filePath);
- MediaMetadataRetriever.BitmapParams params = new MediaMetadataRetriever.BitmapParams();
- params.setPreferredConfig(Bitmap.Config.ARGB_8888);
- thumbnail = retriever.getThumbnailImageAtIndex(-1, params, targetSize, maxPixels);
- } catch (RuntimeException ex) {
- // Assume this is a corrupt video file.
- } finally {
- if (retriever != null) {
- retriever.release();
- }
- }
- return thumbnail;
}
}
diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml
index 34bc4eb..0be71e6 100644
--- a/packages/CompanionDeviceManager/AndroidManifest.xml
+++ b/packages/CompanionDeviceManager/AndroidManifest.xml
@@ -26,6 +26,7 @@
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
diff --git a/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java b/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java
index ab718021..81a63dd 100644
--- a/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java
+++ b/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java
@@ -17,14 +17,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.database.Cursor;
import android.database.CursorWindow;
-import android.net.Uri;
import android.os.Bundle;
import android.service.sms.FinancialSmsService;
-import android.util.Log;
-import java.util.ArrayList;
/**
* Service to provide financial apps access to sms messages.
*/
@@ -36,45 +32,6 @@
@Nullable
@Override
public CursorWindow onGetSmsMessages(@NonNull Bundle params) {
- ArrayList<String> columnNames = params.getStringArrayList(KEY_COLUMN_NAMES);
- if (columnNames == null || columnNames.size() <= 0) {
- return null;
- }
-
- Uri inbox = Uri.parse("content://sms/inbox");
-
- try (Cursor cursor = getContentResolver().query(inbox, null, null, null, null);
- CursorWindow window = new CursorWindow("FinancialSmsMessages")) {
- int messageCount = cursor.getCount();
- if (messageCount > 0 && cursor.moveToFirst()) {
- window.setNumColumns(columnNames.size());
- for (int row = 0; row < messageCount; row++) {
- if (!window.allocRow()) {
- Log.e(TAG, "CursorWindow ran out of memory.");
- return null;
- }
- for (int col = 0; col < columnNames.size(); col++) {
- String columnName = columnNames.get(col);
- int inboxColumnIndex = cursor.getColumnIndexOrThrow(columnName);
- String inboxColumnValue = cursor.getString(inboxColumnIndex);
- boolean addedToCursorWindow = window.putString(inboxColumnValue, row, col);
- if (!addedToCursorWindow) {
- Log.e(TAG, "Failed to add:"
- + inboxColumnValue
- + ";column:"
- + columnName);
- return null;
- }
- }
- cursor.moveToNext();
- }
- } else {
- Log.w(TAG, "No sms messages.");
- }
- return window;
- } catch (Exception e) {
- Log.e(TAG, "Failed to get sms messages.");
- return null;
- }
+ return null;
}
}
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 042808a0..2321790 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -19,6 +19,7 @@
"SettingsLibLayoutPreference",
"SettingsLibActionButtonsPreference",
"SettingsLibEntityHeaderWidgets",
+ "SettingsLibBarChartPreference"
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/BarChartPreference/Android.bp b/packages/SettingsLib/BarChartPreference/Android.bp
new file mode 100644
index 0000000..477e897
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/Android.bp
@@ -0,0 +1,13 @@
+android_library {
+ name: "SettingsLibBarChartPreference",
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+
+ static_libs: [
+ "androidx.preference_preference",
+ ],
+
+ sdk_version: "system_current",
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/BarChartPreference/AndroidManifest.xml b/packages/SettingsLib/BarChartPreference/AndroidManifest.xml
new file mode 100644
index 0000000..4b9f1ab
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.widget">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_chart.xml b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_chart.xml
new file mode 100644
index 0000000..1a4d7b7
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_chart.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="16dp"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/bar_chart_title"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:gravity="center"
+ android:textAppearance="@style/BarChart.Text.HeaderTitle"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center|bottom">
+
+ <com.android.settingslib.widget.BarView
+ android:id="@+id/bar_view1"
+ style="@style/BarViewStyle"
+ settings:barColor="#FA7B17"/>
+ <com.android.settingslib.widget.BarView
+ android:id="@+id/bar_view2"
+ style="@style/BarViewStyle"
+ settings:barColor="#F439A0"/>
+ <com.android.settingslib.widget.BarView
+ android:id="@+id/bar_view3"
+ style="@style/BarViewStyle"
+ settings:barColor="#A142F4"/>
+ <com.android.settingslib.widget.BarView
+ android:id="@+id/bar_view4"
+ style="@style/BarViewStyle"
+ settings:barColor="#24C1E0"/>
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/bar_chart_details"
+ style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:gravity="center"/>
+
+</LinearLayout>
diff --git a/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_view.xml b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_view.xml
new file mode 100644
index 0000000..b053317
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_view.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <View
+ android:id="@+id/bar_view"
+ android:layout_width="8dp"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/colorAccent"/>
+
+ <ImageView
+ android:id="@+id/icon_view"
+ android:layout_width="@dimen/settings_bar_view_icon_size"
+ android:layout_height="@dimen/settings_bar_view_icon_size"
+ android:scaleType="fitCenter"
+ android:layout_marginTop="12dp"
+ android:layout_marginBottom="12dp"/>
+
+ <TextView
+ android:id="@+id/bar_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="2dp"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="@style/BarChart.Text.Title"/>
+
+ <TextView
+ android:id="@+id/bar_summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="12dp"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="@style/BarChart.Text.Summary"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/BarChartPreference/res/values/attrs.xml b/packages/SettingsLib/BarChartPreference/res/values/attrs.xml
new file mode 100644
index 0000000..df3eb0a
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/res/values/attrs.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+
+ <declare-styleable name="SettingsBarView">
+ <!-- The color of bar view -->
+ <attr name="barColor" format="color" />
+ </declare-styleable>
+
+</resources>
diff --git a/packages/SettingsLib/BarChartPreference/res/values/dimens.xml b/packages/SettingsLib/BarChartPreference/res/values/dimens.xml
new file mode 100644
index 0000000..7148afa
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <dimen name="settings_bar_view_max_height">106dp</dimen>
+ <dimen name="settings_bar_view_icon_size">24dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/BarChartPreference/res/values/styles.xml b/packages/SettingsLib/BarChartPreference/res/values/styles.xml
new file mode 100644
index 0000000..647d080
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/res/values/styles.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <style name="BarViewStyle">
+ <item name="android:layout_width">0dp</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_weight">1</item>
+ <item name="android:layout_marginStart">8dp</item>
+ <item name="android:layout_marginEnd">8dp</item>
+ </style>
+
+ <style name="BarChart.Text"
+ parent="@android:style/TextAppearance.Material.Subhead">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+
+ <style name="BarChart.Text.HeaderTitle">
+ <item name="android:textSize">14sp</item>
+ </style>
+
+ <style name="BarChart.Text.Title">
+ <item name="android:textSize">22sp</item>
+ </style>
+
+ <style name="BarChart.Text.Summary"
+ parent="@android:style/TextAppearance.Material.Body1">
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textSize">14sp</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
new file mode 100644
index 0000000..89ebf4d
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+import java.util.Arrays;
+
+/**
+ * This BarChartPreference shows four bar views in this preference at most.
+ *
+ * <p>The following code sample shows a typical use, with an XML layout and code to initialize the
+ * contents of the BarChartPreference:
+ *
+ * <pre>
+ * <com.android.settingslib.widget.BarChartPreference
+ * android:key="bar_chart"/>
+ * </pre>
+ *
+ * <p>This code sample demonstrates how to initialize the contents of the BarChartPreference defined
+ * in the previous XML layout:
+ *
+ * <pre>
+ * BarViewInfo[] viewsInfo = new BarViewInfo [] {
+ * new BarViewInfo(icon, 18, res of summary),
+ * new BarViewInfo(icon, 25, res of summary),
+ * new BarViewInfo(icon, 10, res of summary),
+ * new BarViewInfo(icon, 3, res of summary),
+ * };
+ * </pre>
+ *
+ * <pre>
+ * BarChartPreference preference = ((BarChartPreference) findPreference("bar_chart"));
+ *
+ * preference.setBarChartTitleRes(R.string.title_res);
+ * preference.setBarChartDetailsRes(R.string.details_res);
+ * preference.setBarChartDetailsClickListener(v -> doSomething());
+ * preference.setAllBarViewsData(viewsInfo);
+ * </pre>
+ */
+public class BarChartPreference extends Preference {
+
+ private static final String TAG = "BarChartPreference";
+ private static final int MAXIMUM_BAR_VIEWS = 4;
+ private static final int[] BAR_VIEWS = {
+ R.id.bar_view1,
+ R.id.bar_view2,
+ R.id.bar_view3,
+ R.id.bar_view4
+ };
+
+ private int mMaxBarHeight;
+ private @StringRes int mTitleId;
+ private @StringRes int mDetailsId;
+ private BarViewInfo[] mBarViewsInfo;
+ private View.OnClickListener mDetailsOnClickListener;
+
+ /**
+ * Constructs a new BarChartPreference with the given context's theme.
+ * It sets a layout with settings bar chart style
+ *
+ * @param context The Context the view is running in, through which it can
+ * access the current theme, resources, etc.
+ */
+ public BarChartPreference(Context context) {
+ super(context);
+ init();
+ }
+
+ /**
+ * Constructs a new BarChartPreference with the given context's theme and the supplied
+ * attribute set.
+ * It sets a layout with settings bar chart style
+ *
+ * @param context the Context the view is running in
+ * @param attrs the attributes of the XML tag that is inflating the view.
+ */
+ public BarChartPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ /**
+ * Constructs a new BarChartPreference with the given context's theme, the supplied
+ * attribute set, and default style attribute.
+ * It sets a layout with settings bar chart style
+ *
+ * @param context The Context the view is running in, through which it can
+ * access the current theme, resources, etc.
+ * @param attrs The attributes of the XML tag that is inflating the view.
+ * @param defStyleAttr An attribute in the current theme that contains a
+ * reference to a style resource that supplies default
+ * values for the view. Can be 0 to not look for
+ * defaults.
+ */
+ public BarChartPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ /**
+ * Constructs a new BarChartPreference with the given context's theme, the supplied
+ * attribute set, and default styles.
+ * It sets a layout with settings bar chart style
+ *
+ * @param context The Context the view is running in, through which it can
+ * access the current theme, resources, etc.
+ * @param attrs The attributes of the XML tag that is inflating the view.
+ * @param defStyleAttr An attribute in the current theme that contains a
+ * reference to a style resource that supplies default
+ * values for the view. Can be 0 to not look for
+ * defaults.
+ * @param defStyleRes A resource identifier of a style resource that
+ * supplies default values for the view, used only if
+ * defStyleAttr is 0 or can not be found in the theme.
+ * Can be 0 to not look for defaults.
+ */
+ public BarChartPreference(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init();
+ }
+
+ /**
+ * Set the text resource for bar chart title.
+ */
+ public void setBarChartTitle(@StringRes int resId) {
+ mTitleId = resId;
+ notifyChanged();
+ }
+
+ /**
+ * Set the text resource for bar chart details.
+ */
+ public void setBarChartDetails(@StringRes int resId) {
+ mDetailsId = resId;
+ notifyChanged();
+ }
+
+ /**
+ * Register a callback to be invoked when bar chart details view is clicked.
+ */
+ public void setBarChartDetailsClickListener(@Nullable View.OnClickListener clickListener) {
+ mDetailsOnClickListener = clickListener;
+ notifyChanged();
+ }
+
+ /**
+ * Set all bar view information which you'd like to show in preference.
+ *
+ * <p>This method helps you do a sort by {@linkBarViewInfo#mBarNumber} in descending order.
+ *
+ * @param barViewsInfo the barViewsInfo contain at least one {@link BarViewInfo}.
+ */
+ public void setAllBarViewsInfo(@NonNull BarViewInfo[] barViewsInfo) {
+ mBarViewsInfo = barViewsInfo;
+ // Do a sort in descending order, the first element would have max {@link
+ // BarViewInfo#mBarNumber}
+ Arrays.sort(mBarViewsInfo);
+ caculateAllBarViewsHeight();
+ notifyChanged();
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+ holder.setDividerAllowedAbove(true);
+ holder.setDividerAllowedBelow(true);
+
+ bindChartTitleView(holder);
+ bindChartDetailsView(holder);
+ updateBarChart(holder);
+ }
+
+ private void init() {
+ setSelectable(false);
+ setLayoutResource(R.layout.settings_bar_chart);
+ mMaxBarHeight = getContext().getResources().getDimensionPixelSize(
+ R.dimen.settings_bar_view_max_height);
+ }
+
+ private void bindChartTitleView(PreferenceViewHolder holder) {
+ final TextView titleView = (TextView) holder.findViewById(R.id.bar_chart_title);
+ titleView.setText(mTitleId);
+ }
+
+ private void bindChartDetailsView(PreferenceViewHolder holder) {
+ final Button detailsView = (Button) holder.findViewById(R.id.bar_chart_details);
+ detailsView.setText(mDetailsId);
+ detailsView.setOnClickListener(mDetailsOnClickListener);
+ }
+
+ private void updateBarChart(PreferenceViewHolder holder) {
+ for (int index = 0; index < MAXIMUM_BAR_VIEWS; index++) {
+ final BarView barView = (BarView) holder.findViewById(BAR_VIEWS[index]);
+
+ // If there is no bar views data can be shown.
+ if (mBarViewsInfo == null || index >= mBarViewsInfo.length) {
+ barView.setVisibility(View.GONE);
+ continue;
+ }
+ barView.setVisibility(View.VISIBLE);
+ barView.updateBarViewUI(mBarViewsInfo[index]);
+ }
+ }
+
+ private void caculateAllBarViewsHeight() {
+ // Since we sorted this array in advance, the first element must have the max {@link
+ // BarViewInfo#mBarNumber}.
+ final int maxBarViewNumber = mBarViewsInfo[0].getBarNumber();
+ // If the max number of bar view is zero, then we don't caculate the unit for bar height.
+ final int unit = maxBarViewNumber == 0 ? 0 : mMaxBarHeight / maxBarViewNumber;
+
+ for (BarViewInfo barView : mBarViewsInfo) {
+ barView.setBarHeight(barView.getBarNumber() * unit);
+ }
+ }
+}
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
new file mode 100644
index 0000000..6243a2d
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * A extension view for bar chart.
+ */
+public class BarView extends LinearLayout {
+
+ private static final String TAG = "BarView";
+
+ private View mBarView;
+ private ImageView mIcon;
+ private TextView mBarTitle;
+ private TextView mBarSummary;
+
+ /**
+ * Constructs a new BarView with the given context's theme.
+ *
+ * @param context The Context the view is running in, through which it can
+ * access the current theme, resources, etc.
+ */
+ public BarView(Context context) {
+ super(context);
+ init();
+ }
+
+ /**
+ * Constructs a new BarView with the given context's theme and the supplied
+ * attribute set.
+ *
+ * @param context the Context the view is running in
+ * @param attrs the attributes of the XML tag that is inflating the view.
+ */
+ public BarView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+
+ // Get accent color
+ TypedArray a = context.obtainStyledAttributes(new int[]{android.R.attr.colorAccent});
+ @ColorInt final int colorAccent = a.getColor(0, 0);
+
+ // Get bar color from layout XML
+ a = context.obtainStyledAttributes(attrs, R.styleable.SettingsBarView);
+ @ColorInt final int barColor = a.getColor(R.styleable.SettingsBarView_barColor,
+ colorAccent);
+ a.recycle();
+
+ mBarView.setBackgroundColor(barColor);
+ }
+
+ /**
+ * This helps update the bar view UI with a {@link BarViewInfo}.
+ *
+ * @param barViewInfo A {@link BarViewInfo} saves bar view status.
+ */
+ public void updateBarViewUI(BarViewInfo barViewInfo) {
+ //Set height of bar view
+ mBarView.getLayoutParams().height = barViewInfo.getBarHeight();
+ mIcon.setImageDrawable(barViewInfo.getIcon());
+ // For now, we use the bar number as title.
+ mBarTitle.setText(Integer.toString(barViewInfo.getBarNumber()));
+ mBarSummary.setText(barViewInfo.getSummaryRes());
+ }
+
+ @VisibleForTesting
+ CharSequence getTitle() {
+ return mBarTitle.getText();
+ }
+
+ @VisibleForTesting
+ CharSequence getSummary() {
+ return mBarSummary.getText();
+ }
+
+ private void init() {
+ LayoutInflater.from(getContext()).inflate(R.layout.settings_bar_view, this);
+ setOrientation(LinearLayout.VERTICAL);
+ setGravity(Gravity.CENTER);
+
+ mBarView = findViewById(R.id.bar_view);
+ mIcon = (ImageView) findViewById(R.id.icon_view);
+ mBarTitle = (TextView) findViewById(R.id.bar_title);
+ mBarSummary = (TextView) findViewById(R.id.bar_summary);
+ }
+
+ private void setOnClickListner(View.OnClickListener listener) {
+ mBarView.setOnClickListener(listener);
+ }
+}
diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java
new file mode 100644
index 0000000..aa83ce9
--- /dev/null
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+import androidx.annotation.IntRange;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+
+import java.util.Comparator;
+
+/**
+ * A class responsible for saving bar view information.
+ */
+public class BarViewInfo implements Comparable<BarViewInfo> {
+
+ private final Drawable mIcon;
+ private View.OnClickListener mListener;
+ private @StringRes int mSummaryRes;
+ // A number indicates this bar's height. The larger number shows a higher bar view.
+ private int mBarNumber;
+ // A real height of bar view.
+ private int mBarHeight;
+
+ /**
+ * Construct a BarViewInfo instance.
+ *
+ * @param icon the icon of bar view.
+ * @param barNumber the number of bar view. The larger number show a more height of bar view.
+ * @param summaryRes the resource identifier of the string resource to be displayed
+ * @return BarViewInfo object.
+ */
+ public BarViewInfo(Drawable icon, @IntRange(from = 0) int barNumber,
+ @StringRes int summaryRes) {
+ mIcon = icon;
+ mBarNumber = barNumber;
+ mSummaryRes = summaryRes;
+ }
+
+ /**
+ * Set number for bar view.
+ *
+ * @param barNumber the number of bar view. The larger number shows a higher bar view.
+ */
+ public void setBarNumber(@IntRange(from = 0) int barNumber) {
+ mBarNumber = barNumber;
+ }
+
+ /**
+ * Set summary resource for bar view
+ *
+ * @param resId the resource identifier of the string resource to be displayed
+ */
+ public void setSummary(@StringRes int resId) {
+ mSummaryRes = resId;
+ }
+
+ /**
+ * Set a click listner for bar view.
+ *
+ * @param listener the click listner is attached on bar view.
+ */
+ public void setClickListener(@Nullable View.OnClickListener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Get the icon of bar view.
+ *
+ * @return Drawable the icon of bar view.
+ */
+ public Drawable getIcon() {
+ return mIcon;
+ }
+
+ /**
+ * Get the OnClickListener of bar view.
+ *
+ * @return View.OnClickListener the click listner of bar view.
+ */
+ public View.OnClickListener getListener() {
+ return mListener;
+ }
+
+ /**
+ * Get the real height of bar view.
+ *
+ * @return the real height of bar view.
+ */
+ public int getBarHeight() {
+ return mBarHeight;
+ }
+
+ /**
+ * Get summary resource of bar view.
+ *
+ * @return summary resource of bar view.
+ */
+ public int getSummaryRes() {
+ return mSummaryRes;
+ }
+
+ /**
+ * Get the number of app uses this permisssion.
+ *
+ * @return the number of app uses this permission.
+ */
+ public int getBarNumber() {
+ return mBarNumber;
+ }
+
+ @Override
+ public int compareTo(BarViewInfo other) {
+ // Descending order
+ return Comparator.comparingInt((BarViewInfo barViewInfo) -> barViewInfo.mBarNumber)
+ .compare(other, this);
+ }
+
+ /**
+ * Set a real height for bar view.
+ *
+ * <p>This method should not be called by outside. It usually should be called by
+ * {@link BarChartPreference#caculateAllBarViewsHeight}
+ *
+ * @param barHeight the real bar height for bar view.
+ */
+ void setBarHeight(@IntRange(from = 0) int barHeight) {
+ mBarHeight = barHeight;
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
new file mode 100644
index 0000000..371c3d4
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.preference.PreferenceViewHolder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class BarChartPreferenceTest {
+
+ private Context mContext;
+ private View mBarChartView;
+ private Drawable mIcon;
+ private BarView mBarView1;
+ private BarView mBarView2;
+ private BarView mBarView3;
+ private BarView mBarView4;
+ private PreferenceViewHolder mHolder;
+ private BarChartPreference mPreference;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mBarChartView = View.inflate(mContext, R.layout.settings_bar_chart, null /* parent */);
+ mHolder = PreferenceViewHolder.createInstanceForTests(mBarChartView);
+ mPreference = new BarChartPreference(mContext, null /* attrs */);
+ mPreference.setBarChartTitle(R.string.debug_app);
+ mPreference.setBarChartDetails(R.string.debug_app);
+
+
+ mIcon = mContext.getDrawable(R.drawable.ic_menu);
+ mBarView1 = (BarView) mBarChartView.findViewById(R.id.bar_view1);
+ mBarView2 = (BarView) mBarChartView.findViewById(R.id.bar_view2);
+ mBarView3 = (BarView) mBarChartView.findViewById(R.id.bar_view3);
+ mBarView4 = (BarView) mBarChartView.findViewById(R.id.bar_view4);
+ }
+
+ @Test
+ public void setBarChartTitleRes_setTitleRes_showInBarChartTitle() {
+ final TextView titleView = (TextView) mBarChartView.findViewById(R.id.bar_chart_title);
+
+ mPreference.setBarChartTitle(R.string.debug_app);
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(titleView.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(titleView.getText()).isEqualTo(mContext.getText(R.string.debug_app));
+ }
+
+ @Test
+ public void setBarChartDetailsRes_setDetailsRes_showInBarChartDetails() {
+ final TextView detailsView = (TextView) mBarChartView.findViewById(R.id.bar_chart_details);
+
+ mPreference.setBarChartDetails(R.string.debug_app);
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(detailsView.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(detailsView.getText()).isEqualTo(mContext.getText(R.string.debug_app));
+ }
+
+ @Test
+ public void setBarChartDetailsClickListener_setClickListener_detailsViewAttachClickListener() {
+ final TextView detailsView = (TextView) mBarChartView.findViewById(R.id.bar_chart_details);
+
+ mPreference.setBarChartDetailsClickListener(v -> {
+ });
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(detailsView.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(detailsView.hasOnClickListeners()).isTrue();
+ }
+
+ @Test
+ public void setAllBarViewsInfo_setOneBarViewInfo_showOneBarView() {
+ final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+ new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app)
+ };
+
+ mPreference.setAllBarViewsInfo(barViewsInfo);
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView1.getTitle()).isEqualTo("10");
+
+ assertThat(mBarView2.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mBarView3.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mBarView4.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void setAllBarViewsInfo_setTwoBarViewsInfo_showTwoBarViews() {
+ final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+ new BarViewInfo(mIcon, 20 /* barNumber */, R.string.debug_app),
+ new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app)
+ };
+
+ mPreference.setAllBarViewsInfo(barViewsInfo);
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView1.getTitle()).isEqualTo("20");
+ assertThat(mBarView2.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView2.getTitle()).isEqualTo("10");
+
+ assertThat(mBarView3.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mBarView4.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void setAllBarViewsInfo_setThreeBarViewsInfo_showThreeBarViews() {
+ final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+ new BarViewInfo(mIcon, 20 /* barNumber */, R.string.debug_app),
+ new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app),
+ new BarViewInfo(mIcon, 5 /* barNumber */, R.string.debug_app)
+ };
+
+ mPreference.setAllBarViewsInfo(barViewsInfo);
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView1.getTitle()).isEqualTo("20");
+ assertThat(mBarView2.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView2.getTitle()).isEqualTo("10");
+ assertThat(mBarView3.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView3.getTitle()).isEqualTo("5");
+
+ assertThat(mBarView4.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void setAllBarViewsInfo_setFourBarViewsInfo_showFourBarViews() {
+ final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+ new BarViewInfo(mIcon, 20 /* barNumber */, R.string.debug_app),
+ new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app),
+ new BarViewInfo(mIcon, 5 /* barNumber */, R.string.debug_app),
+ new BarViewInfo(mIcon, 2 /* barNumber */, R.string.debug_app),
+ };
+
+ mPreference.setAllBarViewsInfo(barViewsInfo);
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView1.getTitle()).isEqualTo("20");
+ assertThat(mBarView2.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView2.getTitle()).isEqualTo("10");
+ assertThat(mBarView3.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView3.getTitle()).isEqualTo("5");
+ assertThat(mBarView4.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView4.getTitle()).isEqualTo("2");
+ }
+
+ @Test
+ public void setAllBarViewsInfo_setFourBarViewsInfo_barViewWasSortedInDescending() {
+ final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+ new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app),
+ new BarViewInfo(mIcon, 50 /* barNumber */, R.string.debug_app),
+ new BarViewInfo(mIcon, 5 /* barNumber */, R.string.debug_app),
+ new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app),
+ };
+
+ mPreference.setAllBarViewsInfo(barViewsInfo);
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView1.getTitle()).isEqualTo("50");
+ assertThat(mBarView2.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView2.getTitle()).isEqualTo("30");
+ assertThat(mBarView3.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView3.getTitle()).isEqualTo("10");
+ assertThat(mBarView4.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView4.getTitle()).isEqualTo("5");
+ }
+
+ @Test
+ public void setAllBarViewsInfo_setValidSummaryRes_barViewShouldShowSummary() {
+ final BarViewInfo[] barViewsInfo = new BarViewInfo[]{
+ new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app),
+ };
+
+ mPreference.setAllBarViewsInfo(barViewsInfo);
+ mPreference.onBindViewHolder(mHolder);
+
+ assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mBarView1.getSummary()).isEqualTo(mContext.getText(R.string.debug_app));
+ }
+}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 5fe08aa..8cfc2a6 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -27,6 +27,7 @@
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
+ <uses-permission android:name="android.permission.READ_PRECISE_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index c2e107a..ad44b9a 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -134,6 +134,7 @@
],
platform_apis: true,
+ product_specific: true,
certificate: "platform",
privileged: true,
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
index 1a18f60..6135aeb 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
@@ -62,4 +62,12 @@
* Notifies that the time zone has changed.
*/
default void onTimeZoneChanged(TimeZone timeZone) {}
+
+ /**
+ * Indicates whether the keyguard status area (date) should be shown below
+ * the clock.
+ */
+ default boolean shouldShowStatusArea() {
+ return true;
+ }
}
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 367a9ae..d52866f 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -20,19 +20,31 @@
<!-- This is a view that shows clock information in Keyguard. -->
<com.android.keyguard.KeyguardClockSwitch
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_clock_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_alignParentTop="true">
- <TextClock
- android:id="@+id/default_clock_view"
- android:layout_width="wrap_content"
+ android:layout_gravity="center_horizontal|top">
+ <FrameLayout
+ android:id="@+id/clock_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_alignParentTop="true">
+ <TextClock
+ android:id="@+id/default_clock_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:letterSpacing="0.03"
+ android:textColor="?attr/wallpaperTextColor"
+ android:singleLine="true"
+ style="@style/widget_big"
+ android:format12Hour="@string/keyguard_widget_12_hours_format"
+ android:format24Hour="@string/keyguard_widget_24_hours_format" />
+ </FrameLayout>
+ <include layout="@layout/keyguard_status_area"
+ android:id="@+id/keyguard_status_area"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:letterSpacing="0.03"
- android:textColor="?attr/wallpaperTextColor"
- android:singleLine="true"
- style="@style/widget_big"
- android:format12Hour="@string/keyguard_widget_12_hours_format"
- android:format24Hour="@string/keyguard_widget_24_hours_format" />
+ android:layout_below="@id/clock_view" />
</com.android.keyguard.KeyguardClockSwitch>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
index 7d8a1f5b..a9ba19d 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
@@ -33,21 +33,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
- <RelativeLayout
+ <include
+ layout="@layout/keyguard_clock_switch"
android:id="@+id/keyguard_clock_container"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|top">
- <include layout="@layout/keyguard_clock_switch"
- android:id="@+id/clock_view"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- <include layout="@layout/keyguard_status_area"
- android:id="@+id/keyguard_status_area"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/clock_view" />
- </RelativeLayout>
+ android:layout_height="wrap_content" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index 67ecf6f..10fea9d 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -50,21 +50,11 @@
android:textSize="13sp"
android:text="@*android:string/global_action_logout" />
- <RelativeLayout
+ <include
+ layout="@layout/keyguard_clock_switch"
android:id="@+id/keyguard_clock_container"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|top">
- <include layout="@layout/keyguard_clock_switch"
- android:id="@+id/clock_view"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- <include layout="@layout/keyguard_status_area"
- android:id="@+id/keyguard_status_area"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/clock_view" />
- </RelativeLayout>
+ android:layout_height="wrap_content" />
<TextView
android:id="@+id/owner_info"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java
index 74fd13f..01b012d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java
@@ -14,12 +14,40 @@
package com.android.systemui.shared.plugins;
+import android.annotation.IntDef;
import android.content.ComponentName;
/**
* Enables and disables plugins.
*/
public interface PluginEnabler {
- void setEnabled(ComponentName component, boolean enabled);
+
+ int ENABLED = 0;
+ int DISABLED_MANUALLY = 1;
+ int DISABLED_INVALID_VERSION = 1;
+ int DISABLED_FROM_EXPLICIT_CRASH = 2;
+ int DISABLED_FROM_SYSTEM_CRASH = 3;
+
+ @IntDef({ENABLED, DISABLED_MANUALLY, DISABLED_INVALID_VERSION, DISABLED_FROM_EXPLICIT_CRASH,
+ DISABLED_FROM_SYSTEM_CRASH})
+ @interface DisableReason {
+ }
+
+ /** Enables plugin via the PackageManager. */
+ void setEnabled(ComponentName component);
+
+ /** Disables a plugin via the PackageManager and records the reason for disabling. */
+ void setDisabled(ComponentName component, @DisableReason int reason);
+
+ /** Returns true if the plugin is enabled in the PackageManager. */
boolean isEnabled(ComponentName component);
+
+ /**
+ * Returns the reason that a plugin is disabled, (if it is).
+ *
+ * It should return {@link #ENABLED} if the plugin is turned on.
+ * It should return {@link #DISABLED_MANUALLY} if the plugin is off but the reason is unknown.
+ */
+ @DisableReason
+ int getDisableReason(ComponentName componentName);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
index 8e7fadb..523720d5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
@@ -136,7 +136,7 @@
ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
for (PluginInfo info : plugins) {
if (className.startsWith(info.mPackage)) {
- disable(info);
+ disable(info, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH);
disableAny = true;
}
}
@@ -146,12 +146,13 @@
public boolean disableAll() {
ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
for (int i = 0; i < plugins.size(); i++) {
- disable(plugins.get(i));
+ disable(plugins.get(i), PluginEnabler.DISABLED_FROM_SYSTEM_CRASH);
}
return plugins.size() != 0;
}
- private void disable(PluginInfo info) {
+ private void disable(PluginInfo info,
+ @PluginEnabler.DisableReason int reason) {
// Live by the sword, die by the sword.
// Misbehaving plugins get disabled and won't come back until uninstall/reinstall.
@@ -162,9 +163,9 @@
// Don't disable whitelisted plugins as they are a part of the OS.
return;
}
- Log.w(TAG, "Disabling plugin " + info.mPackage + "/" + info.mClass);
- mManager.getPluginEnabler().setEnabled(new ComponentName(info.mPackage, info.mClass),
- false);
+ ComponentName pluginComponent = new ComponentName(info.mPackage, info.mClass);
+ Log.w(TAG, "Disabling plugin " + pluginComponent.flattenToShortString());
+ mManager.getPluginEnabler().setDisabled(pluginComponent, reason);
}
public <T> boolean dependsOn(Plugin p, Class<T> cls) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
index dc2a9bd..10b5f1c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
@@ -184,6 +184,7 @@
mListening = true;
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(PLUGIN_CHANGED);
filter.addAction(DISABLE_PLUGIN);
@@ -214,12 +215,13 @@
// Don't disable whitelisted plugins as they are a part of the OS.
return;
}
- getPluginEnabler().setEnabled(component, false);
+ getPluginEnabler().setDisabled(component, PluginEnabler.DISABLED_INVALID_VERSION);
mContext.getSystemService(NotificationManager.class).cancel(component.getClassName(),
SystemMessage.NOTE_PLUGIN);
} else {
Uri data = intent.getData();
String pkg = data.getEncodedSchemeSpecificPart();
+ ComponentName componentName = ComponentName.unflattenFromString(pkg);
if (mOneShotPackages.contains(pkg)) {
int icon = mContext.getResources().getIdentifier("tuner", "drawable",
mContext.getPackageName());
@@ -256,6 +258,17 @@
Log.v(TAG, "Reloading " + pkg);
}
}
+ if (Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction())) {
+ @PluginEnabler.DisableReason int disableReason =
+ getPluginEnabler().getDisableReason(componentName);
+ if (disableReason == PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH
+ || disableReason == PluginEnabler.DISABLED_FROM_SYSTEM_CRASH
+ || disableReason == PluginEnabler.DISABLED_INVALID_VERSION) {
+ Log.i(TAG, "Re-enabling previously disabled plugin that has been "
+ + "updated: " + componentName.flattenToShortString());
+ getPluginEnabler().setEnabled(componentName);
+ }
+ }
if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
for (PluginInstanceManager manager : mPluginMap.values()) {
manager.onPackageChange(pkg);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 0ec9014..22a23a8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -7,6 +7,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
import android.widget.TextClock;
import androidx.annotation.VisibleForTesting;
@@ -22,7 +23,7 @@
/**
* Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
*/
-public class KeyguardClockSwitch extends FrameLayout {
+public class KeyguardClockSwitch extends RelativeLayout {
/**
* Optional/alternative clock injected via plugin.
*/
@@ -31,6 +32,15 @@
* Default clock.
*/
private TextClock mClockView;
+ /**
+ * Frame for default and custom clock.
+ */
+ private FrameLayout mClockFrame;
+ /**
+ * Status area (date and other stuff) shown below the clock. Plugin can decide whether
+ * or not to show it below the alternate clock.
+ */
+ private View mKeyguardStatusArea;
private final PluginListener<ClockPlugin> mClockPluginListener =
new PluginListener<ClockPlugin>() {
@@ -43,11 +53,14 @@
// selected clock face. In the future, the user should be able to
// pick a clock face from the available plugins.
mClockPlugin = plugin;
- addView(view, -1,
+ mClockFrame.addView(view, -1,
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
initPluginParams();
mClockView.setVisibility(View.GONE);
+ if (!plugin.shouldShowStatusArea()) {
+ mKeyguardStatusArea.setVisibility(View.GONE);
+ }
}
}
@@ -56,6 +69,7 @@
if (Objects.equals(plugin, mClockPlugin)) {
disconnectPlugin();
mClockView.setVisibility(View.VISIBLE);
+ mKeyguardStatusArea.setVisibility(View.VISIBLE);
}
}
};
@@ -72,6 +86,8 @@
protected void onFinishInflate() {
super.onFinishInflate();
mClockView = findViewById(R.id.default_clock_view);
+ mClockFrame = findViewById(R.id.clock_view);
+ mKeyguardStatusArea = findViewById(R.id.keyguard_status_area);
}
@Override
@@ -185,7 +201,7 @@
if (mClockPlugin != null) {
View view = mClockPlugin.getView();
if (view != null) {
- removeView(view);
+ mClockFrame.removeView(view);
}
mClockPlugin = null;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 1e9d288..c6f1726 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -37,7 +37,7 @@
import android.util.TypedValue;
import android.view.View;
import android.widget.GridLayout;
-import android.widget.RelativeLayout;
+import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.core.graphics.ColorUtils;
@@ -173,7 +173,7 @@
mLogoutView.setOnClickListener(this::onLogoutClicked);
}
- mClockView = findViewById(R.id.clock_view);
+ mClockView = findViewById(R.id.keyguard_clock_container);
mClockView.setShowCurrentUserTime(true);
if (KeyguardClockAccessibilityDelegate.isNeeded(mContext)) {
mClockView.setAccessibilityDelegate(new KeyguardClockAccessibilityDelegate(mContext));
@@ -205,8 +205,8 @@
* Moves clock, adjusting margins when slice content changes.
*/
private void onSliceContentChanged() {
- RelativeLayout.LayoutParams layoutParams =
- (RelativeLayout.LayoutParams) mClockView.getLayoutParams();
+ LinearLayout.LayoutParams layoutParams =
+ (LinearLayout.LayoutParams) mClockView.getLayoutParams();
layoutParams.bottomMargin = mPulsing ? mSmallClockPadding : 0;
mClockView.setLayoutParams(layoutParams);
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 4fb1bc5..974cd88 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -35,7 +35,7 @@
private static final int SIZE = Build.IS_DEBUGGABLE ? 400 : 50;
static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
- private static final int REASONS = 8;
+ private static final int REASONS = 7;
public static final int PULSE_REASON_NONE = -1;
public static final int PULSE_REASON_INTENT = 0;
@@ -182,7 +182,7 @@
*/
public static void traceLockScreenWakeUp(boolean wake) {
if (!ENABLED) return;
- log("wakeLockScreenWakeUp " + wake);
+ log("wakeLockScreen " + wake);
}
/**
@@ -191,7 +191,7 @@
*/
public static void traceWakeDisplay(boolean wake) {
if (!ENABLED) return;
- log("wakeLockScreenWakeUp " + wake);
+ log("wakeDisplay " + wake);
}
public static void traceProximityResult(Context context, boolean near, long millis,
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java
index e2417f7..6337415 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java
@@ -16,28 +16,44 @@
import android.content.ComponentName;
import android.content.Context;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.shared.plugins.PluginEnabler;
public class PluginEnablerImpl implements PluginEnabler {
+ private static final String CRASH_DISABLED_PLUGINS_PREF_FILE = "auto_disabled_plugins_prefs";
- final private PackageManager mPm;
+ private PackageManager mPm;
+ private final SharedPreferences mAutoDisabledPrefs;
public PluginEnablerImpl(Context context) {
- this(context.getPackageManager());
+ this(context, context.getPackageManager());
}
- @VisibleForTesting public PluginEnablerImpl(PackageManager pm) {
+ @VisibleForTesting public PluginEnablerImpl(Context context, PackageManager pm) {
+ mAutoDisabledPrefs = context.getSharedPreferences(
+ CRASH_DISABLED_PLUGINS_PREF_FILE, Context.MODE_PRIVATE);
mPm = pm;
}
@Override
- public void setEnabled(ComponentName component, boolean enabled) {
+ public void setEnabled(ComponentName component) {
+ setDisabled(component, ENABLED);
+ }
+
+ @Override
+ public void setDisabled(ComponentName component, @DisableReason int reason) {
+ boolean enabled = reason == ENABLED;
final int desiredState = enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
: PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
mPm.setComponentEnabledSetting(component, desiredState, PackageManager.DONT_KILL_APP);
+ if (enabled) {
+ mAutoDisabledPrefs.edit().remove(component.flattenToString()).apply();
+ } else {
+ mAutoDisabledPrefs.edit().putInt(component.flattenToString(), reason).apply();
+ }
}
@Override
@@ -45,4 +61,12 @@
return mPm.getComponentEnabledSetting(component)
!= PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
}
+
+ @Override
+ public @DisableReason int getDisableReason(ComponentName componentName) {
+ if (isEnabled(componentName)) {
+ return ENABLED;
+ }
+ return mAutoDisabledPrefs.getInt(componentName.flattenToString(), DISABLED_MANUALLY);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index f5d6904..e31f90d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -88,10 +88,14 @@
return mSendingKeys.contains(key);
}
- public void smartRepliesAdded(final NotificationData.Entry entry, int replyCount) {
+ /**
+ * Smart Replies and Actions have been added to the UI.
+ */
+ public void smartSuggestionsAdded(final NotificationData.Entry entry, int replyCount,
+ int actionCount, boolean generatedByAssistant) {
try {
- mBarService.onNotificationSmartRepliesAdded(entry.notification.getKey(),
- replyCount);
+ mBarService.onNotificationSmartSuggestionsAdded(
+ entry.notification.getKey(), replyCount, actionCount, generatedByAssistant);
} catch (RemoteException e) {
// Nothing to do, system going down
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
new file mode 100644
index 0000000..49f1a8d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification;
+
+import android.app.Notification;
+import android.os.SystemClock;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.view.View;
+
+import com.android.systemui.DejankUtils;
+import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.phone.ShadeController;
+
+/**
+ * Click handler for generic clicks on notifications. Clicks on specific areas (expansion caret,
+ * app ops icon, etc) are handled elsewhere.
+ */
+public final class NotificationClicker implements View.OnClickListener {
+ private static final String TAG = "NotificationClicker";
+
+ private final ShadeController mShadeController;
+ private final BubbleController mBubbleController;
+ private final NotificationActivityStarter mNotificationActivityStarter;
+
+ public NotificationClicker(ShadeController shadeController,
+ BubbleController bubbleController,
+ NotificationActivityStarter notificationActivityStarter) {
+ mShadeController = shadeController;
+ mBubbleController = bubbleController;
+ mNotificationActivityStarter = notificationActivityStarter;
+ }
+
+ @Override
+ public void onClick(final View v) {
+ if (!(v instanceof ExpandableNotificationRow)) {
+ Log.e(TAG, "NotificationClicker called on a view that is not a notification row.");
+ return;
+ }
+
+ mShadeController.wakeUpIfDozing(SystemClock.uptimeMillis(), v);
+
+ final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ final StatusBarNotification sbn = row.getStatusBarNotification();
+ if (sbn == null) {
+ Log.e(TAG, "NotificationClicker called on an unclickable notification,");
+ return;
+ }
+
+ // Check if the notification is displaying the menu, if so slide notification back
+ if (isMenuVisible(row)) {
+ row.animateTranslateNotification(0);
+ return;
+ } else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) {
+ row.getNotificationParent().animateTranslateNotification(0);
+ return;
+ } else if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
+ // We never want to open the app directly if the user clicks in between
+ // the notifications.
+ return;
+ }
+
+ // Mark notification for one frame.
+ row.setJustClicked(true);
+ DejankUtils.postAfterTraversal(() -> row.setJustClicked(false));
+
+ // If it was a bubble we should close it
+ if (row.getEntry().isBubble()) {
+ mBubbleController.collapseStack();
+ }
+
+ mNotificationActivityStarter.onNotificationClicked(sbn, row);
+ }
+
+ private boolean isMenuVisible(ExpandableNotificationRow row) {
+ return row.getProvider() != null && row.getProvider().isMenuVisible();
+ }
+
+ /**
+ * Attaches the click listener to the row if appropriate.
+ */
+ public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
+ Notification notification = sbn.getNotification();
+ if (notification.contentIntent != null || notification.fullScreenIntent != null) {
+ row.setOnClickListener(this);
+ } else {
+ row.setOnClickListener(null);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index aab3fc4..d2a5864 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -15,6 +15,7 @@
*/
package com.android.systemui.statusbar.notification;
+import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.systemui.bubbles.BubbleController.DEBUG_DEMOTE_TO_NOTIF;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
@@ -36,7 +37,6 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.dreams.DreamService;
@@ -49,7 +49,6 @@
import android.util.ArraySet;
import android.util.EventLog;
import android.util.Log;
-import android.view.View;
import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
@@ -57,7 +56,6 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.util.NotificationMessagingUtil;
-import com.android.systemui.DejankUtils;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.EventLogTags;
@@ -115,7 +113,6 @@
private final NotificationMessagingUtil mMessagingUtil;
protected final Context mContext;
protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>();
- private final NotificationClicker mNotificationClicker = new NotificationClicker();
private final NotificationGroupManager mGroupManager =
Dependency.get(NotificationGroupManager.class);
@@ -146,7 +143,6 @@
protected IStatusBarService mBarService;
private NotificationPresenter mPresenter;
private Callback mCallback;
- private NotificationActivityStarter mNotificationActivityStarter;
protected PowerManager mPowerManager;
private NotificationListenerService.RankingMap mLatestRankingMap;
protected HeadsUpManager mHeadsUpManager;
@@ -161,63 +157,7 @@
private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
private NotificationViewHierarchyManager.StatusBarStateListener mStatusBarStateListener;
@Nullable private AlertTransferListener mAlertTransferListener;
-
- private final class NotificationClicker implements View.OnClickListener {
-
- @Override
- public void onClick(final View v) {
- if (!(v instanceof ExpandableNotificationRow)) {
- Log.e(TAG, "NotificationClicker called on a view that is not a notification row.");
- return;
- }
-
- getShadeController().wakeUpIfDozing(SystemClock.uptimeMillis(), v);
-
- final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
- final StatusBarNotification sbn = row.getStatusBarNotification();
- if (sbn == null) {
- Log.e(TAG, "NotificationClicker called on an unclickable notification,");
- return;
- }
-
- // Check if the notification is displaying the menu, if so slide notification back
- if (isMenuVisible(row)) {
- row.animateTranslateNotification(0);
- return;
- } else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) {
- row.getNotificationParent().animateTranslateNotification(0);
- return;
- } else if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
- // We never want to open the app directly if the user clicks in between
- // the notifications.
- return;
- }
-
- // Mark notification for one frame.
- row.setJustClicked(true);
- DejankUtils.postAfterTraversal(() -> row.setJustClicked(false));
-
- // If it was a bubble we should close it
- if (row.getEntry().isBubble()) {
- mBubbleController.collapseStack();
- }
-
- mNotificationActivityStarter.onNotificationClicked(sbn, row);
- }
-
- private boolean isMenuVisible(ExpandableNotificationRow row) {
- return row.getProvider() != null && row.getProvider().isMenuVisible();
- }
-
- public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
- Notification notification = sbn.getNotification();
- if (notification.contentIntent != null || notification.fullScreenIntent != null) {
- row.setOnClickListener(this);
- } else {
- row.setOnClickListener(null);
- }
- }
- }
+ @Nullable private NotificationClicker mNotificationClicker;
private final DeviceProvisionedController.DeviceProvisionedListener
mDeviceProvisionedListener =
@@ -269,6 +209,10 @@
mAlertTransferListener = listener;
}
+ public void setNotificationClicker(NotificationClicker clicker) {
+ mNotificationClicker = clicker;
+ }
+
/**
* Our dependencies can have cyclic references, so some need to be lazy
*/
@@ -355,11 +299,6 @@
mOnAppOpsClickListener = mGutsManager::openGuts;
}
- public void setNotificationActivityStarter(
- NotificationActivityStarter notificationActivityStarter) {
- mNotificationActivityStarter = notificationActivityStarter;
- }
-
public NotificationData getNotificationData() {
return mNotificationData;
}
@@ -757,7 +696,7 @@
row.setIsLowPriority(isLowPriority);
row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority));
// bind the click event to the content area
- mNotificationClicker.register(row, sbn);
+ checkNotNull(mNotificationClicker).register(row, sbn);
// Extract target SDK version.
try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 6bc39c8..02a310c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -1474,9 +1474,19 @@
if (mExpandedChild != null) {
mExpandedSmartReplyView =
applySmartReplyView(mExpandedChild, smartRepliesAndActions, entry);
- if (mExpandedSmartReplyView != null && smartRepliesAndActions.smartReplies != null) {
- mSmartReplyController.smartRepliesAdded(
- entry, smartRepliesAndActions.smartReplies.choices.length);
+ if (mExpandedSmartReplyView != null) {
+ if (smartRepliesAndActions.smartReplies != null
+ || smartRepliesAndActions.smartActions != null) {
+ int numSmartReplies = smartRepliesAndActions.smartReplies == null
+ ? 0 : smartRepliesAndActions.smartReplies.choices.length;
+ int numSmartActions = smartRepliesAndActions.smartActions == null
+ ? 0 : smartRepliesAndActions.smartActions.actions.size();
+ boolean fromAssistant = smartRepliesAndActions.smartReplies == null
+ ? smartRepliesAndActions.smartActions.fromAssistant
+ : smartRepliesAndActions.smartReplies.fromAssistant;
+ mSmartReplyController.smartSuggestionsAdded(entry, numSmartReplies,
+ numSmartActions, fromAssistant);
+ }
}
}
if (mHeadsUpChild != null) {
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 75e5cba..1e70912 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -187,6 +187,7 @@
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationClicker;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationData.Entry;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -630,8 +631,6 @@
mBubbleController = Dependency.get(BubbleController.class);
mBubbleController.setExpandListener(mBubbleExpandListener);
- mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager);
-
mColorExtractor.addOnColorsChangedListener(this);
mStatusBarStateController.addCallback(this, StatusBarStateController.RANK_STATUS_BAR);
@@ -1018,7 +1017,7 @@
return new QSFragment();
}
- protected void setUpPresenter() {
+ private void setUpPresenter() {
// Set up the initial notification state.
mActivityLaunchAnimator = new ActivityLaunchAnimator(
mStatusBarWindow, this, mNotificationPanel,
@@ -1036,7 +1035,10 @@
mNotificationActivityStarter = new StatusBarNotificationActivityStarter(
mContext, mNotificationPanel, mPresenter, mHeadsUpManager, mActivityLaunchAnimator);
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
- mEntryManager.setNotificationActivityStarter(mNotificationActivityStarter);
+
+ mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager);
+ mEntryManager.setNotificationClicker(new NotificationClicker(
+ this, Dependency.get(BubbleController.class), mNotificationActivityStarter));
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
index dae1472..0a29e04 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
@@ -184,7 +184,11 @@
mInfo.services[i].name);
if (mPluginEnabler.isEnabled(componentName) != isEnabled) {
- mPluginEnabler.setEnabled(componentName, isEnabled);
+ if (isEnabled) {
+ mPluginEnabler.setEnabled(componentName);
+ } else {
+ mPluginEnabler.setDisabled(componentName, PluginEnabler.DISABLED_MANUALLY);
+ }
shouldSendBroadcast = true;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 7ca5423..fb2ceac 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -35,6 +35,7 @@
import android.testing.TestableLooper.RunWithLooper;
import android.text.TextPaint;
import android.view.LayoutInflater;
+import android.widget.FrameLayout;
import android.widget.TextClock;
import com.android.systemui.SysuiTestCase;
@@ -51,10 +52,14 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWithLooper
@RunWith(AndroidTestingRunner.class)
+// Need to run on the main thread because KeyguardSliceView$Row init checks for
+// the main thread before acquiring a wake lock. This class is constructed when
+// the keyguard_clcok_switch layout is inflated.
+@RunWithLooper(setAsMainLooper = true)
public class KeyguardClockSwitchTest extends SysuiTestCase {
private PluginManager mPluginManager;
+ private FrameLayout mClockContainer;
@Mock
TextClock mClockView;
@@ -67,6 +72,7 @@
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
mKeyguardClockSwitch =
(KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null);
+ mClockContainer = mKeyguardClockSwitch.findViewById(R.id.clock_view);
MockitoAnnotations.initMocks(this);
when(mClockView.getPaint()).thenReturn(mock(TextPaint.class));
}
@@ -97,7 +103,7 @@
listener.onPluginConnected(plugin, null);
verify(mClockView).setVisibility(GONE);
- assertThat(plugin.getView().getParent()).isEqualTo(mKeyguardClockSwitch);
+ assertThat(plugin.getView().getParent()).isEqualTo(mClockContainer);
}
@Test
@@ -120,7 +126,7 @@
when(plugin2.getView()).thenReturn(new TextClock(getContext()));
listener.onPluginConnected(plugin2, null);
// THEN only the view from the second plugin should be a child of KeyguardClockSwitch.
- assertThat(plugin2.getView().getParent()).isEqualTo(mKeyguardClockSwitch);
+ assertThat(plugin2.getView().getParent()).isEqualTo(mClockContainer);
assertThat(plugin1.getView().getParent()).isNull();
}
@@ -161,7 +167,7 @@
// WHEN the first plugin is disconnected
listener.onPluginDisconnected(plugin1);
// THEN the view from the second plugin is still a child of KeyguardClockSwitch.
- assertThat(plugin2.getView().getParent()).isEqualTo(mKeyguardClockSwitch);
+ assertThat(plugin2.getView().getParent()).isEqualTo(mClockContainer);
assertThat(plugin1.getView().getParent()).isNull();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
index 5cc3b3c..4583770 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
@@ -17,6 +17,7 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
+
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
@@ -26,22 +27,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.Plugin;
-import com.android.systemui.plugins.PluginEnablerImpl;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
-import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
-import com.android.systemui.plugins.annotations.Requires;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mockito;
-
import android.app.Activity;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
@@ -60,6 +45,21 @@
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.annotations.Requires;
+import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
+import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -79,6 +79,9 @@
private PluginInstanceManager mPluginInstanceManager;
private PluginManagerImpl mMockManager;
private VersionInfo mMockVersionInfo;
+ private PluginEnabler mMockEnabler;
+ ComponentName mTestPluginComponentName =
+ new ComponentName(WHITELISTED_PACKAGE, TestPlugin.class.getName());
@Before
public void setup() throws Exception {
@@ -88,9 +91,9 @@
mMockPm = mock(PackageManager.class);
mMockListener = mock(PluginListener.class);
mMockManager = mock(PluginManagerImpl.class);
- when(mMockManager.getClassLoader(any(), any()))
- .thenReturn(getClass().getClassLoader());
- when(mMockManager.getPluginEnabler()).thenReturn(new PluginEnablerImpl(mMockPm));
+ when(mMockManager.getClassLoader(any(), any())).thenReturn(getClass().getClassLoader());
+ mMockEnabler = mock(PluginEnabler.class);
+ when(mMockManager.getPluginEnabler()).thenReturn(mMockEnabler);
mMockVersionInfo = mock(VersionInfo.class);
mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
@@ -230,18 +233,13 @@
// Start with an unrelated class.
boolean result = mPluginInstanceManager.checkAndDisable(Activity.class.getName());
assertFalse(result);
- verify(mMockPm, never()).setComponentEnabledSetting(
- ArgumentCaptor.forClass(ComponentName.class).capture(),
- ArgumentCaptor.forClass(int.class).capture(),
- ArgumentCaptor.forClass(int.class).capture());
+ verify(mMockEnabler, never()).setDisabled(any(ComponentName.class), anyInt());
// Now hand it a real class and make sure it disables the plugin.
result = mPluginInstanceManager.checkAndDisable(TestPlugin.class.getName());
assertTrue(result);
- verify(mMockPm).setComponentEnabledSetting(
- ArgumentCaptor.forClass(ComponentName.class).capture(),
- ArgumentCaptor.forClass(int.class).capture(),
- ArgumentCaptor.forClass(int.class).capture());
+ verify(mMockEnabler).setDisabled(
+ mTestPluginComponentName, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH);
}
@Test
@@ -250,10 +248,8 @@
mPluginInstanceManager.disableAll();
- verify(mMockPm).setComponentEnabledSetting(
- ArgumentCaptor.forClass(ComponentName.class).capture(),
- ArgumentCaptor.forClass(int.class).capture(),
- ArgumentCaptor.forClass(int.class).capture());
+ verify(mMockEnabler).setDisabled(
+ mTestPluginComponentName, PluginEnabler.DISABLED_FROM_SYSTEM_CRASH);
}
@Test
@@ -275,8 +271,8 @@
List<ResolveInfo> list = new ArrayList<>();
ResolveInfo info = new ResolveInfo();
info.serviceInfo = mock(ServiceInfo.class);
- info.serviceInfo.packageName = "com.android.systemui";
- info.serviceInfo.name = TestPlugin.class.getName();
+ info.serviceInfo.packageName = mTestPluginComponentName.getPackageName();
+ info.serviceInfo.name = mTestPluginComponentName.getClassName();
when(info.serviceInfo.loadLabel(any())).thenReturn("Test Plugin");
list.add(info);
when(mMockPm.queryIntentServices(any(), Mockito.anyInt())).thenReturn(list);
@@ -288,6 +284,7 @@
ApplicationInfo appInfo = getContext().getApplicationInfo();
when(mMockPm.getApplicationInfo(Mockito.anyString(), Mockito.anyInt())).thenReturn(
appInfo);
+ when(mMockEnabler.isEnabled(mTestPluginComponentName)).thenReturn(true);
}
private void createPlugin() throws Exception {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
index ff1bc8ab..76e68f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
@@ -27,7 +27,6 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
-import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -82,17 +81,18 @@
.thenReturn(mMockPluginInstance);
mMockPackageManager = mock(PackageManager.class);
- mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, true,
+ mPluginManager = new PluginManagerImpl(
+ getContext(), mMockFactory, true,
mMockExceptionHandler, new PluginInitializerImpl() {
- @Override
- public String[] getWhitelistedPlugins(Context context) {
- return new String[0];
- }
+ @Override
+ public String[] getWhitelistedPlugins(Context context) {
+ return new String[0];
+ }
- @Override
- public PluginEnabler getPluginEnabler(Context context) {
- return new PluginEnablerImpl(mMockPackageManager);
- }
+ @Override
+ public PluginEnabler getPluginEnabler(Context context) {
+ return new PluginEnablerImpl(context, mMockPackageManager);
+ }
});
resetExceptionHandler();
mMockListener = mock(PluginListener.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index 8d52ccd..14e611a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -51,6 +51,7 @@
private static final String TEST_CHOICE_TEXT = "A Reply";
private static final int TEST_CHOICE_INDEX = 2;
private static final int TEST_CHOICE_COUNT = 4;
+ private static final int TEST_ACTION_COUNT = 3;
private Notification mNotification;
private NotificationData.Entry mEntry;
@@ -117,12 +118,14 @@
}
@Test
- public void testShowSmartReply_logsToStatusBar() throws RemoteException {
- mSmartReplyController.smartRepliesAdded(mEntry, TEST_CHOICE_COUNT);
+ public void testShowSmartSuggestions_logsToStatusBar() throws RemoteException {
+ final boolean generatedByAsssistant = true;
+ mSmartReplyController.smartSuggestionsAdded(mEntry, TEST_CHOICE_COUNT, TEST_ACTION_COUNT,
+ generatedByAsssistant);
// Check we log the result to the status bar service.
- verify(mIStatusBarService).onNotificationSmartRepliesAdded(mSbn.getKey(),
- TEST_CHOICE_COUNT);
+ verify(mIStatusBarService).onNotificationSmartSuggestionsAdded(mSbn.getKey(),
+ TEST_CHOICE_COUNT, TEST_ACTION_COUNT, generatedByAsssistant);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 904e5b9..7f0e435 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -231,6 +231,7 @@
mEntryManager = new TestableNotificationEntryManager(mContext, mBarService);
Dependency.get(InitController.class).executePostInitTasks();
mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mCallback, mHeadsUpManager);
+ mEntryManager.setNotificationClicker(mock(NotificationClicker.class));
setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index c207fef..e5620a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -176,6 +176,7 @@
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
mDependency.injectTestDependency(NotificationLogger.class, mNotificationLogger);
mNotificationLogger = new NotificationLogger();
+ DozeLog.traceDozing(mContext, false /* dozing */);
IPowerManager powerManagerService = mock(IPowerManager.class);
mPowerManager = new PowerManager(mContext, powerManagerService,
diff --git a/packages/overlays/AccentColorBlackOverlay/Android.mk b/packages/overlays/AccentColorBlackOverlay/Android.mk
index d316fbd..b81ae5bb 100644
--- a/packages/overlays/AccentColorBlackOverlay/Android.mk
+++ b/packages/overlays/AccentColorBlackOverlay/Android.mk
@@ -19,6 +19,7 @@
LOCAL_RRO_THEME := AccentColorBlack
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/AccentColorGreenOverlay/Android.mk b/packages/overlays/AccentColorGreenOverlay/Android.mk
index afc4287..db92157 100644
--- a/packages/overlays/AccentColorGreenOverlay/Android.mk
+++ b/packages/overlays/AccentColorGreenOverlay/Android.mk
@@ -19,6 +19,7 @@
LOCAL_RRO_THEME := AccentColorGreen
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/AccentColorPurpleOverlay/Android.mk b/packages/overlays/AccentColorPurpleOverlay/Android.mk
index 3366169..d7dc497 100644
--- a/packages/overlays/AccentColorPurpleOverlay/Android.mk
+++ b/packages/overlays/AccentColorPurpleOverlay/Android.mk
@@ -19,6 +19,7 @@
LOCAL_RRO_THEME := AccentColorPurple
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/CleanSpec.mk b/packages/overlays/CleanSpec.mk
new file mode 100644
index 0000000..16fbaa20
--- /dev/null
+++ b/packages/overlays/CleanSpec.mk
@@ -0,0 +1,53 @@
+# Copyright (C) 2018 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/AccentColor*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/DisplayCutout*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/IconShape*)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk
index 74c43b4..bf2b631 100644
--- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk
@@ -4,6 +4,8 @@
LOCAL_RRO_THEME := DisplayCutoutEmulationCorner
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
+
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk
index d83b30a..7042906 100644
--- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk
@@ -4,6 +4,8 @@
LOCAL_RRO_THEME := DisplayCutoutEmulationDouble
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
+
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
index f5afad2..ae69e11 100644
--- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
@@ -4,6 +4,8 @@
LOCAL_RRO_THEME := DisplayCutoutEmulationNarrow
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
+
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
index f1f8c27..7dcadfb 100644
--- a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
@@ -4,6 +4,8 @@
LOCAL_RRO_THEME := DisplayCutoutEmulationTall
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
+
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
index d149d8e..3f7be73 100644
--- a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
@@ -4,6 +4,8 @@
LOCAL_RRO_THEME := DisplayCutoutEmulationWide
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
+
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/packages/overlays/IconShapeRoundedRectOverlay/Android.mk b/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
index a734a6b..08428d1 100644
--- a/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
+++ b/packages/overlays/IconShapeRoundedRectOverlay/Android.mk
@@ -19,6 +19,7 @@
LOCAL_RRO_THEME := IconShapeRoundedRect
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconShapeSquareOverlay/Android.mk b/packages/overlays/IconShapeSquareOverlay/Android.mk
index 217da9f..ceb745a 100644
--- a/packages/overlays/IconShapeSquareOverlay/Android.mk
+++ b/packages/overlays/IconShapeSquareOverlay/Android.mk
@@ -19,6 +19,7 @@
LOCAL_RRO_THEME := IconShapeSquare
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconShapeSquircleOverlay/Android.mk b/packages/overlays/IconShapeSquircleOverlay/Android.mk
index fd3bfa0..34edc3b 100644
--- a/packages/overlays/IconShapeSquircleOverlay/Android.mk
+++ b/packages/overlays/IconShapeSquircleOverlay/Android.mk
@@ -19,6 +19,7 @@
LOCAL_RRO_THEME := IconShapeSquircle
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/packages/overlays/IconShapeTeardropOverlay/Android.mk b/packages/overlays/IconShapeTeardropOverlay/Android.mk
index ea43423..834a1c3 100644
--- a/packages/overlays/IconShapeTeardropOverlay/Android.mk
+++ b/packages/overlays/IconShapeTeardropOverlay/Android.mk
@@ -19,6 +19,7 @@
LOCAL_RRO_THEME := IconShapeTeardrop
LOCAL_CERTIFICATE := platform
+LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 2cbab49..a533640 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -16,10 +16,13 @@
package com.android.server.backup;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
import android.Manifest;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.backup.BackupManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
import android.app.backup.IFullBackupRestoreObserver;
@@ -41,6 +44,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.SystemConfig;
@@ -83,22 +87,27 @@
}
private final Context mContext;
- private UserBackupManagerService mUserBackupManagerService;
+ private final Trampoline mTrampoline;
+ private final HandlerThread mBackupThread;
+
+ // Keeps track of all unlocked users registered with this service. Indexed by user id.
+ private final SparseArray<UserBackupManagerService> mServiceUsers = new SparseArray<>();
+
+ private Set<ComponentName> mTransportWhitelist;
/** Instantiate a new instance of {@link BackupManagerService}. */
public BackupManagerService(
Context context, Trampoline trampoline, HandlerThread backupThread) {
- // Set up our transport options and initialize the default transport
- SystemConfig systemConfig = SystemConfig.getInstance();
- Set<ComponentName> transportWhitelist = systemConfig.getBackupTransportWhitelist();
- if (transportWhitelist == null) {
- transportWhitelist = Collections.emptySet();
- }
+ mContext = checkNotNull(context);
+ mTrampoline = checkNotNull(trampoline);
+ mBackupThread = checkNotNull(backupThread);
- mContext = context;
- mUserBackupManagerService =
- UserBackupManagerService.createAndInitializeService(
- context, trampoline, backupThread, transportWhitelist);
+ // Set up our transport options.
+ SystemConfig systemConfig = SystemConfig.getInstance();
+ mTransportWhitelist = systemConfig.getBackupTransportWhitelist();
+ if (mTransportWhitelist == null) {
+ mTransportWhitelist = Collections.emptySet();
+ }
}
/**
@@ -115,12 +124,6 @@
}
}
- // TODO(b/118520567): Remove when tests are modified to use per-user instance.
- @VisibleForTesting
- void setUserBackupManagerService(UserBackupManagerService userBackupManagerService) {
- mUserBackupManagerService = userBackupManagerService;
- }
-
/**
* Called through Trampoline from {@link Lifecycle#onUnlockUser(int)}. We run the heavy work on
* a background thread to keep the unlock time down.
@@ -139,9 +142,42 @@
* Starts the backup service for user {@code userId} by creating a new instance of {@link
* UserBackupManagerService} and registering it with this service.
*/
- // TODO(b/120212806): Add UserBackupManagerService initialization logic.
- void startServiceForUser(int userId) {
- // Intentionally empty.
+ @VisibleForTesting
+ protected void startServiceForUser(int userId) {
+ UserBackupManagerService userBackupManagerService =
+ UserBackupManagerService.createAndInitializeService(
+ mContext, mTrampoline, mBackupThread, mTransportWhitelist);
+ startServiceForUser(userId, userBackupManagerService);
+ }
+
+ /**
+ * Starts the backup service for user {@code userId} by registering its instance of {@link
+ * UserBackupManagerService} with this service.
+ */
+ void startServiceForUser(int userId, UserBackupManagerService userBackupManagerService) {
+ mServiceUsers.put(userId, userBackupManagerService);
+ }
+
+ SparseArray<UserBackupManagerService> getServiceUsers() {
+ return mServiceUsers;
+ }
+
+ /**
+ * Returns the {@link UserBackupManagerService} instance for the specified user {@code userId}.
+ * If the user is not registered with the service (either the user is locked or not eligible for
+ * the backup service) then return {@code null}.
+ *
+ * @param userId The id of the user to retrieve its instance of {@link
+ * UserBackupManagerService}.
+ * @param caller A {@link String} identifying the caller for logging purposes.
+ */
+ @Nullable
+ private UserBackupManagerService getServiceForUser(@UserIdInt int userId, String caller) {
+ UserBackupManagerService userBackupManagerService = mServiceUsers.get(userId);
+ if (userBackupManagerService == null) {
+ Slog.w(TAG, "Called " + caller + " for unknown user: " + userId);
+ }
+ return userBackupManagerService;
}
/*
@@ -149,7 +185,7 @@
* They delegate to the appropriate per-user instance of UserBackupManagerService to perform the
* action on the passed in user. Currently this is a straight redirection (see TODO).
*/
- // TODO (b/118520567): Take in user id and call per-user instance of UserBackupManagerService.
+ // TODO (b/118520567): Stop hardcoding system user when we pass in user id as a parameter
// ---------------------------------------------
// BACKUP AGENT OPERATIONS
@@ -161,7 +197,12 @@
* backup.
*/
public void dataChanged(String packageName) {
- mUserBackupManagerService.dataChanged(packageName);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "dataChanged()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.dataChanged(packageName);
+ }
}
/**
@@ -169,7 +210,12 @@
* {@link ActivityManager}.
*/
public void agentConnected(String packageName, IBinder agentBinder) {
- mUserBackupManagerService.agentConnected(packageName, agentBinder);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "agentConnected()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.agentConnected(packageName, agentBinder);
+ }
}
/**
@@ -177,7 +223,12 @@
* called from the {@link ActivityManager}.
*/
public void agentDisconnected(String packageName) {
- mUserBackupManagerService.agentDisconnected(packageName);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "agentDisconnected()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.agentDisconnected(packageName);
+ }
}
/**
@@ -185,7 +236,12 @@
* outstanding asynchronous backup/restore operation.
*/
public void opComplete(int token, long result) {
- mUserBackupManagerService.opComplete(token, result);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "opComplete()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.opComplete(token, result);
+ }
}
// ---------------------------------------------
@@ -194,7 +250,12 @@
/** Run an initialize operation for the given transports {@code transportNames}. */
public void initializeTransports(String[] transportNames, IBackupObserver observer) {
- mUserBackupManagerService.initializeTransports(transportNames, observer);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "initializeTransports()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.initializeTransports(transportNames, observer);
+ }
}
/**
@@ -202,35 +263,70 @@
* transportName}.
*/
public void clearBackupData(String transportName, String packageName) {
- mUserBackupManagerService.clearBackupData(transportName, packageName);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "clearBackupData()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.clearBackupData(transportName, packageName);
+ }
}
/** Return the name of the currently active transport. */
+ @Nullable
public String getCurrentTransport() {
- return mUserBackupManagerService.getCurrentTransport();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "getCurrentTransport()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getCurrentTransport();
}
/**
* Returns the {@link ComponentName} of the host service of the selected transport or {@code
* null} if no transport selected or if the transport selected is not registered.
*/
+ @Nullable
public ComponentName getCurrentTransportComponent() {
- return mUserBackupManagerService.getCurrentTransportComponent();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "getCurrentTransportComponent()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getCurrentTransportComponent();
}
/** Report all known, available backup transports by name. */
+ @Nullable
public String[] listAllTransports() {
- return mUserBackupManagerService.listAllTransports();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "listAllTransports()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.listAllTransports();
}
/** Report all known, available backup transports by {@link ComponentName}. */
+ @Nullable
public ComponentName[] listAllTransportComponents() {
- return mUserBackupManagerService.listAllTransportComponents();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "listAllTransportComponents()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.listAllTransportComponents();
}
/** Report all system whitelisted transports. */
+ @Nullable
public String[] getTransportWhitelist() {
- return mUserBackupManagerService.getTransportWhitelist();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "getTransportWhitelist()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getTransportWhitelist();
}
/**
@@ -263,13 +359,18 @@
String currentDestinationString,
@Nullable Intent dataManagementIntent,
String dataManagementLabel) {
- mUserBackupManagerService.updateTransportAttributes(
- transportComponent,
- name,
- configurationIntent,
- currentDestinationString,
- dataManagementIntent,
- dataManagementLabel);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "updateTransportAttributes()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.updateTransportAttributes(
+ transportComponent,
+ name,
+ configurationIntent,
+ currentDestinationString,
+ dataManagementIntent,
+ dataManagementLabel);
+ }
}
/**
@@ -281,7 +382,12 @@
@Deprecated
@Nullable
public String selectBackupTransport(String transportName) {
- return mUserBackupManagerService.selectBackupTransport(transportName);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "selectBackupTransport()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.selectBackupTransport(transportName);
}
/**
@@ -290,7 +396,12 @@
*/
public void selectBackupTransportAsync(
ComponentName transportComponent, ISelectBackupTransportCallback listener) {
- mUserBackupManagerService.selectBackupTransportAsync(transportComponent, listener);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "selectBackupTransportAsync()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.selectBackupTransportAsync(transportComponent, listener);
+ }
}
/**
@@ -298,8 +409,14 @@
* available transports, or if the transport does not supply any configuration UI, the method
* returns {@code null}.
*/
+ @Nullable
public Intent getConfigurationIntent(String transportName) {
- return mUserBackupManagerService.getConfigurationIntent(transportName);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "getConfigurationIntent()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getConfigurationIntent(transportName);
}
/**
@@ -311,21 +428,39 @@
* @param transportName The name of the registered transport.
* @return The current destination string or null if the transport is not registered.
*/
+ @Nullable
public String getDestinationString(String transportName) {
- return mUserBackupManagerService.getDestinationString(transportName);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "getDestinationString()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getDestinationString(transportName);
}
/** Supply the manage-data intent for the given transport. */
+ @Nullable
public Intent getDataManagementIntent(String transportName) {
- return mUserBackupManagerService.getDataManagementIntent(transportName);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "getDataManagementIntent()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getDataManagementIntent(transportName);
}
/**
* Supply the menu label for affordances that fire the manage-data intent for the given
* transport.
*/
+ @Nullable
public String getDataManagementLabel(String transportName) {
- return mUserBackupManagerService.getDataManagementLabel(transportName);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "getDataManagementLabel()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getDataManagementLabel(transportName);
}
// ---------------------------------------------
@@ -335,17 +470,32 @@
/** Enable/disable the backup service. This is user-configurable via backup settings. */
public void setBackupEnabled(@UserIdInt int userId, boolean enable) {
enforceCallingPermissionOnUserId(userId, "setBackupEnabled");
- mUserBackupManagerService.setBackupEnabled(enable);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(userId, "setBackupEnabled()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.setBackupEnabled(enable);
+ }
}
/** Enable/disable automatic restore of app data at install time. */
public void setAutoRestore(boolean autoRestore) {
- mUserBackupManagerService.setAutoRestore(autoRestore);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "setAutoRestore()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.setAutoRestore(autoRestore);
+ }
}
/** Mark the backup service as having been provisioned (device has gone through SUW). */
public void setBackupProvisioned(boolean provisioned) {
- mUserBackupManagerService.setBackupProvisioned(provisioned);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "setBackupProvisioned()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.setBackupProvisioned(provisioned);
+ }
}
/**
@@ -353,7 +503,10 @@
*/
public boolean isBackupEnabled(@UserIdInt int userId) {
enforceCallingPermissionOnUserId(userId, "isBackupEnabled");
- return mUserBackupManagerService.isBackupEnabled();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(userId, "isBackupEnabled()");
+
+ return userBackupManagerService != null && userBackupManagerService.isBackupEnabled();
}
// ---------------------------------------------
@@ -362,14 +515,24 @@
/** Checks if the given package {@code packageName} is eligible for backup. */
public boolean isAppEligibleForBackup(String packageName) {
- return mUserBackupManagerService.isAppEligibleForBackup(packageName);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "isAppEligibleForBackup()");
+
+ return userBackupManagerService != null
+ && userBackupManagerService.isAppEligibleForBackup(packageName);
}
/**
* Returns from the inputted packages {@code packages}, the ones that are eligible for backup.
*/
+ @Nullable
public String[] filterAppsEligibleForBackup(String[] packages) {
- return mUserBackupManagerService.filterAppsEligibleForBackup(packages);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "filterAppsEligibleForBackup()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.filterAppsEligibleForBackup(packages);
}
/**
@@ -378,7 +541,12 @@
*/
public void backupNow(@UserIdInt int userId) {
enforceCallingPermissionOnUserId(userId, "backupNow");
- mUserBackupManagerService.backupNow();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(userId, "backupNow()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.backupNow();
+ }
}
/**
@@ -392,13 +560,23 @@
IBackupManagerMonitor monitor,
int flags) {
enforceCallingPermissionOnUserId(userId, "requestBackup");
- return mUserBackupManagerService.requestBackup(packages, observer, monitor, flags);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(userId, "requestBackup()");
+
+ return userBackupManagerService == null
+ ? BackupManager.ERROR_BACKUP_NOT_ALLOWED
+ : userBackupManagerService.requestBackup(packages, observer, monitor, flags);
}
/** Cancel all running backup operations. */
public void cancelBackups(@UserIdInt int userId) {
enforceCallingPermissionOnUserId(userId, "cancelBackups");
- mUserBackupManagerService.cancelBackups();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(userId, "cancelBackups()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.cancelBackups();
+ }
}
/**
@@ -410,7 +588,11 @@
* return value to the callback {@link JobService#onStartJob(JobParameters)}.
*/
public boolean beginFullBackup(FullBackupJob scheduledJob) {
- return mUserBackupManagerService.beginFullBackup(scheduledJob);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "beginFullBackup()");
+
+ return userBackupManagerService != null
+ && userBackupManagerService.beginFullBackup(scheduledJob);
}
/**
@@ -418,14 +600,24 @@
* longer met for running the full backup job.
*/
public void endFullBackup() {
- mUserBackupManagerService.endFullBackup();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "endFullBackup()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.endFullBackup();
+ }
}
/**
* Run a full backup pass for the given packages {@code packageNames}. Used by 'adb shell bmgr'.
*/
public void fullTransportBackup(String[] packageNames) {
- mUserBackupManagerService.fullTransportBackup(packageNames);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "fullTransportBackup()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.fullTransportBackup(packageNames);
+ }
}
// ---------------------------------------------
@@ -437,15 +629,26 @@
* called from the {@link PackageManager}.
*/
public void restoreAtInstall(String packageName, int token) {
- mUserBackupManagerService.restoreAtInstall(packageName, token);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "restoreAtInstall()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.restoreAtInstall(packageName, token);
+ }
}
/**
* Begin a restore for the specified package {@code packageName} using the specified transport
* {@code transportName}.
*/
+ @Nullable
public IRestoreSession beginRestoreSession(String packageName, String transportName) {
- return mUserBackupManagerService.beginRestoreSession(packageName, transportName);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "beginRestoreSession()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.beginRestoreSession(packageName, transportName);
}
/**
@@ -453,7 +656,12 @@
* the active set if possible, else the ancestral one. Returns zero if none available.
*/
public long getAvailableRestoreToken(String packageName) {
- return mUserBackupManagerService.getAvailableRestoreToken(packageName);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "getAvailableRestoreToken()");
+
+ return userBackupManagerService == null
+ ? 0
+ : userBackupManagerService.getAvailableRestoreToken(packageName);
}
// ---------------------------------------------
@@ -462,12 +670,19 @@
/** Sets the backup password used when running adb backup. */
public boolean setBackupPassword(String currentPassword, String newPassword) {
- return mUserBackupManagerService.setBackupPassword(currentPassword, newPassword);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "setBackupPassword()");
+
+ return userBackupManagerService != null
+ && userBackupManagerService.setBackupPassword(currentPassword, newPassword);
}
/** Returns {@code true} if adb backup was run with a password, else returns {@code false}. */
public boolean hasBackupPassword() {
- return mUserBackupManagerService.hasBackupPassword();
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "hasBackupPassword()");
+
+ return userBackupManagerService != null && userBackupManagerService.hasBackupPassword();
}
/**
@@ -489,18 +704,22 @@
boolean doKeyValue,
String[] packageNames) {
enforceCallingPermissionOnUserId(userId, "adbBackup");
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(userId, "adbBackup()");
- mUserBackupManagerService.adbBackup(
- fd,
- includeApks,
- includeObbs,
- includeShared,
- doWidgets,
- doAllApps,
- includeSystem,
- doCompress,
- doKeyValue,
- packageNames);
+ if (userBackupManagerService != null) {
+ userBackupManagerService.adbBackup(
+ fd,
+ includeApks,
+ includeObbs,
+ includeShared,
+ doWidgets,
+ doAllApps,
+ includeSystem,
+ doCompress,
+ doKeyValue,
+ packageNames);
+ }
}
/**
@@ -510,8 +729,12 @@
*/
public void adbRestore(@UserIdInt int userId, ParcelFileDescriptor fd) {
enforceCallingPermissionOnUserId(userId, "setBackupEnabled");
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(userId, "adbRestore()");
- mUserBackupManagerService.adbRestore(fd);
+ if (userBackupManagerService != null) {
+ userBackupManagerService.adbRestore(fd);
+ }
}
/**
@@ -524,8 +747,13 @@
String currentPassword,
String encryptionPassword,
IFullBackupRestoreObserver observer) {
- mUserBackupManagerService.acknowledgeAdbBackupOrRestore(
- token, allow, currentPassword, encryptionPassword, observer);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "acknowledgeAdbBackupOrRestore()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.acknowledgeAdbBackupOrRestore(
+ token, allow, currentPassword, encryptionPassword, observer);
+ }
}
// ---------------------------------------------
@@ -534,7 +762,12 @@
/** Prints service state for 'dumpsys backup'. */
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- mUserBackupManagerService.dump(fd, pw, args);
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUser(UserHandle.USER_SYSTEM, "dump()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.dump(fd, pw, args);
+ }
}
private static boolean readBackupEnableState(int userId) {
@@ -592,7 +825,7 @@
if (userId == UserHandle.USER_SYSTEM) {
sInstance.initializeServiceAndUnlockSystemUser();
} else {
- sInstance.startServiceForUser(userId);
+ sInstance.unlockUser(userId);
}
}
}
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 4acd5c4..59b72f9 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -116,7 +116,7 @@
return SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false);
}
- protected boolean isMultiUserEnabled() {
+ private boolean isMultiUserEnabled() {
return Settings.Global.getInt(
mContext.getContentResolver(),
Settings.Global.BACKUP_MULTI_USER_ENABLED,
@@ -145,6 +145,10 @@
return new BackupManagerService(mContext, this, mHandlerThread);
}
+ protected void postToHandler(Runnable runnable) {
+ mHandler.post(runnable);
+ }
+
/**
* Initialize {@link BackupManagerService} if the backup service is not disabled. Only the
* system user can initialize the service.
@@ -174,14 +178,18 @@
* to initialize {@link BackupManagerService} and set backup state for the system user.
*/
void initializeServiceAndUnlockSystemUser() {
- mHandler.post(
+ postToHandler(
() -> {
+ // Initialize the backup service.
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
initializeService(UserHandle.USER_SYSTEM);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ // Start the service for the system user.
BackupManagerService service = mService;
if (service != null) {
+ Slog.i(TAG, "Starting service for system user");
+ service.startServiceForUser(UserHandle.USER_SYSTEM);
Slog.i(TAG, "Unlocking system user");
service.unlockSystemUser();
}
@@ -195,20 +203,21 @@
*/
// TODO(b/120212806): Consolidate service start for system and non-system users when system
// user-only logic is removed.
- void startServiceForUser(int userId) {
+ void unlockUser(int userId) {
if (!isMultiUserEnabled()) {
Slog.i(TAG, "Multi-user disabled, cannot start service for user: " + userId);
return;
}
- mHandler.post(
- () -> {
- BackupManagerService service = mService;
- if (service != null) {
- Slog.i(TAG, "Starting service for user: " + userId);
- service.startServiceForUser(userId);
- }
- });
+ postToHandler(() -> startServiceForUser(userId));
+ }
+
+ private void startServiceForUser(int userId) {
+ BackupManagerService service = mService;
+ if (service != null) {
+ Slog.i(TAG, "Starting service for user: " + userId);
+ service.startServiceForUser(userId);
+ }
}
/**
@@ -242,6 +251,7 @@
if (makeActive) {
mService = createBackupManagerService();
mSuppressFile.delete();
+ startServiceForUser(userId);
} else {
mService = null;
try {
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 10e713d..e8820ae 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -25,6 +25,7 @@
import android.app.ActivityManagerInternal;
import android.content.ComponentName;
import android.content.Context;
+import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -34,7 +35,6 @@
import android.os.UserManager;
import android.util.Slog;
import android.view.contentcapture.ContentCaptureContext;
-import android.view.contentcapture.ContentCaptureEvent;
import android.view.contentcapture.IContentCaptureManager;
import com.android.internal.annotations.GuardedBy;
@@ -47,7 +47,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.List;
/**
* A service used to observe the contents of the screen.
@@ -182,30 +181,18 @@
synchronized (mLock) {
final ContentCapturePerUserService service = getServiceForUserLocked(userId);
service.startSessionLocked(activityToken, componentName, taskId, displayId,
- sessionId, clientContext, flags, mAllowInstantService, result);
+ sessionId, Binder.getCallingUid(), clientContext, flags,
+ mAllowInstantService, result);
}
}
@Override
- public void sendEvents(@UserIdInt int userId, @NonNull String sessionId,
- @NonNull List<ContentCaptureEvent> events) {
- Preconditions.checkNotNull(sessionId);
- Preconditions.checkNotNull(events);
-
- synchronized (mLock) {
- final ContentCapturePerUserService service = getServiceForUserLocked(userId);
- service.sendEventsLocked(sessionId, events);
- }
- }
-
- @Override
- public void finishSession(@UserIdInt int userId, @NonNull String sessionId,
- @Nullable List<ContentCaptureEvent> events) {
+ public void finishSession(@UserIdInt int userId, @NonNull String sessionId) {
Preconditions.checkNotNull(sessionId);
synchronized (mLock) {
final ContentCapturePerUserService service = getServiceForUserLocked(userId);
- service.finishSessionLocked(sessionId, events);
+ service.finishSessionLocked(sessionId);
}
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 8130912..f21b0d8 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -16,6 +16,8 @@
package com.android.server.contentcapture;
+import static android.service.contentcapture.ContentCaptureService.setClientState;
+
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
@@ -38,7 +40,6 @@
import android.util.ArrayMap;
import android.util.Slog;
import android.view.contentcapture.ContentCaptureContext;
-import android.view.contentcapture.ContentCaptureEvent;
import android.view.contentcapture.ContentCaptureSession;
import com.android.internal.annotations.GuardedBy;
@@ -48,7 +49,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.List;
/**
* Per-user instance of {@link ContentCaptureManagerService}.
@@ -114,10 +114,10 @@
@GuardedBy("mLock")
public void startSessionLocked(@NonNull IBinder activityToken,
@NonNull ComponentName componentName, int taskId, int displayId,
- @NonNull String sessionId, @Nullable ContentCaptureContext clientContext,
- int flags, boolean bindInstantServiceAllowed, @NonNull IResultReceiver resultReceiver) {
+ @NonNull String sessionId, int uid, @Nullable ContentCaptureContext clientContext,
+ int flags, boolean bindInstantServiceAllowed, @NonNull IResultReceiver clientReceiver) {
if (!isEnabledLocked()) {
- sendToClient(resultReceiver, ContentCaptureSession.STATE_DISABLED);
+ setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED, /* binder=*/ null);
return;
}
final ComponentName serviceComponentName = getServiceComponentName();
@@ -131,35 +131,30 @@
return;
}
- ContentCaptureServerSession session = mSessions.get(sessionId);
- if (session != null) {
- if (mMaster.debug) {
- Slog.d(TAG, "startSession(): reusing session " + sessionId + " for "
- + componentName);
- }
- // TODO(b/111276913): check if local ids match and decide what to do if they don't
- // TODO(b/111276913): should we call session.notifySessionStartedLocked() again??
- // if not, move notifySessionStartedLocked() into session constructor
- sendToClient(resultReceiver, ContentCaptureSession.STATE_ACTIVE);
+ final ContentCaptureServerSession existingSession = mSessions.get(sessionId);
+ if (existingSession != null) {
+ Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
+ + ": ignoring because it already exists for " + existingSession.mActivityToken);
+ setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_DUPLICATED_ID,
+ /* binder=*/ null);
return;
}
- session = new ContentCaptureServerSession(getContext(), mUserId, mLock, activityToken,
- this, serviceComponentName, componentName, taskId, displayId, sessionId,
- clientContext, flags, bindInstantServiceAllowed, mMaster.verbose);
+ final ContentCaptureServerSession newSession = new ContentCaptureServerSession(getContext(),
+ mUserId, mLock, activityToken, this, serviceComponentName, componentName, taskId,
+ displayId, sessionId, uid, clientContext, flags, bindInstantServiceAllowed,
+ mMaster.verbose);
if (mMaster.verbose) {
- Slog.v(TAG, "startSession(): new session for " + componentName + " and id "
- + sessionId);
+ Slog.v(TAG, "startSession(): new session for "
+ + ComponentName.flattenToShortString(componentName) + " and id " + sessionId);
}
- mSessions.put(sessionId, session);
- session.notifySessionStartedLocked();
- sendToClient(resultReceiver, ContentCaptureSession.STATE_ACTIVE);
+ mSessions.put(sessionId, newSession);
+ newSession.notifySessionStartedLocked(clientReceiver);
}
// TODO(b/111276913): log metrics
@GuardedBy("mLock")
- public void finishSessionLocked(@NonNull String sessionId,
- @Nullable List<ContentCaptureEvent> events) {
+ public void finishSessionLocked(@NonNull String sessionId) {
if (!isEnabledLocked()) {
return;
}
@@ -171,41 +166,8 @@
}
return;
}
- if (events != null && !events.isEmpty()) {
- // TODO(b/111276913): for now we're sending the events and the onDestroy() in 2 separate
- // calls because it's not clear yet whether we'll change the manager to send events
- // to the service directly (i.e., without passing through system server). Once we
- // decide, we might need to split IContentCaptureManager.onSessionLifecycle() in 2
- // methods, one for start and another for finish (and passing the events to finish),
- // otherwise the service might receive the 2 calls out of order.
- session.sendEventsLocked(events);
- }
- if (mMaster.verbose) {
- Slog.v(TAG, "finishSession(" + (events == null ? 0 : events.size()) + " events): "
- + session);
- }
- session.removeSelfLocked(true);
- }
-
- // TODO(b/111276913): need to figure out why some events are sent before session is started;
- // probably because ContentCaptureManager is not buffering them until it gets the session back
- @GuardedBy("mLock")
- public void sendEventsLocked(@NonNull String sessionId,
- @NonNull List<ContentCaptureEvent> events) {
- if (!isEnabledLocked()) {
- return;
- }
- final ContentCaptureServerSession session = mSessions.get(sessionId);
- if (session == null) {
- if (mMaster.verbose) {
- Slog.v(TAG, "sendEvents(): no session for " + sessionId);
- }
- return;
- }
- if (mMaster.verbose) {
- Slog.v(TAG, "sendEvents(): id=" + sessionId + ", events=" + events.size());
- }
- session.sendEventsLocked(events);
+ if (mMaster.verbose) Slog.v(TAG, "finishSession(): id=" + sessionId);
+ session.removeSelfLocked(/* notifyRemoteService= */ true);
}
@GuardedBy("mLock")
@@ -308,12 +270,4 @@
}
return null;
}
-
- private static void sendToClient(@NonNull IResultReceiver resultReceiver, int value) {
- try {
- resultReceiver.send(value, null);
- } catch (RemoteException e) {
- Slog.w(TAG, "Error async reporting result to client: " + e);
- }
- }
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
index 181a2da..ba98b95 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
@@ -24,15 +24,14 @@
import android.service.contentcapture.SnapshotData;
import android.util.Slog;
import android.view.contentcapture.ContentCaptureContext;
-import android.view.contentcapture.ContentCaptureEvent;
import android.view.contentcapture.ContentCaptureSessionId;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.IResultReceiver;
import com.android.internal.util.Preconditions;
import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks;
import java.io.PrintWriter;
-import java.util.List;
final class ContentCaptureServerSession implements ContentCaptureServiceCallbacks {
@@ -43,18 +42,28 @@
private final ContentCapturePerUserService mService;
private final RemoteContentCaptureService mRemoteService;
private final ContentCaptureContext mContentCaptureContext;
+
+ /**
+ * Canonical session id.
+ */
private final String mId;
+ /**
+ * UID of the app whose contents is being captured.
+ */
+ private final int mUid;
+
ContentCaptureServerSession(@NonNull Context context, int userId, @NonNull Object lock,
@NonNull IBinder activityToken, @NonNull ContentCapturePerUserService service,
@NonNull ComponentName serviceComponentName, @NonNull ComponentName appComponentName,
- int taskId, int displayId, @NonNull String sessionId,
+ int taskId, int displayId, @NonNull String sessionId, int uid,
@Nullable ContentCaptureContext clientContext, int flags,
boolean bindInstantServiceAllowed, boolean verbose) {
mLock = lock;
mActivityToken = activityToken;
mService = service;
mId = Preconditions.checkNotNull(sessionId);
+ mUid = uid;
mRemoteService = new RemoteContentCaptureService(context,
ContentCaptureService.SERVICE_INTERFACE, serviceComponentName, userId, this,
bindInstantServiceAllowed, verbose);
@@ -73,15 +82,8 @@
* Notifies the {@link ContentCaptureService} that the service started.
*/
@GuardedBy("mLock")
- public void notifySessionStartedLocked() {
- mRemoteService.onSessionLifecycleRequest(mContentCaptureContext, mId);
- }
-
- /**
- * Notifies the {@link ContentCaptureService} of a batch of events.
- */
- public void sendEventsLocked(@NonNull List<ContentCaptureEvent> events) {
- mRemoteService.onContentCaptureEventsRequest(mId, events);
+ public void notifySessionStartedLocked(@NonNull IResultReceiver clientReceiver) {
+ mRemoteService.onSessionStarted(mContentCaptureContext, mId, mUid, clientReceiver);
}
/**
@@ -118,11 +120,11 @@
@GuardedBy("mLock")
public void destroyLocked(boolean notifyRemoteService) {
if (mService.isVerbose()) {
- Slog.v(TAG, "destroyLocked(notifyRemoteService=" + notifyRemoteService + ")");
+ Slog.v(TAG, "destroy(notifyRemoteService=" + notifyRemoteService + ")");
}
// TODO(b/111276913): must call client to set session as FINISHED_BY_SERVER
if (notifyRemoteService) {
- mRemoteService.onSessionLifecycleRequest(/* context= */ null, mId);
+ mRemoteService.onSessionFinished(mId);
}
}
@@ -133,13 +135,14 @@
Slog.d(TAG, "onServiceDied() for " + mId);
}
synchronized (mLock) {
- removeSelfLocked(/* notifyRemoteService= */ false);
+ removeSelfLocked(/* notifyRemoteService= */ true);
}
}
@GuardedBy("mLock")
public void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
pw.print(prefix); pw.print("id: "); pw.print(mId); pw.println();
+ pw.print(prefix); pw.print("uid: "); pw.print(mUid); pw.println();
pw.print(prefix); pw.print("context: "); mContentCaptureContext.dump(pw); pw.println();
pw.print(prefix); pw.print("activity token: "); pw.println(mActivityToken);
pw.print(prefix); pw.print("has autofill callback: ");
diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
index b4edf7e..b9b1943 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
@@ -20,17 +20,14 @@
import android.content.ComponentName;
import android.content.Context;
import android.os.IBinder;
-import android.service.contentcapture.ContentCaptureEventsRequest;
import android.service.contentcapture.IContentCaptureService;
import android.service.contentcapture.SnapshotData;
import android.text.format.DateUtils;
import android.view.contentcapture.ContentCaptureContext;
-import android.view.contentcapture.ContentCaptureEvent;
+import com.android.internal.os.IResultReceiver;
import com.android.server.infra.AbstractMultiplePendingRequestsRemoteService;
-import java.util.List;
-
final class RemoteContentCaptureService
extends AbstractMultiplePendingRequestsRemoteService<RemoteContentCaptureService,
IContentCaptureService> {
@@ -67,21 +64,19 @@
/**
* Called by {@link ContentCaptureServerSession} to generate a call to the
- * {@link RemoteContentCaptureService} to indicate the session was created (when {@code context}
- * is not {@code null} or destroyed (when {@code context} is {@code null}).
+ * {@link RemoteContentCaptureService} to indicate the session was created.
*/
- public void onSessionLifecycleRequest(@Nullable ContentCaptureContext context,
- @NonNull String sessionId) {
- scheduleAsyncRequest((s) -> s.onSessionLifecycle(context, sessionId));
+ public void onSessionStarted(@Nullable ContentCaptureContext context,
+ @NonNull String sessionId, int uid, @NonNull IResultReceiver clientReceiver) {
+ scheduleAsyncRequest((s) -> s.onSessionStarted(context, sessionId, uid, clientReceiver));
}
/**
- * Called by {@link ContentCaptureServerSession} to send a batch of events to the service.
+ * Called by {@link ContentCaptureServerSession} to generate a call to the
+ * {@link RemoteContentCaptureService} to indicate the session was finished.
*/
- public void onContentCaptureEventsRequest(@NonNull String sessionId,
- @NonNull List<ContentCaptureEvent> events) {
- scheduleAsyncRequest((s) -> s.onContentCaptureEventsRequest(sessionId,
- new ContentCaptureEventsRequest(events)));
+ public void onSessionFinished(@NonNull String sessionId) {
+ scheduleAsyncRequest((s) -> s.onSessionFinished(sessionId));
}
/**
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 8d912fa..f0ec69f 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -86,6 +86,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsActiveCallback;
import com.android.internal.app.IAppOpsCallback;
+import com.android.internal.app.IAppOpsNotedCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
@@ -456,6 +457,7 @@
final ArrayMap<String, ArraySet<ModeCallback>> mPackageModeWatchers = new ArrayMap<>();
final ArrayMap<IBinder, ModeCallback> mModeWatchers = new ArrayMap<>();
final ArrayMap<IBinder, SparseArray<ActiveCallback>> mActiveWatchers = new ArrayMap<>();
+ final ArrayMap<IBinder, SparseArray<NotedCallback>> mNotedWatchers = new ArrayMap<>();
final SparseArray<SparseArray<Restriction>> mAudioRestrictions = new SparseArray<>();
final class ModeCallback implements DeathRecipient {
@@ -475,6 +477,7 @@
try {
mCallback.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
+ /*ignored*/
}
}
@@ -524,6 +527,7 @@
try {
mCallback.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
+ /*ignored*/
}
}
@@ -552,6 +556,50 @@
}
}
+ final class NotedCallback implements DeathRecipient {
+ final IAppOpsNotedCallback mCallback;
+ final int mWatchingUid;
+ final int mCallingUid;
+ final int mCallingPid;
+
+ NotedCallback(IAppOpsNotedCallback callback, int watchingUid, int callingUid,
+ int callingPid) {
+ mCallback = callback;
+ mWatchingUid = watchingUid;
+ mCallingUid = callingUid;
+ mCallingPid = callingPid;
+ try {
+ mCallback.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ /*ignored*/
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("NotedCallback{");
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ sb.append(" watchinguid=");
+ UserHandle.formatUid(sb, mWatchingUid);
+ sb.append(" from uid=");
+ UserHandle.formatUid(sb, mCallingUid);
+ sb.append(" pid=");
+ sb.append(mCallingPid);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ void destroy() {
+ mCallback.asBinder().unlinkToDeath(this, 0);
+ }
+
+ @Override
+ public void binderDied() {
+ stopWatchingNoted(mCallback);
+ }
+ }
+
final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
final class ClientState extends Binder implements DeathRecipient {
@@ -1629,7 +1677,7 @@
UidState uidState = getUidStateLocked(uid, false);
if (uidState != null && uidState.opModes != null
&& uidState.opModes.indexOfKey(code) >= 0) {
- return uidState.opModes.get(code);
+ return uidState.evalMode(uidState.opModes.get(code));
}
Op op = getOpLocked(code, uid, packageName, false, true, false);
if (op == null) {
@@ -1795,12 +1843,16 @@
final Ops ops = getOpsRawLocked(uid, packageName, true /* edit */,
false /* uidMismatchExpected */);
if (ops == null) {
+ scheduleOpNotedIfNeededLocked(code, uid, packageName,
+ AppOpsManager.MODE_IGNORED);
if (DEBUG) Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
+ " package " + packageName);
return AppOpsManager.MODE_ERRORED;
}
final Op op = getOpLocked(ops, code, true);
if (isOpRestrictedLocked(uid, code, packageName)) {
+ scheduleOpNotedIfNeededLocked(code, uid, packageName,
+ AppOpsManager.MODE_IGNORED);
return AppOpsManager.MODE_IGNORED;
}
final UidState uidState = ops.uidState;
@@ -1820,6 +1872,7 @@
+ switchCode + " (" + code + ") uid " + uid + " package "
+ packageName);
op.rejectTime[uidState.state] = System.currentTimeMillis();
+ scheduleOpNotedIfNeededLocked(code, uid, packageName, uidMode);
return uidMode;
}
} else {
@@ -1830,6 +1883,7 @@
+ switchCode + " (" + code + ") uid " + uid + " package "
+ packageName);
op.rejectTime[uidState.state] = System.currentTimeMillis();
+ scheduleOpNotedIfNeededLocked(op.op, uid, packageName, mode);
return mode;
}
}
@@ -1839,6 +1893,8 @@
op.rejectTime[uidState.state] = 0;
op.proxyUid = proxyUid;
op.proxyPackageName = proxyPackageName;
+ scheduleOpNotedIfNeededLocked(code, uid, packageName,
+ AppOpsManager.MODE_ALLOWED);
return AppOpsManager.MODE_ALLOWED;
}
}
@@ -1886,10 +1942,50 @@
}
final int callbackCount = activeCallbacks.size();
for (int i = 0; i < callbackCount; i++) {
- // Apps ops are mapped to a singleton
- if (i == 0) {
- activeCallbacks.valueAt(i).destroy();
- }
+ activeCallbacks.valueAt(i).destroy();
+ }
+ }
+ }
+
+ @Override
+ public void startWatchingNoted(@NonNull int[] ops, @NonNull IAppOpsNotedCallback callback) {
+ int watchedUid = Process.INVALID_UID;
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
+ != PackageManager.PERMISSION_GRANTED) {
+ watchedUid = callingUid;
+ }
+ Preconditions.checkArgument(!ArrayUtils.isEmpty(ops), "Ops cannot be null or empty");
+ Preconditions.checkArrayElementsInRange(ops, 0, AppOpsManager._NUM_OP - 1,
+ "Invalid op code in: " + Arrays.toString(ops));
+ Preconditions.checkNotNull(callback, "Callback cannot be null");
+ synchronized (this) {
+ SparseArray<NotedCallback> callbacks = mNotedWatchers.get(callback.asBinder());
+ if (callbacks == null) {
+ callbacks = new SparseArray<>();
+ mNotedWatchers.put(callback.asBinder(), callbacks);
+ }
+ final NotedCallback notedCallback = new NotedCallback(callback, watchedUid,
+ callingUid, callingPid);
+ for (int op : ops) {
+ callbacks.put(op, notedCallback);
+ }
+ }
+ }
+
+ @Override
+ public void stopWatchingNoted(IAppOpsNotedCallback callback) {
+ Preconditions.checkNotNull(callback, "Callback cannot be null");
+ synchronized (this) {
+ final SparseArray<NotedCallback> notedCallbacks =
+ mNotedWatchers.remove(callback.asBinder());
+ if (notedCallbacks == null) {
+ return;
+ }
+ final int callbackCount = notedCallbacks.size();
+ for (int i = 0; i < callbackCount; i++) {
+ notedCallbacks.valueAt(i).destroy();
}
}
}
@@ -2052,6 +2148,51 @@
}
}
+ private void scheduleOpNotedIfNeededLocked(int code, int uid, String packageName,
+ int result) {
+ ArraySet<NotedCallback> dispatchedCallbacks = null;
+ final int callbackListCount = mNotedWatchers.size();
+ for (int i = 0; i < callbackListCount; i++) {
+ final SparseArray<NotedCallback> callbacks = mNotedWatchers.valueAt(i);
+ final NotedCallback callback = callbacks.get(code);
+ if (callback != null) {
+ if (callback.mWatchingUid >= 0 && callback.mWatchingUid != uid) {
+ continue;
+ }
+ if (dispatchedCallbacks == null) {
+ dispatchedCallbacks = new ArraySet<>();
+ }
+ dispatchedCallbacks.add(callback);
+ }
+ }
+ if (dispatchedCallbacks == null) {
+ return;
+ }
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ AppOpsService::notifyOpChecked,
+ this, dispatchedCallbacks, code, uid, packageName, result));
+ }
+
+ private void notifyOpChecked(ArraySet<NotedCallback> callbacks,
+ int code, int uid, String packageName, int result) {
+ // There are components watching for checks in our process. The callbacks in
+ // these components may require permissions our remote caller does not have.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final int callbackCount = callbacks.size();
+ for (int i = 0; i < callbackCount; i++) {
+ final NotedCallback callback = callbacks.valueAt(i);
+ try {
+ callback.mCallback.opNoted(code, uid, packageName, result);
+ } catch (RemoteException e) {
+ /* do nothing */
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
@Override
public int permissionToOpCode(String permission) {
if (permission == null) {
@@ -3463,6 +3604,46 @@
pw.println(cb);
}
}
+ if (mNotedWatchers.size() > 0 && dumpMode < 0) {
+ needSep = true;
+ boolean printedHeader = false;
+ for (int i = 0; i < mNotedWatchers.size(); i++) {
+ final SparseArray<NotedCallback> notedWatchers = mNotedWatchers.valueAt(i);
+ if (notedWatchers.size() <= 0) {
+ continue;
+ }
+ final NotedCallback cb = notedWatchers.valueAt(0);
+ if (dumpOp >= 0 && notedWatchers.indexOfKey(dumpOp) < 0) {
+ continue;
+ }
+ if (dumpPackage != null && cb.mWatchingUid >= 0
+ && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) {
+ continue;
+ }
+ if (!printedHeader) {
+ pw.println(" All op noted watchers:");
+ printedHeader = true;
+ }
+ pw.print(" ");
+ pw.print(Integer.toHexString(System.identityHashCode(
+ mNotedWatchers.keyAt(i))));
+ pw.println(" ->");
+ pw.print(" [");
+ final int opCount = notedWatchers.size();
+ for (i = 0; i < opCount; i++) {
+ if (i > 0) {
+ pw.print(' ');
+ }
+ pw.print(AppOpsManager.opToName(notedWatchers.keyAt(i)));
+ if (i < opCount - 1) {
+ pw.print(',');
+ }
+ }
+ pw.println("]");
+ pw.print(" ");
+ pw.println(cb);
+ }
+ }
if (mClients.size() > 0 && dumpMode < 0) {
needSep = true;
boolean printedHeader = false;
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index a07939e..7bbc543 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -76,6 +76,7 @@
import java.util.LinkedList;
import java.util.Locale;
import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -1276,7 +1277,11 @@
if (mService == null) {
return;
}
- mService.unlinkToDeath(this, 0);
+ try {
+ mService.unlinkToDeath(this, 0);
+ } catch (NoSuchElementException e) {
+ Log.e(TAG, "error unlinking to death", e);
+ }
mService = null;
mClassName = null;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 89194e4..66ceae4 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -5714,7 +5714,6 @@
// This should never fail. Specifying an already in use NetID will cause failure.
if (networkAgent.isVPN()) {
mNMS.createVirtualNetwork(networkAgent.network.netId,
- !networkAgent.linkProperties.getDnsServers().isEmpty(),
(networkAgent.networkMisc == null ||
!networkAgent.networkMisc.allowBypass));
} else {
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 4e8ef54..d33b617 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -302,12 +302,14 @@
AppOpsManager.OnOpChangedListener callback
= new AppOpsManager.OnOpChangedInternalListener() {
public void onOpChanged(int op, String packageName) {
- synchronized (mLock) {
- for (Receiver receiver : mReceivers.values()) {
- receiver.updateMonitoring(true);
- }
- applyAllProviderRequirementsLocked();
- }
+ mLocationHandler.post(() -> {
+ synchronized (mLock) {
+ for (Receiver receiver : mReceivers.values()) {
+ receiver.updateMonitoring(true);
+ }
+ applyAllProviderRequirementsLocked();
+ }
+ });
}
};
mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null,
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 8869af4..b0ca2df 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -2314,11 +2314,11 @@
}
@Override
- public void createVirtualNetwork(int netId, boolean hasDNS, boolean secure) {
+ public void createVirtualNetwork(int netId, boolean secure) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mNetdService.networkCreateVpn(netId, hasDNS, secure);
+ mNetdService.networkCreateVpn(netId, secure);
} catch (RemoteException | ServiceSpecificException e) {
throw new IllegalStateException(e);
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 6c62725..7adcaba 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1504,6 +1504,10 @@
public StorageManagerService(Context context) {
sSelf = this;
+ // Snapshot feature flag used for this boot
+ SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString(
+ SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)));
+
mContext = context;
mCallbacks = new Callbacks(FgThread.get().getLooper());
mLockPatternUtils = new LockPatternUtils(mContext);
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 1ef3e94..d07cf78 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -214,6 +214,10 @@
private PreciseCallState mPreciseCallState = new PreciseCallState();
+ private int mCallDisconnectCause = DisconnectCause.NOT_VALID;
+
+ private int mCallPreciseDisconnectCause = PreciseDisconnectCause.NOT_VALID;
+
private boolean mCarrierNetworkChangeState = false;
private PhoneCapability mPhoneCapability = null;
@@ -714,6 +718,14 @@
remove(r.binder);
}
}
+ if ((events & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) {
+ try {
+ r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause,
+ mCallPreciseDisconnectCause);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
if ((events & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) {
try {
r.callback.onPreciseDataConnectionStateChanged(
@@ -1491,9 +1503,8 @@
}
handleRemoveListLocked();
}
- broadcastPreciseCallStateChanged(ringingCallState, foregroundCallState, backgroundCallState,
- DisconnectCause.NOT_VALID,
- PreciseDisconnectCause.NOT_VALID);
+ broadcastPreciseCallStateChanged(ringingCallState, foregroundCallState,
+ backgroundCallState);
}
public void notifyDisconnectCause(int disconnectCause, int preciseDisconnectCause) {
@@ -1501,12 +1512,14 @@
return;
}
synchronized (mRecords) {
- mPreciseCallState = new PreciseCallState(mRingingCallState, mForegroundCallState,
- mBackgroundCallState, disconnectCause, preciseDisconnectCause);
+ mCallDisconnectCause = disconnectCause;
+ mCallPreciseDisconnectCause = preciseDisconnectCause;
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_PRECISE_CALL_STATE)) {
+ if (r.matchPhoneStateListenerEvent(PhoneStateListener
+ .LISTEN_CALL_DISCONNECT_CAUSES)) {
try {
- r.callback.onPreciseCallStateChanged(mPreciseCallState);
+ r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause,
+ mCallPreciseDisconnectCause);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
@@ -1514,8 +1527,6 @@
}
handleRemoveListLocked();
}
- broadcastPreciseCallStateChanged(mRingingCallState, mForegroundCallState,
- mBackgroundCallState, disconnectCause, preciseDisconnectCause);
}
public void notifyPreciseDataConnectionFailed(String reason, String apnType,
@@ -1737,6 +1748,8 @@
}
pw.println("mPreciseDataConnectionState=" + mPreciseDataConnectionState);
pw.println("mPreciseCallState=" + mPreciseCallState);
+ pw.println("mCallDisconnectCause=" + mCallDisconnectCause);
+ pw.println("mCallPreciseDisconnectCause=" + mCallPreciseDisconnectCause);
pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState);
pw.println("mRingingCallState=" + mRingingCallState);
pw.println("mForegroundCallState=" + mForegroundCallState);
@@ -1912,13 +1925,11 @@
}
private void broadcastPreciseCallStateChanged(int ringingCallState, int foregroundCallState,
- int backgroundCallState, int disconnectCause, int preciseDisconnectCause) {
+ int backgroundCallState) {
Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_CALL_STATE_CHANGED);
intent.putExtra(TelephonyManager.EXTRA_RINGING_CALL_STATE, ringingCallState);
intent.putExtra(TelephonyManager.EXTRA_FOREGROUND_CALL_STATE, foregroundCallState);
intent.putExtra(TelephonyManager.EXTRA_BACKGROUND_CALL_STATE, backgroundCallState);
- intent.putExtra(TelephonyManager.EXTRA_DISCONNECT_CAUSE, disconnectCause);
- intent.putExtra(TelephonyManager.EXTRA_PRECISE_DISCONNECT_CAUSE, preciseDisconnectCause);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
android.Manifest.permission.READ_PRECISE_PHONE_STATE);
}
@@ -2006,6 +2017,11 @@
+ "LISTEN_PREFERRED_DATA_SUBID_CHANGE");
}
+ if ((events & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
+ }
+
return true;
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 5afb90d..fe632e5 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1665,7 +1665,7 @@
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent,
- callerApp.uid, callerApp.processName);
+ callerApp.uid, callerApp.processName, callingPackage);
IBinder binder = connection.asBinder();
ArrayList<ConnectionRecord> clist = s.connections.get(binder);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 739dbbc..d114397 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2329,9 +2329,16 @@
fos.close();
long[] rssAfter = Process.getRss(pid);
long end = SystemClock.uptimeMillis();
+ long time = end - start;
EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name, action,
rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
- rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], end-start);
+ rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
+ lastCompactAction, lastCompactTime, msg.arg1, msg.arg2);
+ StatsLog.write(StatsLog.APP_COMPACTED, pid, name, pendingAction,
+ rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3],
+ rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time,
+ lastCompactAction, lastCompactTime, msg.arg1,
+ ActivityManager.processStateAmToProto(msg.arg2));
synchronized(ActivityManagerService.this) {
proc.lastCompactTime = end;
proc.lastCompactAction = pendingAction;
@@ -6284,7 +6291,7 @@
ContentProviderConnection incProviderCountLocked(ProcessRecord r,
final ContentProviderRecord cpr, IBinder externalProcessToken, int callingUid,
- String callingTag, boolean stable) {
+ String callingPackage, String callingTag, boolean stable) {
if (r != null) {
for (int i=0; i<r.conProviders.size(); i++) {
ContentProviderConnection conn = r.conProviders.get(i);
@@ -6304,7 +6311,7 @@
return conn;
}
}
- ContentProviderConnection conn = new ContentProviderConnection(cpr, r);
+ ContentProviderConnection conn = new ContentProviderConnection(cpr, r, callingPackage);
conn.startAssociationIfNeeded();
if (stable) {
conn.stableCount = 1;
@@ -6411,8 +6418,8 @@
}
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
- String name, IBinder token, int callingUid, String callingTag, boolean stable,
- int userId) {
+ String name, IBinder token, int callingUid, String callingPackage, String callingTag,
+ boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
@@ -6535,7 +6542,8 @@
// In this case the provider instance already exists, so we can
// return it right away.
- conn = incProviderCountLocked(r, cpr, token, callingUid, callingTag, stable);
+ conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
+ stable);
if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
// If this is a perceptible app accessing the provider,
@@ -6782,7 +6790,8 @@
}
mProviderMap.putProviderByName(name, cpr);
- conn = incProviderCountLocked(r, cpr, token, callingUid, callingTag, stable);
+ conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
+ stable);
if (conn != null) {
conn.waiting = true;
}
@@ -6924,7 +6933,8 @@
@Override
public final ContentProviderHolder getContentProvider(
- IApplicationThread caller, String name, int userId, boolean stable) {
+ IApplicationThread caller, String callingPackage, String name, int userId,
+ boolean stable) {
enforceNotIsolatedCaller("getContentProvider");
if (caller == null) {
String msg = "null IApplicationThread when getting content provider "
@@ -6934,8 +6944,14 @@
}
// The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
// with cross-user grant.
- return getContentProviderImpl(caller, name, null, Binder.getCallingUid(), null, stable,
- userId);
+ final int callingUid = Binder.getCallingUid();
+ if (callingPackage != null && mAppOpsService.checkPackage(callingUid, callingPackage)
+ != AppOpsManager.MODE_ALLOWED) {
+ throw new SecurityException("Given calling package " + callingPackage
+ + " does not match caller's uid " + callingUid);
+ }
+ return getContentProviderImpl(caller, name, null, callingUid, callingPackage,
+ null, stable, userId);
}
public ContentProviderHolder getContentProviderExternal(
@@ -6950,7 +6966,8 @@
private ContentProviderHolder getContentProviderExternalUnchecked(String name,
IBinder token, int callingUid, String callingTag, int userId) {
- return getContentProviderImpl(null, name, token, callingUid, callingTag, true, userId);
+ return getContentProviderImpl(null, name, token, callingUid, null, callingTag,
+ true, userId);
}
/**
@@ -17028,12 +17045,16 @@
app.curAdj == ProcessList.HOME_APP_ADJ)) {
app.reqCompactAction = COMPACT_PROCESS_SOME;
mPendingCompactionProcesses.add(app);
- mCompactionHandler.sendEmptyMessage(COMPACT_PROCESS_MSG);
+ mCompactionHandler.sendMessage(
+ mCompactionHandler.obtainMessage(
+ COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
} else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ &&
app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
app.reqCompactAction = COMPACT_PROCESS_FULL;
mPendingCompactionProcesses.add(app);
- mCompactionHandler.sendEmptyMessage(COMPACT_PROCESS_MSG);
+ mCompactionHandler.sendMessage(
+ mCompactionHandler.obtainMessage(
+ COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
}
}
ProcessList.setOomAdj(app.pid, app.uid, app.curAdj);
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index aa76b3d..af1031e 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -43,6 +43,7 @@
final PendingIntent clientIntent; // How to launch the client.
final int clientUid; // The identity of this connection's client
final String clientProcessName; // The source process of this connection's client
+ final String clientPackageName; // The source package of this connection's client
public AssociationState.SourceState association; // Association tracking
String stringName; // Caching of toString.
boolean serviceDead; // Well is it?
@@ -96,7 +97,7 @@
ActivityServiceConnectionsHolder<ConnectionRecord> _activity,
IServiceConnection _conn, int _flags,
int _clientLabel, PendingIntent _clientIntent,
- int _clientUid, String _clientProcessName) {
+ int _clientUid, String _clientProcessName, String _clientPackageName) {
binding = _binding;
activity = _activity;
conn = _conn;
@@ -105,6 +106,7 @@
clientIntent = _clientIntent;
clientUid = _clientUid;
clientProcessName = _clientProcessName;
+ clientPackageName = _clientPackageName;
}
public void startAssociationIfNeeded() {
@@ -125,7 +127,7 @@
} else {
association = holder.pkg.getAssociationStateLocked(holder.state,
binding.service.instanceName.getClassName()).startSource(clientUid,
- clientProcessName);
+ clientProcessName, clientPackageName);
}
}
diff --git a/services/core/java/com/android/server/am/ContentProviderConnection.java b/services/core/java/com/android/server/am/ContentProviderConnection.java
index f2d4f73..5f184c2 100644
--- a/services/core/java/com/android/server/am/ContentProviderConnection.java
+++ b/services/core/java/com/android/server/am/ContentProviderConnection.java
@@ -32,6 +32,7 @@
public final class ContentProviderConnection extends Binder {
public final ContentProviderRecord provider;
public final ProcessRecord client;
+ public final String clientPackage;
public AssociationState.SourceState association;
public final long createTime;
public int stableCount;
@@ -46,9 +47,11 @@
public int numStableIncs;
public int numUnstableIncs;
- public ContentProviderConnection(ContentProviderRecord _provider, ProcessRecord _client) {
+ public ContentProviderConnection(ContentProviderRecord _provider, ProcessRecord _client,
+ String _clientPackage) {
provider = _provider;
client = _client;
+ clientPackage = _clientPackage;
createTime = SystemClock.elapsedRealtime();
}
@@ -69,7 +72,8 @@
+ provider.name.toShortString() + ": proc=" + provider.proc);
} else {
association = holder.pkg.getAssociationStateLocked(holder.state,
- provider.name.getClassName()).startSource(client.uid, client.processName);
+ provider.name.getClassName()).startSource(client.uid, client.processName,
+ clientPackage);
}
}
diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java
index 2fc4adc..46dfc7c 100644
--- a/services/core/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/core/java/com/android/server/am/ContentProviderRecord.java
@@ -305,7 +305,7 @@
} else {
mAssociation = holder.pkg.getAssociationStateLocked(holder.state,
provider.name.getClassName()).startSource(mOwningUid,
- mOwningProcessName);
+ mOwningProcessName, null);
}
}
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index fa7a4c5..a71f6af 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -138,4 +138,4 @@
30061 am_remove_task (Task ID|1|5), (Stack ID|1|5)
# The task is being compacted
-30063 am_compact (Pid|1|5),(Process Name|3),(Action|3),(BeforeRssTotal|2|2),(BeforeRssFile|2|2),(BeforeRssAnon|2|2),(BeforeRssSwap|2|2),(AfterRssTotal|2|2),(AfterRssFile|2|2),(AfterRssAnon|2|2),(AfterRssSwap|2|2),(Time|2|3)
\ No newline at end of file
+30063 am_compact (Pid|1|5),(Process Name|3),(Action|3),(BeforeRssTotal|2|2),(BeforeRssFile|2|2),(BeforeRssAnon|2|2),(BeforeRssSwap|2|2),(AfterRssTotal|2|2),(AfterRssFile|2|2),(AfterRssAnon|2|2),(AfterRssSwap|2|2),(Time|2|3),(LastAction|1|2),(LastActionTimestamp|2|3),(setAdj|1|2),(procState|1|2)
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 4c4a090..d5ede5b 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -19,8 +19,10 @@
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Build;
import android.os.SystemProperties;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Slog;
@@ -61,13 +63,18 @@
// Add the global setting you want to push to native level as experiment flag into this list.
//
// NOTE: please grant write permission system property prefix
- // with format persist.experiment.[experiment_category_name]. in system_server.te and grant read
- // permission in the corresponding .te file your feature belongs to.
+ // with format persist.device_config.global_settings.[flag_name] in system_server.te and grant
+ // read permission in the corresponding .te file your feature belongs to.
@VisibleForTesting
static final String[] sGlobalSettings = new String[] {
Settings.Global.NATIVE_FLAGS_HEALTH_CHECK_ENABLED,
};
+ // All the flags under the listed DeviceConfig scopes will be synced to native level.
+ //
+ // NOTE: please grant write permission system property prefix
+ // with format persist.device_config.[device_config_scope]. in system_server.te and grant read
+ // permission in the corresponding .te file your feature belongs to.
@VisibleForTesting
static final String[] sDeviceConfigScopes = new String[] {
};
@@ -104,19 +111,31 @@
ContentObserver co = new ContentObserver(null) {
@Override
public void onChange(boolean selfChange) {
- updatePropertyFromSetting(globalSetting, propName, true);
+ updatePropertyFromSetting(globalSetting, propName);
}
};
// only updating on starting up when no native flags reset is performed during current
// booting.
if (!isNativeFlagsResetPerformed()) {
- updatePropertyFromSetting(globalSetting, propName, true);
+ updatePropertyFromSetting(globalSetting, propName);
}
mContentResolver.registerContentObserver(settingUri, false, co);
}
- // TODO: address sDeviceConfigScopes after DeviceConfig APIs are available.
+ for (String deviceConfigScope : mDeviceConfigScopes) {
+ DeviceConfig.addOnPropertyChangedListener(
+ deviceConfigScope,
+ AsyncTask.THREAD_POOL_EXECUTOR,
+ (String scope, String name, String value) -> {
+ String propertyName = makePropertyName(scope, name);
+ if (propertyName == null) {
+ log("unable to construct system property for " + scope + "/" + name);
+ return;
+ }
+ setProperty(propertyName, value);
+ });
+ }
}
public static SettingsToPropertiesMapper start(ContentResolver contentResolver) {
@@ -184,15 +203,6 @@
return propertyName;
}
- private String getSetting(String name, boolean isGlobalSetting) {
- if (isGlobalSetting) {
- return Settings.Global.getString(mContentResolver, name);
- } else {
- // TODO: complete the code after DeviceConfig APIs implemented.
- return null;
- }
- }
-
private void setProperty(String key, String value) {
// Check if need to clear the property
if (value == null) {
@@ -259,8 +269,8 @@
}
@VisibleForTesting
- void updatePropertyFromSetting(String settingName, String propName, boolean isGlobalSetting) {
- String settingValue = getSetting(settingName, isGlobalSetting);
+ void updatePropertyFromSetting(String settingName, String propName) {
+ String settingValue = Settings.Global.getString(mContentResolver, settingName);
setProperty(propName, settingValue);
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 67293b9..3552e66 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1685,6 +1685,7 @@
return getInputMethodList(false /* isVrOnly */);
}
+ @Override
public List<InputMethodInfo> getVrInputMethodList() {
return getInputMethodList(true /* isVrOnly */);
}
diff --git a/services/core/java/com/android/server/job/JobConcurrencyManager.java b/services/core/java/com/android/server/job/JobConcurrencyManager.java
index ee1d847..827c0f1 100644
--- a/services/core/java/com/android/server/job/JobConcurrencyManager.java
+++ b/services/core/java/com/android/server/job/JobConcurrencyManager.java
@@ -44,17 +44,11 @@
* We manipulate this array until we arrive at what jobs should be running on
* what JobServiceContext.
*/
- JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
+ JobStatus[] mRecycledAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
- /**
- * Indicates whether we need to act on this jobContext id
- */
- boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
+ boolean[] mRecycledSlotChanged = new boolean[MAX_JOB_CONTEXTS_COUNT];
- /**
- * The uid whose jobs we would like to assign to a context.
- */
- int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
+ int[] mRecycledPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
JobConcurrencyManager(JobSchedulerService service) {
mService = service;
@@ -99,28 +93,30 @@
break;
}
- JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
- boolean[] act = mTmpAssignAct;
- int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
- int numActive = 0;
- int numForeground = 0;
+ // To avoid GC churn, we recycle the arrays.
+ JobStatus[] contextIdToJobMap = mRecycledAssignContextIdToJobMap;
+ boolean[] slotChanged = mRecycledSlotChanged;
+ int[] preferredUidForContext = mRecycledPreferredUidForContext;
+
+ int numTotalRunningJobs = 0;
+ int numForegroundJobs = 0;
for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
final JobServiceContext js = mService.mActiveServices.get(i);
final JobStatus status = js.getRunningJobLocked();
if ((contextIdToJobMap[i] = status) != null) {
- numActive++;
+ numTotalRunningJobs++;
if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
- numForeground++;
+ numForegroundJobs++;
}
}
- act[i] = false;
+ slotChanged[i] = false;
preferredUidForContext[i] = js.getPreferredUid();
}
if (DEBUG) {
Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
}
for (int i=0; i<pendingJobs.size(); i++) {
- JobStatus nextPending = pendingJobs.get(i);
+ final JobStatus nextPending = pendingJobs.get(i);
// If job is already running, go to next job.
int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
@@ -131,23 +127,32 @@
final int priority = mService.evaluateJobPriorityLocked(nextPending);
nextPending.lastEvaluatedPriority = priority;
- // Find a context for nextPending. The context should be available OR
+ // Find an available slot for nextPending. The context should be available OR
// it should have lowest priority among all running jobs
// (sharing the same Uid as nextPending)
- int minPriority = Integer.MAX_VALUE;
- int minPriorityContextId = -1;
+ int minPriorityForPreemption = Integer.MAX_VALUE;
+ int selectedContextId = -1;
+ boolean startingJob = false;
for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
JobStatus job = contextIdToJobMap[j];
int preferredUid = preferredUidForContext[j];
if (job == null) {
- if ((numActive < mService.mMaxActiveJobs ||
- (priority >= JobInfo.PRIORITY_TOP_APP &&
- numForeground < mConstants.FG_JOB_COUNT)) &&
- (preferredUid == nextPending.getUid() ||
- preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
+ final boolean totalCountOk = numTotalRunningJobs < mService.mMaxActiveJobs;
+ final boolean fgCountOk = (priority >= JobInfo.PRIORITY_TOP_APP)
+ && (numForegroundJobs < mConstants.FG_JOB_COUNT);
+ final boolean preferredUidOkay = (preferredUid == nextPending.getUid())
+ || (preferredUid == JobServiceContext.NO_PREFERRED_UID);
+
+ // TODO: The following check is slightly wrong.
+ // Depending on how the pending jobs are sorted, we sometimes cap the total
+ // job count at mMaxActiveJobs (when all jobs are FG jobs), or
+ // at [mMaxActiveJobs + FG_JOB_COUNT] (when there are mMaxActiveJobs BG jobs
+ // and then FG_JOB_COUNT FG jobs.)
+ if ((totalCountOk || fgCountOk) && preferredUidOkay) {
// This slot is free, and we haven't yet hit the limit on
// concurrent jobs... we can just throw the job in to here.
- minPriorityContextId = j;
+ selectedContextId = j;
+ startingJob = true;
break;
}
// No job on this context, but nextPending can't run here because
@@ -158,30 +163,39 @@
if (job.getUid() != nextPending.getUid()) {
continue;
}
- if (mService.evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
+
+ final int jobPriority = mService.evaluateJobPriorityLocked(job);
+ if (jobPriority >= nextPending.lastEvaluatedPriority) {
continue;
}
- if (minPriority > nextPending.lastEvaluatedPriority) {
- minPriority = nextPending.lastEvaluatedPriority;
- minPriorityContextId = j;
+
+ // TODO lastEvaluatedPriority should be evaluateJobPriorityLocked. (double check it)
+ if (minPriorityForPreemption > nextPending.lastEvaluatedPriority) {
+ minPriorityForPreemption = nextPending.lastEvaluatedPriority;
+ selectedContextId = j;
+ // In this case, we're just going to preempt a low priority job, we're not
+ // actually starting a job, so don't set startingJob.
}
}
- if (minPriorityContextId != -1) {
- contextIdToJobMap[minPriorityContextId] = nextPending;
- act[minPriorityContextId] = true;
- numActive++;
+ if (selectedContextId != -1) {
+ contextIdToJobMap[selectedContextId] = nextPending;
+ slotChanged[selectedContextId] = true;
+ }
+ if (startingJob) {
+ // Increase the counters when we're going to start a job.
+ numTotalRunningJobs++;
if (priority >= JobInfo.PRIORITY_TOP_APP) {
- numForeground++;
+ numForegroundJobs++;
}
}
}
if (DEBUG) {
Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
}
- tracker.noteConcurrency(numActive, numForeground);
+ tracker.noteConcurrency(numTotalRunningJobs, numForegroundJobs);
for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
boolean preservePreferredUid = false;
- if (act[i]) {
+ if (slotChanged[i]) {
JobStatus js = activeServices.get(i).getRunningJobLocked();
if (js != null) {
if (DEBUG) {
@@ -195,7 +209,7 @@
final JobStatus pendingJob = contextIdToJobMap[i];
if (DEBUG) {
Slog.d(TAG, "About to run job on context "
- + String.valueOf(i) + ", job: " + pendingJob);
+ + i + ", job: " + pendingJob);
}
for (int ic=0; ic<controllers.size(); ic++) {
controllers.get(ic).prepareForExecutionLocked(pendingJob);
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 10dc156..3f9d928 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -1261,6 +1261,9 @@
@Override
public void onDeviceIdleStateChanged(boolean deviceIdle) {
synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "Doze state changed: " + deviceIdle);
+ }
if (deviceIdle) {
// When becoming idle, make sure no jobs are actively running,
// except those using the idle exemption flag.
@@ -1829,6 +1832,9 @@
}
} break;
case MSG_CHECK_JOB:
+ if (DEBUG) {
+ Slog.d(TAG, "MSG_CHECK_JOB");
+ }
if (mReportedActive) {
// if jobs are currently being run, queue all ready jobs for execution.
queueReadyJobsForExecutionLocked();
@@ -1838,6 +1844,9 @@
}
break;
case MSG_CHECK_JOB_GREEDY:
+ if (DEBUG) {
+ Slog.d(TAG, "MSG_CHECK_JOB_GREEDY");
+ }
queueReadyJobsForExecutionLocked();
break;
case MSG_STOP_JOB:
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 84bb13e..ee60daa 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -45,7 +45,12 @@
void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded);
void onNotificationDirectReplied(String key);
void onNotificationSettingsViewed(String key);
- void onNotificationSmartRepliesAdded(String key, int replyCount);
+
+ /**
+ * Notifies that smart replies and actions have been added to the UI.
+ */
+ void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount, int smartActionCount,
+ boolean generatedByAssistant);
/**
* Notifies a smart reply is sent.
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index bad259b..405edd2 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -762,7 +762,13 @@
.setType(MetricsEvent.TYPE_ACTION)
.setSubtype(actionIndex)
.addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank)
- .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count));
+ .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count)
+ .addTaggedData(MetricsEvent.NOTIFICATION_ACTION_IS_SMART,
+ (Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION
+ == action.getSemanticAction()) ? 1 : 0)
+ .addTaggedData(
+ MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED,
+ generatedByAssistant ? 1 : 0));
EventLogTags.writeNotificationActionClicked(key, actionIndex,
r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now),
nv.rank, nv.count);
@@ -837,15 +843,20 @@
if (DBG) Slog.d(TAG, "Marking notification as visible " + nv.key);
reportSeen(r);
- // If the newly visible notification has smart replies
+ // If the newly visible notification has smart suggestions
// then log that the user has seen them.
- if (r.getNumSmartRepliesAdded() > 0
+ if ((r.getNumSmartRepliesAdded() > 0 || r.getNumSmartActionsAdded() > 0)
&& !r.hasSeenSmartReplies()) {
r.setSeenSmartReplies(true);
LogMaker logMaker = r.getLogMaker()
.setCategory(MetricsEvent.SMART_REPLY_VISIBLE)
.addTaggedData(MetricsEvent.NOTIFICATION_SMART_REPLY_COUNT,
- r.getNumSmartRepliesAdded());
+ r.getNumSmartRepliesAdded())
+ .addTaggedData(MetricsEvent.NOTIFICATION_SMART_ACTION_COUNT,
+ r.getNumSmartActionsAdded())
+ .addTaggedData(
+ MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED,
+ r.getSuggestionsGeneratedByAssistant());
mMetricsLogger.write(logMaker);
}
}
@@ -908,11 +919,14 @@
}
@Override
- public void onNotificationSmartRepliesAdded(String key, int replyCount) {
+ public void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount,
+ int smartActionCount, boolean generatedByAssistant) {
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
- r.setNumSmartRepliesAdded(replyCount);
+ r.setNumSmartRepliesAdded(smartReplyCount);
+ r.setNumSmartActionsAdded(smartActionCount);
+ r.setSuggestionsGeneratedByAssistant(generatedByAssistant);
}
}
}
@@ -920,6 +934,7 @@
@Override
public void onNotificationSmartReplySent(String key, int replyIndex, CharSequence reply,
boolean generatedByAssistant) {
+
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 39451d4..89ec38d 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -178,6 +178,8 @@
private boolean mTextChanged;
private boolean mRecordedInterruption;
private int mNumberOfSmartRepliesAdded;
+ private int mNumberOfSmartActionsAdded;
+ private boolean mSuggestionsGeneratedByAssistant;
private boolean mHasSeenSmartReplies;
/**
* Whether this notification (and its channels) should be considered user locked. Used in
@@ -1140,6 +1142,22 @@
return mNumberOfSmartRepliesAdded;
}
+ public void setNumSmartActionsAdded(int noActions) {
+ mNumberOfSmartActionsAdded = noActions;
+ }
+
+ public int getNumSmartActionsAdded() {
+ return mNumberOfSmartActionsAdded;
+ }
+
+ public void setSuggestionsGeneratedByAssistant(boolean generatedByAssistant) {
+ mSuggestionsGeneratedByAssistant = generatedByAssistant;
+ }
+
+ public boolean getSuggestionsGeneratedByAssistant() {
+ return mSuggestionsGeneratedByAssistant;
+ }
+
public boolean hasSeenSmartReplies() {
return mHasSeenSmartReplies;
}
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 6d59827..16143d3 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -60,7 +60,6 @@
boolean createIdmap(@NonNull final PackageInfo targetPackage,
@NonNull final PackageInfo overlayPackage, int userId) {
- // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible
if (DEBUG) {
Slog.d(TAG, "create idmap for " + targetPackage.packageName + " and "
+ overlayPackage.packageName);
@@ -70,16 +69,19 @@
final String overlayPath = overlayPackage.applicationInfo.getBaseCodePath();
try {
if (FEATURE_FLAG_IDMAP2) {
- mIdmap2Service.createIdmap(targetPath, overlayPath, userId);
+ if (mIdmap2Service.verifyIdmap(overlayPath, userId)) {
+ return true;
+ }
+ return mIdmap2Service.createIdmap(targetPath, overlayPath, userId) != null;
} else {
mInstaller.idmap(targetPath, overlayPath, sharedGid);
+ return true;
}
} catch (Exception e) {
Slog.w(TAG, "failed to generate idmap for " + targetPath + " and "
+ overlayPath + ": " + e.getMessage());
return false;
}
- return true;
}
boolean removeIdmap(@NonNull final OverlayInfo oi, final int userId) {
@@ -88,15 +90,15 @@
}
try {
if (FEATURE_FLAG_IDMAP2) {
- mIdmap2Service.removeIdmap(oi.baseCodePath, userId);
+ return mIdmap2Service.removeIdmap(oi.baseCodePath, userId);
} else {
mInstaller.removeIdmap(oi.baseCodePath);
+ return true;
}
} catch (Exception e) {
Slog.w(TAG, "failed to remove idmap for " + oi.baseCodePath + ": " + e.getMessage());
return false;
}
- return true;
}
boolean idmapExists(@NonNull final OverlayInfo oi) {
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index d471904..a7f1146 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -223,8 +223,8 @@
public OverlayManagerService(@NonNull final Context context,
@NonNull final Installer installer) {
super(context);
- mSettingsFile =
- new AtomicFile(new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays");
+ mSettingsFile = new AtomicFile(
+ new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays");
mPackageManager = new PackageManagerHelper();
mUserManager = UserManagerService.getInstance();
IdmapManager im = new IdmapManager(installer);
@@ -717,9 +717,9 @@
final Map<String, List<String>> pendingChanges = new ArrayMap<>(targetPackageNames.size());
synchronized (mLock) {
final List<String> frameworkOverlays =
- mImpl.getEnabledOverlayPackageNames("android", userId);
- final int N = targetPackageNames.size();
- for (int i = 0; i < N; i++) {
+ mImpl.getEnabledOverlayPackageNames("android", userId);
+ final int n = targetPackageNames.size();
+ for (int i = 0; i < n; i++) {
final String targetPackageName = targetPackageNames.get(i);
List<String> list = new ArrayList<>();
if (!"android".equals(targetPackageName)) {
@@ -730,8 +730,8 @@
}
}
- final int N = targetPackageNames.size();
- for (int i = 0; i < N; i++) {
+ final int n = targetPackageNames.size();
+ for (int i = 0; i < n; i++) {
final String targetPackageName = targetPackageNames.get(i);
if (DEBUG) {
Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
@@ -789,7 +789,7 @@
if (!mSettingsFile.getBaseFile().exists()) {
return;
}
- try (final FileInputStream stream = mSettingsFile.openRead()) {
+ try (FileInputStream stream = mSettingsFile.openRead()) {
mSettings.restore(stream);
// We might have data for dying users if the device was
@@ -915,8 +915,8 @@
if (!verbose) {
int count = 0;
- final int N = mCache.size();
- for (int i = 0; i < N; i++) {
+ final int n = mCache.size();
+ for (int i = 0; i < n; i++) {
final int userId = mCache.keyAt(i);
count += mCache.get(userId).size();
}
@@ -929,8 +929,8 @@
return;
}
- final int N = mCache.size();
- for (int i = 0; i < N; i++) {
+ final int n = mCache.size();
+ for (int i = 0; i < n; i++) {
final int userId = mCache.keyAt(i);
pw.println(TAB1 + "User " + userId);
final HashMap<String, PackageInfo> map = mCache.get(userId);
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 112059d..b0d2704 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -55,8 +55,8 @@
*/
final class OverlayManagerServiceImpl {
// Flags to use in conjunction with updateState.
- private static final int FLAG_TARGET_IS_UPGRADING = 1<<0;
- private static final int FLAG_OVERLAY_IS_UPGRADING = 1<<1;
+ private static final int FLAG_TARGET_IS_UPGRADING = 1 << 0;
+ private static final int FLAG_OVERLAY_IS_UPGRADING = 1 << 1;
private final PackageManagerHelper mPackageManager;
private final IdmapManager mIdmapManager;
@@ -89,8 +89,8 @@
// a change in priority is only relevant for static RROs: specifically,
// a regular RRO should not have its state reset only because a change
// in priority
- if (theTruth.isStaticOverlayPackage() &&
- theTruth.overlayPriority != oldSettings.priority) {
+ if (theTruth.isStaticOverlayPackage()
+ && theTruth.overlayPriority != oldSettings.priority) {
return true;
}
return false;
@@ -294,8 +294,8 @@
final int userId, final int flags) {
boolean modified = false;
final List<OverlayInfo> ois = mSettings.getOverlaysForTarget(targetPackageName, userId);
- final int N = ois.size();
- for (int i = 0; i < N; i++) {
+ final int n = ois.size();
+ for (int i = 0; i < n; i++) {
final OverlayInfo oi = ois.get(i);
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName,
userId);
@@ -610,8 +610,8 @@
final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName,
userId);
final List<String> paths = new ArrayList<>(overlays.size());
- final int N = overlays.size();
- for (int i = 0; i < N; i++) {
+ final int n = overlays.size();
+ for (int i = 0; i < n; i++) {
final OverlayInfo oi = overlays.get(i);
if (oi.isEnabled()) {
paths.add(oi.packageName);
@@ -632,8 +632,8 @@
userId);
// Static RROs targeting to "android", ie framework-res.apk, are handled by native layers.
- if (targetPackage != null && overlayPackage != null &&
- !("android".equals(targetPackageName)
+ if (targetPackage != null && overlayPackage != null
+ && !("android".equals(targetPackageName)
&& overlayPackage.isStaticOverlayPackage())) {
mIdmapManager.createIdmap(targetPackage, overlayPackage, userId);
}
@@ -663,7 +663,7 @@
private @OverlayInfo.State int calculateNewState(@Nullable final PackageInfo targetPackage,
@Nullable final PackageInfo overlayPackage, final int userId, final int flags)
- throws OverlayManagerSettings.BadKeyException {
+ throws OverlayManagerSettings.BadKeyException {
if ((flags & FLAG_TARGET_IS_UPGRADING) != 0) {
return STATE_TARGET_UPGRADING;
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index 572d368..ee06746 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -290,21 +290,22 @@
return;
}
- final int N = mItems.size();
- for (int i = 0; i < N; i++) {
+ final int n = mItems.size();
+ for (int i = 0; i < n; i++) {
final SettingsItem item = mItems.get(i);
pw.println(item.mPackageName + ":" + item.getUserId() + " {");
pw.increaseIndent();
- pw.print("mPackageName.......: "); pw.println(item.mPackageName);
- pw.print("mUserId............: "); pw.println(item.getUserId());
- pw.print("mTargetPackageName.: "); pw.println(item.getTargetPackageName());
- pw.print("mBaseCodePath......: "); pw.println(item.getBaseCodePath());
- pw.print("mState.............: "); pw.println(OverlayInfo.stateToString(item.getState()));
- pw.print("mIsEnabled.........: "); pw.println(item.isEnabled());
- pw.print("mIsStatic..........: "); pw.println(item.isStatic());
- pw.print("mPriority..........: "); pw.println(item.mPriority);
- pw.print("mCategory..........: "); pw.println(item.mCategory);
+ pw.println("mPackageName.......: " + item.mPackageName);
+ pw.println("mUserId............: " + item.getUserId());
+ pw.println("mTargetPackageName.: " + item.getTargetPackageName());
+ pw.println("mBaseCodePath......: " + item.getBaseCodePath());
+ pw.println("mState.............: " + OverlayInfo.stateToString(item.getState()));
+ pw.println("mState.............: " + OverlayInfo.stateToString(item.getState()));
+ pw.println("mIsEnabled.........: " + item.isEnabled());
+ pw.println("mIsStatic..........: " + item.isStatic());
+ pw.println("mPriority..........: " + item.mPriority);
+ pw.println("mCategory..........: " + item.mCategory);
pw.decreaseIndent();
pw.println("}");
@@ -400,8 +401,8 @@
xml.startTag(null, TAG_OVERLAYS);
XmlUtils.writeIntAttribute(xml, ATTR_VERSION, CURRENT_VERSION);
- final int N = table.size();
- for (int i = 0; i < N; i++) {
+ final int n = table.size();
+ for (int i = 0; i < n; i++) {
final SettingsItem item = table.get(i);
persistRow(xml, item);
}
@@ -542,8 +543,8 @@
}
private int select(@NonNull final String packageName, final int userId) {
- final int N = mItems.size();
- for (int i = 0; i < N; i++) {
+ final int n = mItems.size();
+ for (int i = 0; i < n; i++) {
final SettingsItem item = mItems.get(i);
if (item.mUserId == userId && item.mPackageName.equals(packageName)) {
return i;
diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
index d576d33..e2b1cba 100644
--- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
+++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
@@ -121,8 +121,8 @@
for (final String targetPackageName : allOverlays.keySet()) {
out.println(targetPackageName);
List<OverlayInfo> overlaysForTarget = allOverlays.get(targetPackageName);
- final int N = overlaysForTarget.size();
- for (int i = 0; i < N; i++) {
+ final int n = overlaysForTarget.size();
+ for (int i = 0; i < n; i++) {
final OverlayInfo oi = overlaysForTarget.get(i);
String status;
switch (oi.state) {
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 51619cf..164af38 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -143,6 +143,13 @@
LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
}
+ private static final Set<String> ALWAYS_LOCATION_PERMISSIONS = new ArraySet<>();
+ static {
+ ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION);
+ ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
+ ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
+ }
+
private static final Set<String> ACTIVITY_RECOGNITION_PERMISSIONS = new ArraySet<>();
static {
ACTIVITY_RECOGNITION_PERMISSIONS.add(Manifest.permission.ACTIVITY_RECOGNITION);
@@ -690,7 +697,7 @@
// Companion devices
grantSystemFixedPermissionsToSystemPackage(
CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, userId,
- LOCATION_PERMISSIONS);
+ ALWAYS_LOCATION_PERMISSIONS);
// Ringtone Picker
grantSystemFixedPermissionsToSystemPackage(
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 6e4c00e..02d8c0b 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -1254,12 +1254,13 @@
}
@Override
- public void onNotificationSmartRepliesAdded(String key, int replyCount)
- throws RemoteException {
+ public void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount,
+ int smartActionCount, boolean generatedByAssistant) {
enforceStatusBarService();
long identity = Binder.clearCallingIdentity();
try {
- mNotificationDelegate.onNotificationSmartRepliesAdded(key, replyCount);
+ mNotificationDelegate.onNotificationSmartSuggestionsAdded(key, smartReplyCount,
+ smartActionCount, generatedByAssistant);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 2157c99..7c61e37 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -900,12 +900,21 @@
if (mLastWallpaper == null || mFallbackWallpaper == null) return;
final WallpaperConnection systemConnection = mLastWallpaper.connection;
final WallpaperConnection fallbackConnection = mFallbackWallpaper.connection;
+ if (fallbackConnection == null) {
+ Slog.w(TAG, "Fallback wallpaper connection has not been created yet!!");
+ return;
+ }
if (supportsMultiDisplay(systemConnection)
&& fallbackConnection.getConnectedEngineSize() != 0) {
fallbackConnection.forEachDisplayConnector(
WallpaperConnection.DisplayConnector::disconnectLocked);
fallbackConnection.mDisplayConnector.clear();
} else {
+ // TODO(b/121181553) Handle wallpaper service disconnect case.
+ if (fallbackConnection.mService == null) {
+ Slog.w(TAG, "There is no fallback wallpaper service");
+ return;
+ }
fallbackConnection.appendConnectorWithCondition(display ->
fallbackConnection.isUsableDisplay(display)
&& display.getDisplayId() != DEFAULT_DISPLAY
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 2663d99..6755c73 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -4914,7 +4914,6 @@
// Update override configurations of all tasks in the stack.
final Rect taskBounds = tempTaskBounds != null ? tempTaskBounds : bounds;
- final Rect insetBounds = tempTaskInsetBounds != null ? tempTaskInsetBounds : taskBounds;
mTmpBounds.clear();
mTmpInsetBounds.clear();
@@ -4922,7 +4921,7 @@
for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
final TaskRecord task = mTaskHistory.get(i);
if (task.isResizeable()) {
- task.updateOverrideConfiguration(taskBounds, insetBounds);
+ task.updateOverrideConfiguration(taskBounds, tempTaskInsetBounds);
}
if (task.hasDisplayedBounds()) {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 8b8cadc..6d3c693 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -505,23 +505,6 @@
// TODO: Make it can take screenshot on external display
mScreenshotHelper = displayContent.isDefaultDisplay
? new ScreenshotHelper(mContext) : null;
- }
-
- void systemReady() {
- mSystemGestures.systemReady();
- }
-
- private int getDisplayId() {
- return mDisplayContent.getDisplayId();
- }
-
- void onDisplayRemoved() {
- mDisplayContent.unregisterPointerEventListener(mSystemGestures);
- }
-
- void configure(int width, int height, int shortSizeDp) {
- // Allow the navigation bar to move on non-square small devices (phones).
- mNavigationBarCanMove = width != height && shortSizeDp < 600;
if (mDisplayContent.isDefaultDisplay) {
mHasStatusBar = true;
@@ -541,6 +524,23 @@
}
}
+ void systemReady() {
+ mSystemGestures.systemReady();
+ }
+
+ private int getDisplayId() {
+ return mDisplayContent.getDisplayId();
+ }
+
+ void onDisplayRemoved() {
+ mDisplayContent.unregisterPointerEventListener(mSystemGestures);
+ }
+
+ void configure(int width, int height, int shortSizeDp) {
+ // Allow the navigation bar to move on non-square small devices (phones).
+ mNavigationBarCanMove = width != height && shortSizeDp < 600;
+ }
+
void updateConfigurationDependentBehaviors() {
mNavBarOpacityMode = mContext.getResources().getInteger(R.integer.config_navBarOpacityMode);
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 801e5f2..dcade2f0 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -794,8 +794,9 @@
private void handleResizingWindows() {
for (int i = mWmService.mResizingWindows.size() - 1; i >= 0; i--) {
WindowState win = mWmService.mResizingWindows.get(i);
- if (win.mAppFreezing) {
- // Don't remove this window until rotation has completed.
+ if (win.mAppFreezing || win.getDisplayContent().mWaitingForConfig) {
+ // Don't remove this window until rotation has completed and is not waiting for the
+ // complete configuration.
continue;
}
win.reportResized();
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 5bb6440..8c80009 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -1716,7 +1716,7 @@
updateTaskDescription();
}
- private void adjustForMinimalTaskDimensions(Rect bounds) {
+ private void adjustForMinimalTaskDimensions(Rect bounds, Rect previousBounds) {
if (bounds == null) {
return;
}
@@ -1747,9 +1747,8 @@
return;
}
- final Rect configBounds = getRequestedOverrideBounds();
if (adjustWidth) {
- if (!configBounds.isEmpty() && bounds.right == configBounds.right) {
+ if (!previousBounds.isEmpty() && bounds.right == previousBounds.right) {
bounds.left = bounds.right - minWidth;
} else {
// Either left bounds match, or neither match, or the previous bounds were
@@ -1758,7 +1757,7 @@
}
}
if (adjustHeight) {
- if (!configBounds.isEmpty() && bounds.bottom == configBounds.bottom) {
+ if (!previousBounds.isEmpty() && bounds.bottom == previousBounds.bottom) {
bounds.top = bounds.bottom - minHeight;
} else {
// Either top bounds match, or neither match, or the previous bounds were
@@ -2113,11 +2112,15 @@
// TODO(b/113900640): remove this once ActivityRecord is changed to not need it anymore.
void computeResolvedOverrideConfiguration(Configuration inOutConfig, Configuration parentConfig,
Configuration overrideConfig) {
+ // Save previous bounds because adjustForMinimalTaskDimensions uses that to determine if it
+ // changes left bound vs. right bound, or top bound vs. bottom bound.
+ mTmpBounds.set(inOutConfig.windowConfiguration.getBounds());
+
inOutConfig.setTo(overrideConfig);
Rect outOverrideBounds = inOutConfig.windowConfiguration.getBounds();
if (outOverrideBounds != null && !outOverrideBounds.isEmpty()) {
- adjustForMinimalTaskDimensions(outOverrideBounds);
+ adjustForMinimalTaskDimensions(outOverrideBounds, mTmpBounds);
int windowingMode = overrideConfig.windowConfiguration.getWindowingMode();
if (windowingMode == WINDOWING_MODE_UNDEFINED) {
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 0c9c85a..9159f0d 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -41,7 +41,8 @@
LOCAL_MODULE := FrameworksServicesRoboTests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+ $(call all-java-files-under, backup/src)
LOCAL_RESOURCE_DIR := \
$(LOCAL_PATH)/res
@@ -81,4 +82,13 @@
LOCAL_TEST_PACKAGE := FrameworksServicesLib
-include external/robolectric-shadows/run_robotests.mk
\ No newline at end of file
+LOCAL_ROBOTEST_FILES := $(call find-files-in-subdirs,$(LOCAL_PATH)/src,*Test.java,.) \
+ $(call find-files-in-subdirs,$(LOCAL_PATH)/backup/src,*Test.java,.)
+
+include external/robolectric-shadows/run_robotests.mk
+
+###################################################################
+# include subdir Android.mk files
+###################################################################
+include $(CLEAR_VARS)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/services/robotests/backup/Android.mk b/services/robotests/backup/Android.mk
new file mode 100644
index 0000000..cc59b0c
--- /dev/null
+++ b/services/robotests/backup/Android.mk
@@ -0,0 +1,84 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+###################################################################
+# BackupFrameworksServicesLib app just for Robolectric test target #
+###################################################################
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := BackupFrameworksServicesLib
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ bmgrlib \
+ bu \
+ services.backup \
+ services.core \
+ services.net
+
+include $(BUILD_PACKAGE)
+
+###################################################################
+# BackupFrameworksServicesLib Robolectric test target. #
+###################################################################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := BackupFrameworksServicesRoboTests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+ $(call all-java-files-under, ../src/com/android/server/testing/shadows)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_JAVA_RESOURCE_DIRS := config
+
+# Include the testing libraries
+LOCAL_JAVA_LIBRARIES := \
+ platform-test-annotations \
+ robolectric_android-all-stub \
+ Robolectric_all-target \
+ mockito-robolectric-prebuilt \
+ truth-prebuilt \
+ testng
+
+LOCAL_INSTRUMENTATION_FOR := BackupFrameworksServicesLib
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+###################################################################
+# BackupFrameworksServicesLib runner target to run the previous target. #
+###################################################################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := RunBackupFrameworksServicesRoboTests
+
+LOCAL_JAVA_LIBRARIES := \
+ BackupFrameworksServicesRoboTests \
+ platform-test-annotations \
+ robolectric_android-all-stub \
+ Robolectric_all-target \
+ mockito-robolectric-prebuilt \
+ truth-prebuilt \
+ testng
+
+LOCAL_TEST_PACKAGE := BackupFrameworksServicesLib
+
+include external/robolectric-shadows/run_robotests.mk
diff --git a/services/robotests/backup/config/robolectric.properties b/services/robotests/backup/config/robolectric.properties
new file mode 100644
index 0000000..850557a
--- /dev/null
+++ b/services/robotests/backup/config/robolectric.properties
@@ -0,0 +1 @@
+sdk=NEWEST_SDK
\ No newline at end of file
diff --git a/services/robotests/src/android/app/backup/BackupUtilsTest.java b/services/robotests/backup/src/android/app/backup/BackupUtilsTest.java
similarity index 100%
rename from services/robotests/src/android/app/backup/BackupUtilsTest.java
rename to services/robotests/backup/src/android/app/backup/BackupUtilsTest.java
diff --git a/services/robotests/src/android/app/backup/ForwardingBackupAgent.java b/services/robotests/backup/src/android/app/backup/ForwardingBackupAgent.java
similarity index 100%
rename from services/robotests/src/android/app/backup/ForwardingBackupAgent.java
rename to services/robotests/backup/src/android/app/backup/ForwardingBackupAgent.java
diff --git a/services/robotests/src/com/android/commands/bmgr/BmgrTest.java b/services/robotests/backup/src/com/android/commands/bmgr/BmgrTest.java
similarity index 100%
rename from services/robotests/src/com/android/commands/bmgr/BmgrTest.java
rename to services/robotests/backup/src/com/android/commands/bmgr/BmgrTest.java
diff --git a/services/robotests/src/com/android/commands/bu/AdbBackupTest.java b/services/robotests/backup/src/com/android/commands/bu/AdbBackupTest.java
similarity index 100%
rename from services/robotests/src/com/android/commands/bu/AdbBackupTest.java
rename to services/robotests/backup/src/com/android/commands/bu/AdbBackupTest.java
diff --git a/services/robotests/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java b/services/robotests/backup/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java
rename to services/robotests/backup/src/com/android/server/backup/BackupAgentTimeoutParametersTest.java
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerConstantsTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/BackupManagerConstantsTest.java
rename to services/robotests/backup/src/com/android/server/backup/BackupManagerConstantsTest.java
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
new file mode 100644
index 0000000..83f66c5
--- /dev/null
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -0,0 +1,1480 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startBackupThread;
+import static com.android.server.backup.testing.TransportData.backupTransport;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.robolectric.Shadows.shadowOf;
+import static org.testng.Assert.expectThrows;
+
+import android.annotation.UserIdInt;
+import android.app.Application;
+import android.app.backup.IBackupManagerMonitor;
+import android.app.backup.IBackupObserver;
+import android.app.backup.IFullBackupRestoreObserver;
+import android.app.backup.ISelectBackupTransportCallback;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import android.util.SparseArray;
+
+import com.android.server.backup.testing.TransportData;
+import com.android.server.testing.shadows.ShadowBinder;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowContextWrapper;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBinder.class})
+@Presubmit
+public class BackupManagerServiceTest {
+ private static final String TEST_PACKAGE = "package";
+ private static final String TEST_TRANSPORT = "transport";
+ private static final String[] ADB_TEST_PACKAGES = {TEST_PACKAGE};
+
+ private ShadowContextWrapper mShadowContext;
+ private Context mContext;
+ @UserIdInt private int mUserOneId;
+ @UserIdInt private int mUserTwoId;
+ @Mock private UserBackupManagerService mUserOneService;
+ @Mock private UserBackupManagerService mUserTwoService;
+
+ /** Initialize {@link BackupManagerService}. */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ Application application = RuntimeEnvironment.application;
+ mContext = application;
+ mShadowContext = shadowOf(application);
+
+ // TODO(b/120212806): Hardcoding system user for now since most methods in BMS don't yet
+ // take an user parameter (and instead hardcode the system user).
+ mUserOneId = UserHandle.USER_SYSTEM;
+ mUserTwoId = mUserOneId + 1;
+ }
+
+ /**
+ * Clean up and reset state that was created for testing {@link BackupManagerService}
+ * operations.
+ */
+ @After
+ public void tearDown() throws Exception {
+ ShadowBinder.reset();
+ }
+
+ /**
+ * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}. This is
+ * specifically to prevent overloading the logs in production.
+ */
+ @Test
+ public void testMoreDebug_isFalse() throws Exception {
+ boolean moreDebug = BackupManagerService.MORE_DEBUG;
+
+ assertThat(moreDebug).isFalse();
+ }
+
+ /** Test that the constructor does not create {@link UserBackupManagerService} instances. */
+ @Test
+ public void testConstructor_doesNotRegisterUsers() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ assertThat(backupManagerService.getServiceUsers().size()).isEqualTo(0);
+ }
+
+ /** Test that the constructor handles {@code null} parameters. */
+ @Test
+ public void testConstructor_withNullContext_throws() throws Exception {
+ expectThrows(
+ NullPointerException.class,
+ () ->
+ new BackupManagerService(
+ /* context */ null,
+ new Trampoline(mContext),
+ startBackupThread(null)));
+ }
+
+ /** Test that the constructor handles {@code null} parameters. */
+ @Test
+ public void testConstructor_withNullTrampoline_throws() throws Exception {
+ expectThrows(
+ NullPointerException.class,
+ () ->
+ new BackupManagerService(
+ mContext, /* trampoline */ null, startBackupThread(null)));
+ }
+
+ /** Test that the constructor handles {@code null} parameters. */
+ @Test
+ public void testConstructor_withNullBackupThread_throws() throws Exception {
+ expectThrows(
+ NullPointerException.class,
+ () ->
+ new BackupManagerService(
+ mContext, new Trampoline(mContext), /* backupThread */ null));
+ }
+
+ /** Test that the service registers users. */
+ @Test
+ public void testStartServiceForUser_registersUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.startServiceForUser(mUserOneId);
+
+ SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers();
+ assertThat(serviceUsers.size()).isEqualTo(1);
+ assertThat(serviceUsers.get(mUserOneId)).isNotNull();
+ }
+
+ /** Test that the service registers users. */
+ @Test
+ public void testStartServiceForUser_withServiceInstance_registersUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.startServiceForUser(mUserOneId, mUserOneService);
+
+ SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers();
+ assertThat(serviceUsers.size()).isEqualTo(1);
+ assertThat(serviceUsers.get(mUserOneId)).isEqualTo(mUserOneService);
+ }
+
+ // TODO(b/120212806): When BMS methods take in a user parameter, modify unknown user tests to
+ // check that that we don't call the method on another registered user. Currently these tests
+ // have no registered users since we hardcode the system user in BMS.
+
+ // ---------------------------------------------
+ // Backup agent tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testDataChanged_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.dataChanged(TEST_PACKAGE);
+
+ verify(mUserOneService).dataChanged(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testDataChanged_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.dataChanged(TEST_PACKAGE);
+
+ verify(mUserOneService, never()).dataChanged(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testAgentConnected_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ IBinder agentBinder = mock(IBinder.class);
+
+ backupManagerService.agentConnected(TEST_PACKAGE, agentBinder);
+
+ verify(mUserOneService).agentConnected(TEST_PACKAGE, agentBinder);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testAgentConnected_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ IBinder agentBinder = mock(IBinder.class);
+
+ backupManagerService.agentConnected(TEST_PACKAGE, agentBinder);
+
+ verify(mUserOneService, never()).agentConnected(TEST_PACKAGE, agentBinder);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testAgentDisconnected_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.agentDisconnected(TEST_PACKAGE);
+
+ verify(mUserOneService).agentDisconnected(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testAgentDisconnected_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.agentDisconnected(TEST_PACKAGE);
+
+ verify(mUserOneService, never()).agentDisconnected(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testOpComplete_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.opComplete(/* token */ 0, /* result */ 0L);
+
+ verify(mUserOneService).opComplete(/* token */ 0, /* result */ 0L);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testOpComplete_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.opComplete(/* token */ 0, /* result */ 0L);
+
+ verify(mUserOneService, never()).opComplete(/* token */ 0, /* result */ 0L);
+ }
+
+ // ---------------------------------------------
+ // Transport tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testInitializeTransports_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ String[] transports = {TEST_TRANSPORT};
+
+ backupManagerService.initializeTransports(transports, /* observer */ null);
+
+ verify(mUserOneService).initializeTransports(transports, /* observer */ null);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testInitializeTransports_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ String[] transports = {TEST_TRANSPORT};
+
+ backupManagerService.initializeTransports(transports, /* observer */ null);
+
+ verify(mUserOneService, never()).initializeTransports(transports, /* observer */ null);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testClearBackupData_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+
+ verify(mUserOneService).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testClearBackupData_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+
+ verify(mUserOneService, never()).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetCurrentTransport_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.getCurrentTransport();
+
+ verify(mUserOneService).getCurrentTransport();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetCurrentTransport_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.getCurrentTransport();
+
+ verify(mUserOneService, never()).getCurrentTransport();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetCurrentTransportComponent_onRegisteredUser_callsMethodForUser()
+ throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.getCurrentTransportComponent();
+
+ verify(mUserOneService).getCurrentTransportComponent();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetCurrentTransportComponent_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.getCurrentTransportComponent();
+
+ verify(mUserOneService, never()).getCurrentTransportComponent();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testListAllTransports_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.listAllTransports();
+
+ verify(mUserOneService).listAllTransports();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testListAllTransports_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.listAllTransports();
+
+ verify(mUserOneService, never()).listAllTransports();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testListAllTransportComponents_onRegisteredUser_callsMethodForUser()
+ throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.listAllTransportComponents();
+
+ verify(mUserOneService).listAllTransportComponents();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testListAllTransportComponents_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.listAllTransportComponents();
+
+ verify(mUserOneService, never()).listAllTransportComponents();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetTransportWhitelist_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.getTransportWhitelist();
+
+ verify(mUserOneService).getTransportWhitelist();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetTransportWhitelist_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.getTransportWhitelist();
+
+ verify(mUserOneService, never()).getTransportWhitelist();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testUpdateTransportAttributes_onRegisteredUser_callsMethodForUser()
+ throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ TransportData transport = backupTransport();
+ Intent configurationIntent = new Intent();
+ Intent dataManagementIntent = new Intent();
+
+ backupManagerService.updateTransportAttributes(
+ transport.getTransportComponent(),
+ transport.transportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
+
+ verify(mUserOneService)
+ .updateTransportAttributes(
+ transport.getTransportComponent(),
+ transport.transportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testUpdateTransportAttributes_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+ TransportData transport = backupTransport();
+ Intent configurationIntent = new Intent();
+ Intent dataManagementIntent = new Intent();
+
+ backupManagerService.updateTransportAttributes(
+ transport.getTransportComponent(),
+ transport.transportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
+
+ verify(mUserOneService, never())
+ .updateTransportAttributes(
+ transport.getTransportComponent(),
+ transport.transportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSelectBackupTransport_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.selectBackupTransport(TEST_TRANSPORT);
+
+ verify(mUserOneService).selectBackupTransport(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testSelectBackupTransport_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.selectBackupTransport(TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).selectBackupTransport(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSelectTransportAsync_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ TransportData transport = backupTransport();
+ ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+ backupManagerService.selectBackupTransportAsync(
+ transport.getTransportComponent(), callback);
+
+ verify(mUserOneService)
+ .selectBackupTransportAsync(transport.getTransportComponent(), callback);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testSelectBackupTransportAsync_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+ TransportData transport = backupTransport();
+ ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+ backupManagerService.selectBackupTransportAsync(
+ transport.getTransportComponent(), callback);
+
+ verify(mUserOneService, never())
+ .selectBackupTransportAsync(transport.getTransportComponent(), callback);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetConfigurationIntent_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.getConfigurationIntent(TEST_TRANSPORT);
+
+ verify(mUserOneService).getConfigurationIntent(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetConfigurationIntent_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.getConfigurationIntent(TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).getConfigurationIntent(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetDestinationString_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.getDestinationString(TEST_TRANSPORT);
+
+ verify(mUserOneService).getDestinationString(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetDestinationString_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.getDestinationString(TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).getDestinationString(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetDataManagementIntent_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.getDataManagementIntent(TEST_TRANSPORT);
+
+ verify(mUserOneService).getDataManagementIntent(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetDataManagementIntent_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.getDataManagementIntent(TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).getDataManagementIntent(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetDataManagementLabel_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.getDataManagementLabel(TEST_TRANSPORT);
+
+ verify(mUserOneService).getDataManagementLabel(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetDataManagementLabel_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.getDataManagementLabel(TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).getDataManagementLabel(TEST_TRANSPORT);
+ }
+
+ // ---------------------------------------------
+ // Settings tests
+ // ---------------------------------------------
+
+ /**
+ * Test that the backup services throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testSetBackupEnabled_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ expectThrows(
+ SecurityException.class,
+ () -> backupManagerService.setBackupEnabled(mUserTwoId, true));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testSetBackupEnabled_withPermission_propagatesForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.setBackupEnabled(mUserTwoId, true);
+
+ verify(mUserTwoService).setBackupEnabled(true);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSetBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.setBackupEnabled(mUserOneId, true);
+
+ verify(mUserOneService).setBackupEnabled(true);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testSetBackupEnabled_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.setBackupEnabled(mUserTwoId, true);
+
+ verify(mUserOneService, never()).setBackupEnabled(true);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSetAutoRestore_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.setAutoRestore(true);
+
+ verify(mUserOneService).setAutoRestore(true);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testSetAutoRestore_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.setAutoRestore(true);
+
+ verify(mUserOneService, never()).setAutoRestore(true);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSetBackupProvisioned_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.setBackupProvisioned(true);
+
+ verify(mUserOneService).setBackupProvisioned(true);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testSetBackupProvisioned_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.setBackupProvisioned(true);
+
+ verify(mUserOneService, never()).setBackupProvisioned(true);
+ }
+
+ /**
+ * Test that the backup services throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testIsBackupEnabled_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ expectThrows(
+ SecurityException.class, () -> backupManagerService.isBackupEnabled(mUserTwoId));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testIsBackupEnabled_withPermission_propagatesForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.isBackupEnabled(mUserTwoId);
+
+ verify(mUserTwoService).isBackupEnabled();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testIsBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.isBackupEnabled(mUserOneId);
+
+ verify(mUserOneService).isBackupEnabled();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testIsBackupEnabled_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.isBackupEnabled(mUserTwoId);
+
+ verify(mUserOneService, never()).isBackupEnabled();
+ }
+
+ // ---------------------------------------------
+ // Backup tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testIsAppEligibleForBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.isAppEligibleForBackup(TEST_PACKAGE);
+
+ verify(mUserOneService).isAppEligibleForBackup(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testIsAppEligibleForBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.isAppEligibleForBackup(TEST_PACKAGE);
+
+ verify(mUserOneService, never()).isAppEligibleForBackup(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testFilterAppsEligibleForBackup_onRegisteredUser_callsMethodForUser()
+ throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ String[] packages = {TEST_PACKAGE};
+
+ backupManagerService.filterAppsEligibleForBackup(packages);
+
+ verify(mUserOneService).filterAppsEligibleForBackup(packages);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testFilterAppsEligibleForBackup_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+ String[] packages = {TEST_PACKAGE};
+
+ backupManagerService.filterAppsEligibleForBackup(packages);
+
+ verify(mUserOneService, never()).filterAppsEligibleForBackup(packages);
+ }
+
+ /**
+ * Test verifying that {@link BackupManagerService#backupNow(int)} throws a {@link
+ * SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+ */
+ @Test
+ public void testBackupNow_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ expectThrows(SecurityException.class, () -> backupManagerService.backupNow(mUserTwoId));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testBackupNow_withPermission_propagatesForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.backupNow(mUserTwoId);
+
+ verify(mUserTwoService).backupNow();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testBackupNow_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.backupNow(mUserOneId);
+
+ verify(mUserOneService).backupNow();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testBackupNow_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.backupNow(mUserTwoId);
+
+ verify(mUserOneService, never()).backupNow();
+ }
+
+ /**
+ * Test that the backup services throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testRequestBackup_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+ String[] packages = {TEST_PACKAGE};
+ IBackupObserver observer = mock(IBackupObserver.class);
+ IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.requestBackup(
+ mUserTwoId, packages, observer, monitor, 0));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testRequestBackup_withPermission_propagatesForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+ String[] packages = {TEST_PACKAGE};
+ IBackupObserver observer = mock(IBackupObserver.class);
+ IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
+
+ verify(mUserTwoService).requestBackup(packages, observer, monitor, /* flags */ 0);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testRequestBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ String[] packages = {TEST_PACKAGE};
+ IBackupObserver observer = mock(IBackupObserver.class);
+ IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.requestBackup(mUserOneId, packages, observer, monitor, /* flags */ 0);
+
+ verify(mUserOneService).requestBackup(packages, observer, monitor, /* flags */ 0);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testRequestBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ String[] packages = {TEST_PACKAGE};
+ IBackupObserver observer = mock(IBackupObserver.class);
+ IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
+
+ verify(mUserOneService, never()).requestBackup(packages, observer, monitor, /* flags */ 0);
+ }
+
+ /**
+ * Test verifying that {@link BackupManagerService#cancelBackups(int)} throws a {@link
+ * SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+ */
+ @Test
+ public void testCancelBackups_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ expectThrows(SecurityException.class, () -> backupManagerService.cancelBackups(mUserTwoId));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testCancelBackups_withPermission_propagatesForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.cancelBackups(mUserTwoId);
+
+ verify(mUserTwoService).cancelBackups();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testCancelBackups_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.cancelBackups(mUserOneId);
+
+ verify(mUserOneService).cancelBackups();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testCancelBackups_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.cancelBackups(mUserTwoId);
+
+ verify(mUserOneService, never()).cancelBackups();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testBeginFullBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ FullBackupJob job = new FullBackupJob();
+
+ backupManagerService.beginFullBackup(job);
+
+ verify(mUserOneService).beginFullBackup(job);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testBeginFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ FullBackupJob job = new FullBackupJob();
+
+ backupManagerService.beginFullBackup(job);
+
+ verify(mUserOneService, never()).beginFullBackup(job);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testEndFullBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.endFullBackup();
+
+ verify(mUserOneService).endFullBackup();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testEndFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.endFullBackup();
+
+ verify(mUserOneService, never()).endFullBackup();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testFullTransportBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ String[] packages = {TEST_PACKAGE};
+
+ backupManagerService.fullTransportBackup(packages);
+
+ verify(mUserOneService).fullTransportBackup(packages);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testFullTransportBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ String[] packages = {TEST_PACKAGE};
+
+ backupManagerService.fullTransportBackup(packages);
+
+ verify(mUserOneService, never()).fullTransportBackup(packages);
+ }
+
+ // ---------------------------------------------
+ // Restore tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testRestoreAtInstall_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+
+ verify(mUserOneService).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testRestoreAtInstall_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+
+ verify(mUserOneService, never()).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testBeginRestoreSession_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+
+ verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testBeginRestoreSession_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetAvailableRestoreToken_onRegisteredUser_callsMethodForUser()
+ throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.getAvailableRestoreToken(TEST_PACKAGE);
+
+ verify(mUserOneService).getAvailableRestoreToken(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetAvailableRestoreToken_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.getAvailableRestoreToken(TEST_PACKAGE);
+
+ verify(mUserOneService, never()).getAvailableRestoreToken(TEST_PACKAGE);
+ }
+
+ // ---------------------------------------------
+ // Adb backup/restore tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSetBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.setBackupPassword("currentPassword", "newPassword");
+
+ verify(mUserOneService).setBackupPassword("currentPassword", "newPassword");
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testSetBackupPassword_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.setBackupPassword("currentPassword", "newPassword");
+
+ verify(mUserOneService, never()).setBackupPassword("currentPassword", "newPassword");
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testHasBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+
+ backupManagerService.hasBackupPassword();
+
+ verify(mUserOneService).hasBackupPassword();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testHasBackupPassword_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.hasBackupPassword();
+
+ verify(mUserOneService, never()).hasBackupPassword();
+ }
+
+ /**
+ * Test that the backup services throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testAdbBackup_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.adbBackup(
+ mUserTwoId,
+ /* parcelFileDescriptor*/ null,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ null));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testAdbBackup_withPermission_propagatesForNonCallingUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.adbBackup(
+ mUserTwoId,
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ ADB_TEST_PACKAGES);
+
+ verify(mUserTwoService)
+ .adbBackup(
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ ADB_TEST_PACKAGES);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testAdbBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.adbBackup(
+ mUserOneId,
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ ADB_TEST_PACKAGES);
+
+ verify(mUserOneService)
+ .adbBackup(
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ ADB_TEST_PACKAGES);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testAdbBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.adbBackup(
+ mUserTwoId,
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ ADB_TEST_PACKAGES);
+
+ verify(mUserOneService, never())
+ .adbBackup(
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ ADB_TEST_PACKAGES);
+ }
+
+ /**
+ * Test that the backup services throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testAdbRestore_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ expectThrows(
+ SecurityException.class, () -> backupManagerService.adbRestore(mUserTwoId, null));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testAdbRestore_withPermission_propagatesForNonCallingUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.adbRestore(mUserTwoId, parcelFileDescriptor);
+
+ verify(mUserTwoService).adbRestore(parcelFileDescriptor);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testAdbRestore_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.adbRestore(mUserOneId, parcelFileDescriptor);
+
+ verify(mUserOneService).adbRestore(parcelFileDescriptor);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testAdbRestore_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.adbRestore(mUserTwoId, parcelFileDescriptor);
+
+ verify(mUserOneService, never()).adbRestore(parcelFileDescriptor);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testAcknowledgeAdbBackupOrRestore_onRegisteredUser_callsMethodForUser()
+ throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
+
+ backupManagerService.acknowledgeAdbBackupOrRestore(
+ /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer);
+
+ verify(mUserOneService)
+ .acknowledgeAdbBackupOrRestore(
+ /* token */ 0,
+ /* allow */ true,
+ "currentPassword",
+ "encryptionPassword",
+ observer);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testAcknowledgeAdbBackupOrRestore_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+ IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
+
+ backupManagerService.acknowledgeAdbBackupOrRestore(
+ /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer);
+
+ verify(mUserOneService, never())
+ .acknowledgeAdbBackupOrRestore(
+ /* token */ 0,
+ /* allow */ true,
+ "currentPassword",
+ "encryptionPassword",
+ observer);
+ }
+
+ // ---------------------------------------------
+ // Service tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testDump_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ File testFile = new File(mContext.getFilesDir(), "test");
+ testFile.createNewFile();
+ FileDescriptor fileDescriptor = new FileDescriptor();
+ PrintWriter printWriter = new PrintWriter(testFile);
+ String[] args = {"1", "2"};
+
+ backupManagerService.dump(fileDescriptor, printWriter, args);
+
+ verify(mUserOneService).dump(fileDescriptor, printWriter, args);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testDump_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ File testFile = new File(mContext.getFilesDir(), "test");
+ testFile.createNewFile();
+ FileDescriptor fileDescriptor = new FileDescriptor();
+ PrintWriter printWriter = new PrintWriter(testFile);
+ String[] args = {"1", "2"};
+
+ backupManagerService.dump(fileDescriptor, printWriter, args);
+
+ verify(mUserOneService, never()).dump(fileDescriptor, printWriter, args);
+ }
+
+ private BackupManagerService createService() {
+ return new BackupManagerService(
+ mContext, new Trampoline(mContext), startBackupThread(null));
+ }
+
+ private BackupManagerService createServiceAndRegisterUser(
+ int userId, UserBackupManagerService userBackupManagerService) {
+ BackupManagerService backupManagerService = createService();
+ backupManagerService.startServiceForUser(userId, userBackupManagerService);
+ return backupManagerService;
+ }
+
+ /**
+ * Sets the calling user to {@code userId} and grants the permission INTERACT_ACROSS_USERS_FULL
+ * to the caller if {@code shouldGrantPermission} is {@code true}, else it denies the
+ * permission.
+ */
+ private void setCallerAndGrantInteractUserPermission(
+ @UserIdInt int userId, boolean shouldGrantPermission) {
+ ShadowBinder.setCallingUserHandle(UserHandle.of(userId));
+ if (shouldGrantPermission) {
+ mShadowContext.grantPermissions(INTERACT_ACROSS_USERS_FULL);
+ } else {
+ mShadowContext.denyPermissions(INTERACT_ACROSS_USERS_FULL);
+ }
+ }
+
+ private ParcelFileDescriptor getFileDescriptorForAdbTest() throws Exception {
+ File testFile = new File(mContext.getFilesDir(), "test");
+ testFile.createNewFile();
+ return ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/KeyValueBackupJobTest.java b/services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/KeyValueBackupJobTest.java
rename to services/robotests/backup/src/com/android/server/backup/KeyValueBackupJobTest.java
diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/TransportManagerTest.java
rename to services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
diff --git a/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java
rename to services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunk/ChunkTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java
diff --git a/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
rename to services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
diff --git a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java b/services/robotests/backup/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
rename to services/robotests/backup/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/AgentExceptionTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java
rename to services/robotests/backup/src/com/android/server/backup/keyvalue/AgentExceptionTest.java
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/BackupExceptionTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java
rename to services/robotests/backup/src/com/android/server/backup/keyvalue/BackupExceptionTest.java
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
rename to services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
rename to services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/TaskExceptionTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java
rename to services/robotests/backup/src/com/android/server/backup/keyvalue/TaskExceptionTest.java
diff --git a/services/robotests/src/com/android/server/backup/remote/FutureBackupCallbackTest.java b/services/robotests/backup/src/com/android/server/backup/remote/FutureBackupCallbackTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/remote/FutureBackupCallbackTest.java
rename to services/robotests/backup/src/com/android/server/backup/remote/FutureBackupCallbackTest.java
diff --git a/services/robotests/src/com/android/server/backup/remote/RemoteCallTest.java b/services/robotests/backup/src/com/android/server/backup/remote/RemoteCallTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/remote/RemoteCallTest.java
rename to services/robotests/backup/src/com/android/server/backup/remote/RemoteCallTest.java
diff --git a/services/robotests/src/com/android/server/backup/remote/RemoteResultTest.java b/services/robotests/backup/src/com/android/server/backup/remote/RemoteResultTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/remote/RemoteResultTest.java
rename to services/robotests/backup/src/com/android/server/backup/remote/RemoteResultTest.java
diff --git a/services/robotests/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java b/services/robotests/backup/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java
rename to services/robotests/backup/src/com/android/server/backup/remote/ServiceBackupCallbackTest.java
diff --git a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
rename to services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
diff --git a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
rename to services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
diff --git a/services/robotests/src/com/android/server/backup/testing/CryptoTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/CryptoTestUtils.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/testing/CryptoTestUtils.java
rename to services/robotests/backup/src/com/android/server/backup/testing/CryptoTestUtils.java
diff --git a/services/robotests/src/com/android/server/backup/testing/PackageData.java b/services/robotests/backup/src/com/android/server/backup/testing/PackageData.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/testing/PackageData.java
rename to services/robotests/backup/src/com/android/server/backup/testing/PackageData.java
diff --git a/services/robotests/src/com/android/server/backup/testing/TestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/testing/TestUtils.java
rename to services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java
diff --git a/services/robotests/src/com/android/server/backup/testing/TransportData.java b/services/robotests/backup/src/com/android/server/backup/testing/TransportData.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/testing/TransportData.java
rename to services/robotests/backup/src/com/android/server/backup/testing/TransportData.java
diff --git a/services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/TransportTestUtils.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/testing/TransportTestUtils.java
rename to services/robotests/backup/src/com/android/server/backup/testing/TransportTestUtils.java
diff --git a/services/robotests/src/com/android/server/backup/testing/Utils.java b/services/robotests/backup/src/com/android/server/backup/testing/Utils.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/testing/Utils.java
rename to services/robotests/backup/src/com/android/server/backup/testing/Utils.java
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportClientManagerTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java
rename to services/robotests/backup/src/com/android/server/backup/transport/TransportClientManagerTest.java
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportClientTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
rename to services/robotests/backup/src/com/android/server/backup/transport/TransportClientTest.java
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportStatsTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportStatsTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/transport/TransportStatsTest.java
rename to services/robotests/backup/src/com/android/server/backup/transport/TransportStatsTest.java
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
deleted file mode 100644
index 58bce1c..0000000
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ /dev/null
@@ -1,744 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.backup;
-
-import static com.android.server.backup.testing.TransportData.backupTransport;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.robolectric.Shadows.shadowOf;
-import static org.testng.Assert.expectThrows;
-
-import android.annotation.UserIdInt;
-import android.app.Application;
-import android.app.backup.IBackupManagerMonitor;
-import android.app.backup.IBackupObserver;
-import android.app.backup.IFullBackupRestoreObserver;
-import android.app.backup.ISelectBackupTransportCallback;
-import android.content.Context;
-import android.content.Intent;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.UserHandle;
-import android.platform.test.annotations.Presubmit;
-
-import com.android.server.backup.testing.BackupManagerServiceTestUtils;
-import com.android.server.backup.testing.TransportData;
-import com.android.server.testing.shadows.ShadowBinder;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowContextWrapper;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowBinder.class})
-@Presubmit
-public class BackupManagerServiceTest {
- private static final String TEST_PACKAGE = "package";
- private static final String TEST_TRANSPORT = "transport";
-
- private static final String[] ADB_TEST_PACKAGES = {TEST_PACKAGE};
-
- private static final int NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 1;
-
- private ShadowContextWrapper mShadowContext;
- @Mock private UserBackupManagerService mUserBackupManagerService;
- private BackupManagerService mBackupManagerService;
- private Context mContext;
- @UserIdInt private int mUserId;
-
- /** Initialize {@link BackupManagerService}. */
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- Application application = RuntimeEnvironment.application;
- mContext = application;
- mShadowContext = shadowOf(application);
- mUserId = NON_USER_SYSTEM;
- mBackupManagerService =
- new BackupManagerService(
- application,
- new Trampoline(application),
- BackupManagerServiceTestUtils.startBackupThread(null));
- mBackupManagerService.setUserBackupManagerService(mUserBackupManagerService);
- }
-
- /**
- * Clean up and reset state that was created for testing {@link BackupManagerService}
- * operations.
- */
- @After
- public void tearDown() throws Exception {
- ShadowBinder.reset();
- }
-
- /**
- * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}.
- * This is specifically to prevent overloading the logs in production.
- */
- @Test
- public void testMoreDebug_isFalse() throws Exception {
- boolean moreDebug = BackupManagerService.MORE_DEBUG;
-
- assertThat(moreDebug).isFalse();
- }
-
- // TODO(b/118520567): Change the following tests to use the per-user instance of
- // UserBackupManagerService once it's implemented. Currently these tests only test the straight
- // forward redirection.
-
- // ---------------------------------------------
- // Backup agent tests
- // ---------------------------------------------
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testDataChanged_callsDataChangedForUser() throws Exception {
- mBackupManagerService.dataChanged(TEST_PACKAGE);
-
- verify(mUserBackupManagerService).dataChanged(TEST_PACKAGE);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testAgentConnected_callsAgentConnectedForUser() throws Exception {
- IBinder agentBinder = mock(IBinder.class);
-
- mBackupManagerService.agentConnected(TEST_PACKAGE, agentBinder);
-
- verify(mUserBackupManagerService).agentConnected(TEST_PACKAGE, agentBinder);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testAgentDisconnected_callsAgentDisconnectedForUser() throws Exception {
- mBackupManagerService.agentDisconnected(TEST_PACKAGE);
-
- verify(mUserBackupManagerService).agentDisconnected(TEST_PACKAGE);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testOpComplete_callsOpCompleteForUser() throws Exception {
- mBackupManagerService.opComplete(/* token */ 0, /* result */ 0L);
-
- verify(mUserBackupManagerService).opComplete(/* token */ 0, /* result */ 0L);
- }
-
- // ---------------------------------------------
- // Transport tests
- // ---------------------------------------------
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testInitializeTransports_callsInitializeTransportsForUser() throws Exception {
- String[] transports = {TEST_TRANSPORT};
-
- mBackupManagerService.initializeTransports(transports, /* observer */ null);
-
- verify(mUserBackupManagerService).initializeTransports(transports, /* observer */ null);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testClearBackupData_callsClearBackupDataForUser() throws Exception {
- mBackupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
-
- verify(mUserBackupManagerService).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetCurrentTransport_callsGetCurrentTransportForUser() throws Exception {
- mBackupManagerService.getCurrentTransport();
-
- verify(mUserBackupManagerService).getCurrentTransport();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetCurrentTransportComponent_callsGetCurrentTransportComponentForUser()
- throws Exception {
- mBackupManagerService.getCurrentTransportComponent();
-
- verify(mUserBackupManagerService).getCurrentTransportComponent();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testListAllTransports_callsListAllTransportsForUser() throws Exception {
- mBackupManagerService.listAllTransports();
-
- verify(mUserBackupManagerService).listAllTransports();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testListAllTransportComponents_callsListAllTransportComponentsForUser()
- throws Exception {
- mBackupManagerService.listAllTransportComponents();
-
- verify(mUserBackupManagerService).listAllTransportComponents();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetTransportWhitelist_callsGetTransportWhitelistForUser() throws Exception {
- mBackupManagerService.getTransportWhitelist();
-
- verify(mUserBackupManagerService).getTransportWhitelist();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testUpdateTransportAttributes_callsUpdateTransportAttributesForUser()
- throws Exception {
- TransportData transport = backupTransport();
- Intent configurationIntent = new Intent();
- Intent dataManagementIntent = new Intent();
-
- mBackupManagerService.updateTransportAttributes(
- transport.getTransportComponent(),
- transport.transportName,
- configurationIntent,
- "currentDestinationString",
- dataManagementIntent,
- "dataManagementLabel");
-
- verify(mUserBackupManagerService)
- .updateTransportAttributes(
- transport.getTransportComponent(),
- transport.transportName,
- configurationIntent,
- "currentDestinationString",
- dataManagementIntent,
- "dataManagementLabel");
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testSelectBackupTransport_callsSelectBackupTransportForUser() throws Exception {
- mBackupManagerService.selectBackupTransport(TEST_TRANSPORT);
-
- verify(mUserBackupManagerService).selectBackupTransport(TEST_TRANSPORT);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testSelectTransportAsync_callsSelectTransportAsyncForUser() throws Exception {
- TransportData transport = backupTransport();
- ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
- mBackupManagerService.selectBackupTransportAsync(
- transport.getTransportComponent(), callback);
-
- verify(mUserBackupManagerService)
- .selectBackupTransportAsync(transport.getTransportComponent(), callback);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetConfigurationIntent_callsGetConfigurationIntentForUser() throws Exception {
- mBackupManagerService.getConfigurationIntent(TEST_TRANSPORT);
-
- verify(mUserBackupManagerService).getConfigurationIntent(TEST_TRANSPORT);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetDestinationString_callsGetDestinationStringForUser() throws Exception {
- mBackupManagerService.getDestinationString(TEST_TRANSPORT);
-
- verify(mUserBackupManagerService).getDestinationString(TEST_TRANSPORT);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetDataManagementIntent_callsGetDataManagementIntentForUser() throws Exception {
- mBackupManagerService.getDataManagementIntent(TEST_TRANSPORT);
-
- verify(mUserBackupManagerService).getDataManagementIntent(TEST_TRANSPORT);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetDataManagementLabel_callsGetDataManagementLabelForUser() throws Exception {
- mBackupManagerService.getDataManagementLabel(TEST_TRANSPORT);
-
- verify(mUserBackupManagerService).getDataManagementLabel(TEST_TRANSPORT);
- }
-
- // ---------------------------------------------
- // Settings tests
- // ---------------------------------------------
- /**
- * Test verifying that {@link BackupManagerService#setBackupEnabled(int, boolean)} throws a
- * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
- */
- @Test
- public void setBackupEnabled_withoutPermission_throwsSecurityException() {
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- expectThrows(
- SecurityException.class,
- () -> mBackupManagerService.setBackupEnabled(mUserId, true));
- }
-
- /**
- * Test verifying that {@link BackupManagerService#setBackupEnabled(int, boolean)} does not
- * require the caller to have INTERACT_ACROSS_USERS_FULL permission when the calling user id is
- * the same as the target user id.
- */
- @Test
- public void setBackupEnabled_whenCallingUserIsTargetUser_doesntNeedPermission() {
- ShadowBinder.setCallingUserHandle(UserHandle.of(mUserId));
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- mBackupManagerService.setBackupEnabled(mUserId, true);
-
- verify(mUserBackupManagerService).setBackupEnabled(true);
- }
-
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void setBackupEnabled_callsSetBackupEnabledForUser() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- mBackupManagerService.setBackupEnabled(mUserId, true);
-
- verify(mUserBackupManagerService).setBackupEnabled(true);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void setAutoRestore_callsSetAutoRestoreForUser() throws Exception {
- mBackupManagerService.setAutoRestore(true);
-
- verify(mUserBackupManagerService).setAutoRestore(true);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testSetBackupProvisioned_callsSetBackupProvisionedForUser() throws Exception {
- mBackupManagerService.setBackupProvisioned(true);
-
- verify(mUserBackupManagerService).setBackupProvisioned(true);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#isBackupEnabled(int)} throws a
- * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
- */
- @Test
- public void testIsBackupEnabled_withoutPermission_throwsSecurityException() {
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- expectThrows(
- SecurityException.class,
- () -> mBackupManagerService.isBackupEnabled(mUserId));
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testIsBackupEnabled_callsIsBackupEnabledForUser() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- mBackupManagerService.isBackupEnabled(mUserId);
-
- verify(mUserBackupManagerService).isBackupEnabled();
- }
-
- // ---------------------------------------------
- // Backup tests
- // ---------------------------------------------
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testIsAppEligibleForBackup_callsIsAppEligibleForBackupForUser() throws Exception {
- mBackupManagerService.isAppEligibleForBackup(TEST_PACKAGE);
-
- verify(mUserBackupManagerService).isAppEligibleForBackup(TEST_PACKAGE);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testFilterAppsEligibleForBackup_callsFilterAppsEligibleForBackupForUser()
- throws Exception {
- String[] packages = {TEST_PACKAGE};
-
- mBackupManagerService.filterAppsEligibleForBackup(packages);
-
- verify(mUserBackupManagerService).filterAppsEligibleForBackup(packages);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#backupNow(int)} throws a
- * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
- */
- @Test
- public void testBackupNow_withoutPermission_throwsSecurityException() {
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- expectThrows(
- SecurityException.class,
- () -> mBackupManagerService.backupNow(mUserId));
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testBackupNow_callsBackupNowForUser() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- mBackupManagerService.backupNow(mUserId);
-
- verify(mUserBackupManagerService).backupNow();
- }
-
- /**
- * Test verifying that {@link BackupManagerService#requestBackup(int, String[], IBackupObserver,
- * IBackupManagerMonitor, int)} throws a {@link SecurityException} if the caller does not have
- * INTERACT_ACROSS_USERS_FULL permission.
- */
- @Test
- public void testRequestBackup_withoutPermission_throwsSecurityException() {
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
- String[] packages = {TEST_PACKAGE};
- IBackupObserver observer = mock(IBackupObserver.class);
- IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
-
- expectThrows(
- SecurityException.class,
- () -> mBackupManagerService.requestBackup(mUserId, packages, observer, monitor, 0));
- }
-
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testRequestBackup_callsRequestBackupForUser() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
- String[] packages = {TEST_PACKAGE};
- IBackupObserver observer = mock(IBackupObserver.class);
- IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
-
- mBackupManagerService.requestBackup(mUserId, packages, observer, monitor,
- /* flags */ 0);
-
- verify(mUserBackupManagerService).requestBackup(packages, observer, monitor, /* flags */ 0);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#cancelBackups(int)} throws a
- * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
- */
- @Test
- public void testCancelBackups_withoutPermission_throwsSecurityException() {
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- expectThrows(
- SecurityException.class,
- () -> mBackupManagerService.cancelBackups(mUserId));
- }
-
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testCancelBackups_callsCancelBackupsForUser() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- mBackupManagerService.cancelBackups(mUserId);
-
- verify(mUserBackupManagerService).cancelBackups();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testBeginFullBackup_callsBeginFullBackupForUser() throws Exception {
- FullBackupJob job = new FullBackupJob();
-
- mBackupManagerService.beginFullBackup(job);
-
- verify(mUserBackupManagerService).beginFullBackup(job);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testEndFullBackup_callsEndFullBackupForUser() throws Exception {
- mBackupManagerService.endFullBackup();
-
- verify(mUserBackupManagerService).endFullBackup();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testFullTransportBackup_callsFullTransportBackupForUser() throws Exception {
- String[] packages = {TEST_PACKAGE};
-
- mBackupManagerService.fullTransportBackup(packages);
-
- verify(mUserBackupManagerService).fullTransportBackup(packages);
- }
-
- // ---------------------------------------------
- // Restore tests
- // ---------------------------------------------
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testRestoreAtInstall_callsRestoreAtInstallForUser() throws Exception {
- mBackupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0);
-
- verify(mUserBackupManagerService).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testBeginRestoreSession_callsBeginRestoreSessionForUser() throws Exception {
- mBackupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
-
- verify(mUserBackupManagerService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetAvailableRestoreToken_callsGetAvailableRestoreTokenForUser()
- throws Exception {
- mBackupManagerService.getAvailableRestoreToken(TEST_PACKAGE);
-
- verify(mUserBackupManagerService).getAvailableRestoreToken(TEST_PACKAGE);
- }
-
- // ---------------------------------------------
- // Adb backup/restore tests
- // ---------------------------------------------
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testSetBackupPassword_callsSetBackupPasswordForUser() throws Exception {
- mBackupManagerService.setBackupPassword("currentPassword", "newPassword");
-
- verify(mUserBackupManagerService).setBackupPassword("currentPassword", "newPassword");
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testHasBackupPassword_callsHasBackupPasswordForUser() throws Exception {
- mBackupManagerService.hasBackupPassword();
-
- verify(mUserBackupManagerService).hasBackupPassword();
- }
-
- /**
- * Test verifying that {@link BackupManagerService#adbBackup(ParcelFileDescriptor, int, boolean,
- * boolean, boolean, boolean, boolean, boolean, boolean, boolean, String[])} throws a
- * {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
- */
- @Test
- public void testAdbBackup_withoutPermission_throwsSecurityException() {
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- expectThrows(SecurityException.class,
- () ->
- mBackupManagerService.adbBackup(
- /* userId */ mUserId,
- /* parcelFileDescriptor*/ null,
- /* includeApks */ true,
- /* includeObbs */ true,
- /* includeShared */ true,
- /* doWidgets */ true,
- /* doAllApps */ true,
- /* includeSystem */ true,
- /* doCompress */ true,
- /* doKeyValue */ true,
- null));
-
- }
-
- /**
- * Test verifying that {@link BackupManagerService#adbBackup(ParcelFileDescriptor, int, boolean,
- * boolean, boolean, boolean, boolean, boolean, boolean, boolean, String[])} does not require
- * the caller to have INTERACT_ACROSS_USERS_FULL permission when the calling user id is the
- * same as the target user id.
- */
- @Test
- public void testAdbBackup_whenCallingUserIsTargetUser_doesntNeedPermission() throws Exception {
- ShadowBinder.setCallingUserHandle(UserHandle.of(mUserId));
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
-
- mBackupManagerService.adbBackup(
- /* userId */ mUserId,
- parcelFileDescriptor,
- /* includeApks */ true,
- /* includeObbs */ true,
- /* includeShared */ true,
- /* doWidgets */ true,
- /* doAllApps */ true,
- /* includeSystem */ true,
- /* doCompress */ true,
- /* doKeyValue */ true,
- ADB_TEST_PACKAGES);
-
- verify(mUserBackupManagerService)
- .adbBackup(
- parcelFileDescriptor,
- /* includeApks */ true,
- /* includeObbs */ true,
- /* includeShared */ true,
- /* doWidgets */ true,
- /* doAllApps */ true,
- /* includeSystem */ true,
- /* doCompress */ true,
- /* doKeyValue */ true,
- ADB_TEST_PACKAGES);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testAdbBackup_callsAdbBackupForUser() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
-
- mBackupManagerService.adbBackup(
- /* userId */ mUserId,
- parcelFileDescriptor,
- /* includeApks */ true,
- /* includeObbs */ true,
- /* includeShared */ true,
- /* doWidgets */ true,
- /* doAllApps */ true,
- /* includeSystem */ true,
- /* doCompress */ true,
- /* doKeyValue */ true,
- ADB_TEST_PACKAGES);
-
- verify(mUserBackupManagerService)
- .adbBackup(
- parcelFileDescriptor,
- /* includeApks */ true,
- /* includeObbs */ true,
- /* includeShared */ true,
- /* doWidgets */ true,
- /* doAllApps */ true,
- /* includeSystem */ true,
- /* doCompress */ true,
- /* doKeyValue */ true,
- ADB_TEST_PACKAGES);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#adbRestore(ParcelFileDescriptor, int)} throws
- * a {@link SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL
- * permission.
- */
- @Test
- public void testAdbRestore_withoutPermission_throwsSecurityException() {
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- expectThrows(SecurityException.class,
- () -> mBackupManagerService.adbRestore(mUserId, null));
-
- }
-
- /**
- * Test verifying that {@link BackupManagerService#adbRestore(ParcelFileDescriptor, int)} does
- * not require the caller to have INTERACT_ACROSS_USERS_FULL permission when the calling user id
- * is the same as the target user id.
- */
- @Test
- public void testAdbRestore_whenCallingUserIsTargetUser_doesntNeedPermission() throws Exception {
- ShadowBinder.setCallingUserHandle(UserHandle.of(mUserId));
- mShadowContext.denyPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
-
- mBackupManagerService.adbRestore(mUserId, parcelFileDescriptor);
-
- verify(mUserBackupManagerService).adbRestore(parcelFileDescriptor);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testAdbRestore_callsAdbRestoreForUser() throws Exception {
- mShadowContext.grantPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
-
- ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
-
- mBackupManagerService.adbRestore(mUserId, parcelFileDescriptor);
-
- verify(mUserBackupManagerService).adbRestore(parcelFileDescriptor);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testAcknowledgeAdbBackupOrRestore_callsAcknowledgeAdbBackupOrRestoreForUser()
- throws Exception {
- IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
-
- mBackupManagerService.acknowledgeAdbBackupOrRestore(
- /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer);
-
- verify(mUserBackupManagerService)
- .acknowledgeAdbBackupOrRestore(
- /* token */ 0,
- /* allow */ true,
- "currentPassword",
- "encryptionPassword",
- observer);
- }
-
- // ---------------------------------------------
- // Service tests
- // ---------------------------------------------
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testDump_callsDumpForUser() throws Exception {
- File testFile = new File(mContext.getFilesDir(), "test");
- testFile.createNewFile();
- FileDescriptor fileDescriptor = new FileDescriptor();
- PrintWriter printWriter = new PrintWriter(testFile);
- String[] args = {"1", "2"};
-
- mBackupManagerService.dump(fileDescriptor, printWriter, args);
-
- verify(mUserBackupManagerService).dump(fileDescriptor, printWriter, args);
- }
-
- private ParcelFileDescriptor getFileDescriptorForAdbTest() throws Exception {
- File testFile = new File(mContext.getFilesDir(), "test");
- testFile.createNewFile();
- return ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
- }
-}
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index cf4d3a8..1b5ba26 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -25,7 +25,6 @@
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
- <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
<uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
diff --git a/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java b/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
index d965f8a..0fd5921 100644
--- a/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
@@ -41,11 +41,14 @@
/**
* Tests for {@link SettingsToPropertiesMapper}
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:SettingsToPropertiesMapperTest
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
public class SettingsToPropertiesMapperTest {
- private static final String NAME_VALID_CHARACTERS_REGEX = "^[\\w\\.\\-@:]*$";
+ private static final String NAME_VALID_CHARACTERS_REGEX = "^[\\w\\-@:]*$";
private static final String[] TEST_MAPPING = new String[] {
Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS
};
@@ -77,7 +80,28 @@
}
if (!globalSetting.matches(NAME_VALID_CHARACTERS_REGEX)) {
Assert.fail(globalSetting + " contains invalid characters. "
- + "Only alphanumeric characters, '.', '-', '@', ':' and '_' are valid.");
+ + "Only alphanumeric characters, '-', '@', ':' and '_' are valid.");
+ }
+ }
+ }
+
+ @Test
+ public void validateRegisteredDeviceConfigScopes() {
+ HashSet<String> hashSet = new HashSet<>();
+ for (String deviceConfigScope : SettingsToPropertiesMapper.sDeviceConfigScopes) {
+ if (hashSet.contains(deviceConfigScope)) {
+ Assert.fail("deviceConfigScope "
+ + deviceConfigScope
+ + " is registered more than once in "
+ + "SettingsToPropertiesMapper.sDeviceConfigScopes.");
+ }
+ hashSet.add(deviceConfigScope);
+ if (TextUtils.isEmpty(deviceConfigScope)) {
+ Assert.fail("empty deviceConfigScope registered.");
+ }
+ if (!deviceConfigScope.matches(NAME_VALID_CHARACTERS_REGEX)) {
+ Assert.fail(deviceConfigScope + " contains invalid characters. "
+ + "Only alphanumeric characters, '-', '@', ':' and '_' are valid.");
}
}
}
@@ -98,8 +122,7 @@
Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue2");
mTestMapper.updatePropertyFromSetting(
Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
- systemPropertyName,
- true);
+ systemPropertyName);
propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
Assert.assertEquals("testValue2", propValue);
@@ -107,8 +130,7 @@
Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, null);
mTestMapper.updatePropertyFromSetting(
Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
- systemPropertyName,
- true);
+ systemPropertyName);
propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
Assert.assertEquals("", propValue);
}
diff --git a/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java b/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java
new file mode 100644
index 0000000..52f434d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appops/AppOpsNotedWatcherTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appops;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.OnOpNotedListener;
+import android.content.Context;
+import android.os.Process;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+
+
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+/**
+ * Tests watching noted ops.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AppOpsNotedWatcherTest {
+
+ private static final long NOTIFICATION_TIMEOUT_MILLIS = 5000;
+
+ public void testWatchNotedOpsRequiresPermission() {
+ // Create a mock listener
+ final OnOpNotedListener listener = mock(OnOpNotedListener.class);
+
+ // Try to start watching noted ops
+ final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
+ try {
+ appOpsManager.startWatchingNoted(new String[]{AppOpsManager.OPSTR_FINE_LOCATION,
+ AppOpsManager.OPSTR_RECORD_AUDIO}, listener);
+ fail("Watching noted ops shoudl require " + Manifest.permission.WATCH_APPOPS);
+ } catch (SecurityException expected) {
+ /*ignored*/
+ }
+ }
+
+ @Test
+ public void testWatchNotedOps() {
+ // Create a mock listener
+ final OnOpNotedListener listener = mock(OnOpNotedListener.class);
+
+ // Start watching noted ops
+ final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
+ appOpsManager.startWatchingNoted(new String[]{AppOpsManager.OPSTR_FINE_LOCATION,
+ AppOpsManager.OPSTR_CAMERA}, listener);
+
+ // Note some ops
+ appOpsManager.noteOp(AppOpsManager.OPSTR_FINE_LOCATION, Process.myUid(),
+ getContext().getPackageName());
+ appOpsManager.noteOp(AppOpsManager.OPSTR_CAMERA, Process.myUid(),
+ getContext().getPackageName());
+
+ // Verify that we got called for the ops being noted
+ final InOrder inOrder = inOrder(listener);
+ inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
+ .times(1)).onOpNoted(eq(AppOpsManager.OPSTR_FINE_LOCATION),
+ eq(Process.myUid()), eq(getContext().getPackageName()),
+ eq(AppOpsManager.MODE_ALLOWED));
+ inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
+ .times(1)).onOpNoted(eq(AppOpsManager.OPSTR_CAMERA),
+ eq(Process.myUid()), eq(getContext().getPackageName()),
+ eq(AppOpsManager.MODE_ALLOWED));
+
+ // Stop watching
+ appOpsManager.stopWatchingNoted(listener);
+
+ // This should be the only two callbacks we got
+ verifyNoMoreInteractions(listener);
+ }
+
+ private static Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
index d7a398e..ff31435 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -132,19 +132,21 @@
}
@Test
- public void startServiceForUser_whenMultiUserSettingDisabled_isIgnored() {
+ public void unlockUser_whenMultiUserSettingDisabled_isIgnoredForNonSystemUser() {
Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- mTrampoline.startServiceForUser(10);
+ mTrampoline.unlockUser(10);
verify(mBackupManagerServiceMock, never()).startServiceForUser(10);
}
@Test
- public void startServiceForUser_whenMultiUserSettingEnabled_callsBackupManagerService() {
+ public void unlockUser_whenMultiUserSettingEnabled_callsBackupManagerServiceForNonSystemUser() {
Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1);
+ mTrampoline.initializeService(UserHandle.USER_SYSTEM);
- mTrampoline.startServiceForUser(10);
+ mTrampoline.unlockUser(10);
verify(mBackupManagerServiceMock).startServiceForUser(10);
}
@@ -989,6 +991,11 @@
return sBackupManagerServiceMock;
}
+ @Override
+ protected void postToHandler(Runnable runnable) {
+ runnable.run();
+ }
+
int getCreateServiceCallsCount() {
return mCreateServiceCallsCount;
}
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 e988994..bf4b52e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -52,7 +52,6 @@
import android.provider.Settings;
import android.view.Surface;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.internal.util.test.FakeSettingsProvider;
@@ -81,7 +80,6 @@
*/
@SmallTest
@Presubmit
-@FlakyTest(detail = "Confirm stable in post-submit before removing")
public class DisplayRotationTests {
private static final long UI_HANDLER_WAIT_TIMEOUT_MS = 50;
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index f3a125b..8635364 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -40,7 +40,6 @@
import android.view.DisplayInfo;
import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import com.android.server.LocalServices;
@@ -65,7 +64,6 @@
*/
@MediumTest
@Presubmit
-@FlakyTest(detail = "Confirm stable in post-submit before removing")
public class LaunchParamsPersisterTests extends ActivityTestsBase {
private static final int TEST_USER_ID = 3;
private static final int ALTERNATIVE_USER_ID = 0;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 0bd681b..7186e22 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -47,7 +47,6 @@
import android.view.Display;
import android.view.Gravity;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.server.wm.LaunchParamsController.LaunchParams;
@@ -64,7 +63,6 @@
* atest WmTests:TaskLaunchParamsModifierTests
*/
@SmallTest
-@FlakyTest(detail = "Confirm stable in post-submit before removing")
@Presubmit
public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc
index 94879d0..a78f7d5 100644
--- a/startop/view_compiler/dex_builder.cc
+++ b/startop/view_compiler/dex_builder.cc
@@ -79,6 +79,9 @@
case Instruction::Op::kNew:
out << "kNew";
return out;
+ case Instruction::Op::kCheckCast:
+ out << "kCheckCast";
+ return out;
}
}
@@ -329,6 +332,8 @@
return EncodeBranch(art::Instruction::IF_NEZ, instruction);
case Instruction::Op::kNew:
return EncodeNew(instruction);
+ case Instruction::Op::kCheckCast:
+ return EncodeCast(instruction);
}
}
@@ -422,6 +427,18 @@
Encode21c(::art::Instruction::NEW_INSTANCE, RegisterValue(*instruction.dest()), type.value());
}
+void MethodBuilder::EncodeCast(const Instruction& instruction) {
+ DCHECK_EQ(Instruction::Op::kCheckCast, instruction.opcode());
+ DCHECK(instruction.dest().has_value());
+ DCHECK(instruction.dest()->is_variable());
+ DCHECK_EQ(1, instruction.args().size());
+
+ const Value& type = instruction.args()[0];
+ DCHECK_LT(RegisterValue(*instruction.dest()), 256);
+ DCHECK(type.is_type());
+ Encode21c(::art::Instruction::CHECK_CAST, RegisterValue(*instruction.dest()), type.value());
+}
+
size_t MethodBuilder::RegisterValue(const Value& value) const {
if (value.is_register()) {
return value.value();
diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h
index 45596ac..06059c8 100644
--- a/startop/view_compiler/dex_builder.h
+++ b/startop/view_compiler/dex_builder.h
@@ -142,17 +142,18 @@
// The operation performed by this instruction. These are virtual instructions that do not
// correspond exactly to DEX instructions.
enum class Op {
- kReturn,
- kReturnObject,
- kMove,
- kInvokeVirtual,
- kInvokeDirect,
- kInvokeStatic,
- kInvokeInterface,
kBindLabel,
kBranchEqz,
kBranchNEqz,
- kNew
+ kCheckCast,
+ kInvokeDirect,
+ kInvokeInterface,
+ kInvokeStatic,
+ kInvokeVirtual,
+ kMove,
+ kNew,
+ kReturn,
+ kReturnObject,
};
////////////////////////
@@ -168,6 +169,13 @@
static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest, T... args) {
return Instruction{opcode, /*method_id=*/0, /*result_is_object=*/false, dest, args...};
}
+
+ // A cast instruction. Basically, `(type)val`
+ static inline Instruction Cast(Value val, Value type) {
+ DCHECK(type.is_type());
+ return OpWithArgs(Op::kCheckCast, val, type);
+ }
+
// For method calls.
template <typename... T>
static inline Instruction InvokeVirtual(size_t method_id, std::optional<const Value> dest,
@@ -302,6 +310,7 @@
void EncodeInvoke(const Instruction& instruction, ::art::Instruction::Code opcode);
void EncodeBranch(art::Instruction::Code op, const Instruction& instruction);
void EncodeNew(const Instruction& instruction);
+ void EncodeCast(const Instruction& instruction);
// Low-level instruction format encoding. See
// https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
index 1508ad9e..42d4161 100644
--- a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
@@ -20,6 +20,7 @@
import dalvik.system.InMemoryDexClassLoader;
import dalvik.system.PathClassLoader;
import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import org.junit.Assert;
@@ -151,4 +152,23 @@
Method method = clazz.getMethod("invokeVirtualReturnObject", String.class, int.class);
Assert.assertEquals("bc", method.invoke(null, "abc", 1));
}
+
+ @Test
+ public void castObjectToString() throws Exception {
+ ClassLoader loader = loadDexFile("simple.dex");
+ Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+ Method method = clazz.getMethod("castObjectToString", Object.class);
+ Assert.assertEquals("abc", method.invoke(null, "abc"));
+ boolean castFailed = false;
+ try {
+ method.invoke(null, 5);
+ } catch (InvocationTargetException e) {
+ if (e.getCause() instanceof ClassCastException) {
+ castFailed = true;
+ } else {
+ throw e;
+ }
+ }
+ Assert.assertTrue(castFailed);
+ }
}
diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc
index 2781aa5..f62ec5dd 100644
--- a/startop/view_compiler/dex_testcase_generator.cc
+++ b/startop/view_compiler/dex_testcase_generator.cc
@@ -269,6 +269,19 @@
method.Encode();
}(invokeVirtualReturnObject);
+ // Make sure we can cast objects
+ // String castObjectToString(Object o) { return (String)o; }
+ MethodBuilder castObjectToString{cbuilder.CreateMethod(
+ "castObjectToString",
+ Prototype{string_type, TypeDescriptor::FromClassname("java.lang.Object")})};
+ [&](MethodBuilder& method) {
+ const ir::Type* type_def = dex_file.GetOrAddType(string_type.descriptor());
+ method.AddInstruction(
+ Instruction::Cast(Value::Parameter(0), Value::Type(type_def->orig_index)));
+ method.BuildReturn(Value::Parameter(0), /*is_object=*/true);
+ method.Encode();
+ }(castObjectToString);
+
slicer::MemView image{dex_file.CreateImage()};
std::ofstream out_file(outdir + "/simple.dex");
out_file.write(image.ptr<const char>(), image.size());
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index c6887ab..f53cb82 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -16,12 +16,16 @@
package android.telephony;
+import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
/**
- * Contains disconnect call causes generated by the framework and the RIL.
+ * Describes the cause of a disconnected call. Those disconnect causes can be converted into a more
+ * generic {@link android.telecom.DisconnectCause} object.
+ *
* @hide
*/
+@SystemApi
public class DisconnectCause {
/** The disconnect cause is not valid (Not received a disconnect cause) */
@@ -101,8 +105,8 @@
/** Unknown error or not specified */
public static final int ERROR_UNSPECIFIED = 36;
/**
- * Only emergency numbers are allowed, but we tried to dial
- * a non-emergency number.
+ * Only emergency numbers are allowed, but we tried to dial a non-emergency number.
+ * @hide
*/
// TODO: This should be the same as NOT_EMERGENCY
public static final int EMERGENCY_ONLY = 37;
@@ -115,8 +119,7 @@
*/
public static final int DIALED_MMI = 39;
/**
- * We tried to call a voicemail: URI but the device has no
- * voicemail number configured.
+ * We tried to call a voicemail: URI but the device has no voicemail number configured.
*/
public static final int VOICEMAIL_NUMBER_MISSING = 40;
/**
@@ -129,6 +132,8 @@
* needs to be triggered by a *disconnect* event, rather than when
* the InCallScreen first comes to the foreground. For now we use
* the needToShowCallLostDialog field for this (see below.)
+ *
+ * @hide
*/
public static final int CDMA_CALL_LOST = 41;
/**
@@ -169,62 +174,52 @@
/**
* Stk Call Control modified DIAL request to USSD request.
- * {@hide}
*/
public static final int DIAL_MODIFIED_TO_USSD = 46;
/**
* Stk Call Control modified DIAL request to SS request.
- * {@hide}
*/
public static final int DIAL_MODIFIED_TO_SS = 47;
/**
* Stk Call Control modified DIAL request to DIAL with modified data.
- * {@hide}
*/
public static final int DIAL_MODIFIED_TO_DIAL = 48;
/**
* The call was terminated because CDMA phone service and roaming have already been activated.
- * {@hide}
*/
public static final int CDMA_ALREADY_ACTIVATED = 49;
/**
* The call was terminated because it is not possible to place a video call while TTY is
* enabled.
- * {@hide}
*/
public static final int VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED = 50;
/**
* The call was terminated because it was pulled to another device.
- * {@hide}
*/
public static final int CALL_PULLED = 51;
/**
* The call was terminated because it was answered on another device.
- * {@hide}
*/
public static final int ANSWERED_ELSEWHERE = 52;
/**
* The call was terminated because the maximum allowable number of calls has been reached.
- * {@hide}
*/
public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 53;
/**
* The call was terminated because cellular data has been disabled.
* Used when in a video call and the user disables cellular data via the settings.
- * {@hide}
*/
public static final int DATA_DISABLED = 54;
/**
* The call was terminated because the data policy has disabled cellular data.
* Used when in a video call and the user has exceeded the device data limit.
- * {@hide}
*/
public static final int DATA_LIMIT_REACHED = 55;
@@ -237,7 +232,6 @@
/**
* The network does not accept the emergency call request because IMEI was used as
* identification and this cability is not supported by the network.
- * {@hide}
*/
public static final int IMEI_NOT_ACCEPTED = 58;
@@ -249,7 +243,6 @@
/**
* The call has failed because of access class barring.
- * {@hide}
*/
public static final int IMS_ACCESS_BLOCKED = 60;
@@ -265,51 +258,43 @@
/**
* Emergency call failed with a temporary fail cause and can be redialed on this slot.
- * {@hide}
*/
public static final int EMERGENCY_TEMP_FAILURE = 63;
/**
* Emergency call failed with a permanent fail cause and should not be redialed on this
- * slot.
- * {@hide}
+ * slot.
*/
public static final int EMERGENCY_PERM_FAILURE = 64;
/**
* This cause is used to report a normal event only when no other cause in the normal class
* applies.
- * {@hide}
*/
public static final int NORMAL_UNSPECIFIED = 65;
/**
* Stk Call Control modified DIAL request to video DIAL request.
- * {@hide}
*/
public static final int DIAL_MODIFIED_TO_DIAL_VIDEO = 66;
/**
* Stk Call Control modified Video DIAL request to SS request.
- * {@hide}
*/
public static final int DIAL_VIDEO_MODIFIED_TO_SS = 67;
/**
* Stk Call Control modified Video DIAL request to USSD request.
- * {@hide}
*/
public static final int DIAL_VIDEO_MODIFIED_TO_USSD = 68;
/**
* Stk Call Control modified Video DIAL request to DIAL request.
- * {@hide}
*/
public static final int DIAL_VIDEO_MODIFIED_TO_DIAL = 69;
/**
* Stk Call Control modified Video DIAL request to Video DIAL request.
- * {@hide}
*/
public static final int DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 70;
@@ -359,7 +344,10 @@
// Do nothing.
}
- /** Returns descriptive string for the specified disconnect cause. */
+ /**
+ * Returns descriptive string for the specified disconnect cause.
+ * @hide
+ */
@UnsupportedAppUsage
public static String toString(int cause) {
switch (cause) {
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 0df0daf..9317aa7 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -173,14 +173,14 @@
public static final int LISTEN_CELL_INFO = 0x00000400;
/**
- * Listen for precise changes and fails to the device calls (cellular).
+ * Listen for {@link PreciseCallState.State} of ringing, background and foreground calls.
* {@more}
* Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
* READ_PRECISE_PHONE_STATE}
*
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public static final int LISTEN_PRECISE_CALL_STATE = 0x00000800;
/**
@@ -320,6 +320,18 @@
*/
public static final int LISTEN_EMERGENCY_NUMBER_LIST = 0x01000000;
+ /**
+ * Listen for call disconnect causes which contains {@link DisconnectCause} and
+ * {@link PreciseDisconnectCause}.
+ * {@more}
+ * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE}
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int LISTEN_CALL_DISCONNECT_CAUSES = 0x02000000;
+
/*
* Subscription used to listen to the phone state changes
* @hide
@@ -530,11 +542,23 @@
/**
* Callback invoked when precise device call state changes.
+ * @param callState {@link PreciseCallState}
+ * @hide
+ */
+ @SystemApi
+ public void onPreciseCallStateChanged(PreciseCallState callState) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when call disconnect cause changes.
+ * @param disconnectCause {@link DisconnectCause}.
+ * @param preciseDisconnectCause {@link PreciseDisconnectCause}.
*
* @hide
*/
- @UnsupportedAppUsage
- public void onPreciseCallStateChanged(PreciseCallState callState) {
+ @SystemApi
+ public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) {
// default implementation empty
}
@@ -799,6 +823,15 @@
() -> mExecutor.execute(() -> psl.onPreciseCallStateChanged(callState)));
}
+ public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onCallDisconnectCauseChanged(
+ disconnectCause, preciseDisconnectCause)));
+ }
+
public void onPreciseDataConnectionStateChanged(
PreciseDataConnectionState dataConnectionState) {
PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
diff --git a/telephony/java/android/telephony/PreciseCallState.java b/telephony/java/android/telephony/PreciseCallState.java
index ed5c26a..59f3e1f 100644
--- a/telephony/java/android/telephony/PreciseCallState.java
+++ b/telephony/java/android/telephony/PreciseCallState.java
@@ -16,29 +16,51 @@
package android.telephony;
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.DisconnectCause;
import android.telephony.PreciseDisconnectCause;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
/**
- * Contains precise call state and call fail causes generated by the
- * framework and the RIL.
+ * Contains precise call states.
*
* The following call information is included in returned PreciseCallState:
*
* <ul>
- * <li>Ringing call state.
- * <li>Foreground call state.
- * <li>Background call state.
- * <li>Disconnect cause; generated by the framework.
- * <li>Precise disconnect cause; generated by the RIL.
+ * <li>Precise ringing call state.
+ * <li>Precise foreground call state.
+ * <li>Precise background call state.
* </ul>
*
+ * @see android.telephony.TelephonyManager.CallState which contains generic call states.
+ *
* @hide
*/
-public class PreciseCallState implements Parcelable {
+@SystemApi
+public final class PreciseCallState implements Parcelable {
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"PRECISE_CALL_STATE_"},
+ value = {
+ PRECISE_CALL_STATE_NOT_VALID,
+ PRECISE_CALL_STATE_IDLE,
+ PRECISE_CALL_STATE_ACTIVE,
+ PRECISE_CALL_STATE_HOLDING,
+ PRECISE_CALL_STATE_DIALING,
+ PRECISE_CALL_STATE_ALERTING,
+ PRECISE_CALL_STATE_INCOMING,
+ PRECISE_CALL_STATE_WAITING,
+ PRECISE_CALL_STATE_DISCONNECTED,
+ PRECISE_CALL_STATE_DISCONNECTING})
+ public @interface State {}
/** Call state is not valid (Not received a call state). */
public static final int PRECISE_CALL_STATE_NOT_VALID = -1;
@@ -61,9 +83,9 @@
/** Call state: Disconnecting. */
public static final int PRECISE_CALL_STATE_DISCONNECTING = 8;
- private int mRingingCallState = PRECISE_CALL_STATE_NOT_VALID;
- private int mForegroundCallState = PRECISE_CALL_STATE_NOT_VALID;
- private int mBackgroundCallState = PRECISE_CALL_STATE_NOT_VALID;
+ private @State int mRingingCallState = PRECISE_CALL_STATE_NOT_VALID;
+ private @State int mForegroundCallState = PRECISE_CALL_STATE_NOT_VALID;
+ private @State int mBackgroundCallState = PRECISE_CALL_STATE_NOT_VALID;
private int mDisconnectCause = DisconnectCause.NOT_VALID;
private int mPreciseDisconnectCause = PreciseDisconnectCause.NOT_VALID;
@@ -73,8 +95,9 @@
* @hide
*/
@UnsupportedAppUsage
- public PreciseCallState(int ringingCall, int foregroundCall, int backgroundCall,
- int disconnectCause, int preciseDisconnectCause) {
+ public PreciseCallState(@State int ringingCall, @State int foregroundCall,
+ @State int backgroundCall, int disconnectCause,
+ int preciseDisconnectCause) {
mRingingCallState = ringingCall;
mForegroundCallState = foregroundCall;
mBackgroundCallState = backgroundCall;
@@ -92,6 +115,8 @@
/**
* Construct a PreciseCallState object from the given parcel.
+ *
+ * @hide
*/
private PreciseCallState(Parcel in) {
mRingingCallState = in.readInt();
@@ -102,59 +127,23 @@
}
/**
- * Get precise ringing call state
- *
- * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
- * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
- * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
- * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
- * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
- * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
- * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
- * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
- * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
- * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+ * Returns the precise ringing call state.
*/
- @UnsupportedAppUsage
- public int getRingingCallState() {
+ public @State int getRingingCallState() {
return mRingingCallState;
}
/**
- * Get precise foreground call state
- *
- * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
- * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
- * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
- * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
- * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
- * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
- * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
- * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
- * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
- * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+ * Returns the precise foreground call state.
*/
- @UnsupportedAppUsage
- public int getForegroundCallState() {
+ public @State int getForegroundCallState() {
return mForegroundCallState;
}
/**
- * Get precise background call state
- *
- * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
- * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
- * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
- * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
- * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
- * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
- * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
- * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
- * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
- * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+ * Returns the precise background call state.
*/
- @UnsupportedAppUsage
- public int getBackgroundCallState() {
+ public @State int getBackgroundCallState() {
return mBackgroundCallState;
}
@@ -199,6 +188,11 @@
* @see DisconnectCause#CDMA_NOT_EMERGENCY
* @see DisconnectCause#CDMA_ACCESS_BLOCKED
* @see DisconnectCause#ERROR_UNSPECIFIED
+ *
+ * TODO: remove disconnect cause from preciseCallState as there is no link between random
+ * connection disconnect cause with foreground, background or ringing call.
+ *
+ * @hide
*/
@UnsupportedAppUsage
public int getDisconnectCause() {
@@ -238,6 +232,11 @@
* @see PreciseDisconnectCause#CDMA_NOT_EMERGENCY
* @see PreciseDisconnectCause#CDMA_ACCESS_BLOCKED
* @see PreciseDisconnectCause#ERROR_UNSPECIFIED
+ *
+ * TODO: remove precise disconnect cause from preciseCallState as there is no link between
+ * random connection disconnect cause with foreground, background or ringing call.
+ *
+ * @hide
*/
@UnsupportedAppUsage
public int getPreciseDisconnectCause() {
@@ -272,14 +271,8 @@
@Override
public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + mRingingCallState;
- result = prime * result + mForegroundCallState;
- result = prime * result + mBackgroundCallState;
- result = prime * result + mDisconnectCause;
- result = prime * result + mPreciseDisconnectCause;
- return result;
+ return Objects.hash(mRingingCallState, mForegroundCallState, mForegroundCallState,
+ mDisconnectCause, mPreciseDisconnectCause);
}
@Override
diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java
index 2acaf34..af88748 100644
--- a/telephony/java/android/telephony/PreciseDisconnectCause.java
+++ b/telephony/java/android/telephony/PreciseDisconnectCause.java
@@ -16,279 +16,329 @@
package android.telephony;
+import android.annotation.SystemApi;
+
/**
- * Contains precise disconnect call causes generated by the
- * framework and the RIL.
- *
+ * Contains precise disconnect call causes generated by the framework and the RIL.
* @hide
*/
+@SystemApi
public class PreciseDisconnectCause {
- /** The disconnect cause is not valid (Not received a disconnect cause)*/
+ /** The disconnect cause is not valid (Not received a disconnect cause).*/
public static final int NOT_VALID = -1;
- /** No disconnect cause provided. Generally a local disconnect or an incoming missed call */
+ /** No disconnect cause provided. Generally a local disconnect or an incoming missed call. */
public static final int NO_DISCONNECT_CAUSE_AVAILABLE = 0;
/**
* The destination cannot be reached because the number, although valid,
- * is not currently assigned
+ * is not currently assigned.
*/
public static final int UNOBTAINABLE_NUMBER = 1;
- /** The user cannot be reached because the network through which the call has been
- * routed does not serve the destination desired
+ /**
+ * The user cannot be reached because the network through which the call has been routed does
+ * not serve the destination desired.
*/
public static final int NO_ROUTE_TO_DESTINATION = 3;
- /** The channel most recently identified is not acceptable to the sending entity for
- * use in this call
+ /**
+ * The channel most recently identified is not acceptable to the sending entity for use in this
+ * call.
*/
public static final int CHANNEL_UNACCEPTABLE = 6;
- /** The MS has tried to access a service that the MS's network operator or service
- * provider is not prepared to allow
+ /**
+ * The mobile station (MS) has tried to access a service that the MS's network operator or
+ * service provider is not prepared to allow.
*/
public static final int OPERATOR_DETERMINED_BARRING = 8;
- /** One of the users involved in the call has requested that the call is cleared */
+ /** One of the users involved in the call has requested that the call is cleared. */
public static final int NORMAL = 16;
- /** The called user is unable to accept another call */
+ /** The called user is unable to accept another call. */
public static final int BUSY = 17;
- /** The user does not respond to a call establishment message with either an alerting
- * or connect indication within the prescribed period of time allocated
+ /**
+ * The user does not respond to a call establishment message with either an alerting or connect
+ * indication within the prescribed period of time allocated.
*/
public static final int NO_USER_RESPONDING = 18;
- /** The user has provided an alerting indication but has not provided a connect
- * indication within a prescribed period of time
+ /**
+ * The user has provided an alerting indication but has not provided a connect indication
+ * within a prescribed period of time.
*/
public static final int NO_ANSWER_FROM_USER = 19;
- /** The equipment sending this cause does not wish to accept this call */
+ /** The equipment sending this cause does not wish to accept this call. */
public static final int CALL_REJECTED = 21;
- /** The called number is no longer assigned */
+ /** The called number is no longer assigned. */
public static final int NUMBER_CHANGED = 22;
- /** This cause is returned to the network when a mobile station clears an active
- * call which is being pre-empted by another call with higher precedence
+ /**
+ * This cause is returned to the network when a mobile station clears an active call which is
+ * being pre-empted by another call with higher precedence.
*/
public static final int PREEMPTION = 25;
- /** The destination indicated by the mobile station cannot be reached because
- * the interface to the destination is not functioning correctly
+ /**
+ * The destination indicated by the mobile station cannot be reached because the interface to
+ * the destination is not functioning correctly.
*/
public static final int DESTINATION_OUT_OF_ORDER = 27;
- /** The called party number is not a valid format or is not complete */
+ /** The called party number is not a valid format or is not complete. */
public static final int INVALID_NUMBER_FORMAT = 28;
- /** The facility requested by user can not be provided by the network */
+ /** The facility requested by user can not be provided by the network. */
public static final int FACILITY_REJECTED = 29;
- /** Provided in response to a STATUS ENQUIRY message */
+ /** Provided in response to a STATUS ENQUIRY message. */
public static final int STATUS_ENQUIRY = 30;
- /** Reports a normal disconnect only when no other normal cause applies */
+ /** Reports a normal disconnect only when no other normal cause applies. */
public static final int NORMAL_UNSPECIFIED = 31;
- /** There is no channel presently available to handle the call */
+ /** There is no channel presently available to handle the call. */
public static final int NO_CIRCUIT_AVAIL = 34;
- /** The network is not functioning correctly and that the condition is likely
- * to last a relatively long period of time
+ /**
+ * The network is not functioning correctly and that the condition is likely to last a
+ * relatively long period of time.
*/
public static final int NETWORK_OUT_OF_ORDER = 38;
/**
- * The network is not functioning correctly and the condition is not likely to last
- * a long period of time
+ * The network is not functioning correctly and the condition is not likely to last a long
+ * period of time.
*/
public static final int TEMPORARY_FAILURE = 41;
- /** The switching equipment is experiencing a period of high traffic */
+ /** The switching equipment is experiencing a period of high traffic. */
public static final int SWITCHING_CONGESTION = 42;
- /** The network could not deliver access information to the remote user as requested */
+ /** The network could not deliver access information to the remote user as requested. */
public static final int ACCESS_INFORMATION_DISCARDED = 43;
- /** The channel cannot be provided */
+ /** The channel cannot be provided. */
public static final int CHANNEL_NOT_AVAIL = 44;
- /** This cause is used to report a resource unavailable event only when no other
- * cause in the resource unavailable class applies
+ /**
+ * This cause is used to report a resource unavailable event only when no other cause in the
+ * resource unavailable class applies.
*/
public static final int RESOURCES_UNAVAILABLE_OR_UNSPECIFIED = 47;
- /** The requested quality of service (ITU-T X.213) cannot be provided */
+ /** The requested quality of service (ITU-T X.213) cannot be provided. */
public static final int QOS_NOT_AVAIL = 49;
- /** The facility could not be provided by the network because the user has no
- * complete subscription
+ /**
+ * The facility could not be provided by the network because the user has no complete
+ * subscription.
*/
public static final int REQUESTED_FACILITY_NOT_SUBSCRIBED = 50;
- /** Incoming calls are not allowed within this CUG */
+ /** Incoming calls are not allowed within this calling user group (CUG). */
public static final int INCOMING_CALLS_BARRED_WITHIN_CUG = 55;
- /** The mobile station is not authorized to use bearer capability requested */
+ /** The mobile station is not authorized to use bearer capability requested. */
public static final int BEARER_CAPABILITY_NOT_AUTHORIZED = 57;
- /** The requested bearer capability is not available at this time */
+ /** The requested bearer capability is not available at this time. */
public static final int BEARER_NOT_AVAIL = 58;
- /** The service option is not availble at this time */
+ /** The service option is not availble at this time. */
public static final int SERVICE_OPTION_NOT_AVAILABLE = 63;
- /** The equipment sending this cause does not support the bearer capability requested */
+ /** The equipment sending this cause does not support the bearer capability requested. */
public static final int BEARER_SERVICE_NOT_IMPLEMENTED = 65;
- /** The call clearing is due to ACM being greater than or equal to ACMmax */
+ /** The call clearing is due to ACM being greater than or equal to ACMmax. */
public static final int ACM_LIMIT_EXCEEDED = 68;
- /** The equipment sending this cause does not support the requested facility */
+ /** The equipment sending this cause does not support the requested facility. */
public static final int REQUESTED_FACILITY_NOT_IMPLEMENTED = 69;
- /** The equipment sending this cause only supports the restricted version of
- * the requested bearer capability
+ /**
+ * The equipment sending this cause only supports the restricted version of the requested bearer
+ * capability.
*/
public static final int ONLY_DIGITAL_INFORMATION_BEARER_AVAILABLE = 70;
- /** The service requested is not implemented at network */
+ /** The service requested is not implemented at network. */
public static final int SERVICE_OR_OPTION_NOT_IMPLEMENTED = 79;
- /** The equipment sending this cause has received a message with a transaction identifier
- * which is not currently in use on the MS-network interface
+ /**
+ * The equipment sending this cause has received a message with a transaction identifier
+ * which is not currently in use on the mobile station network interface.
*/
public static final int INVALID_TRANSACTION_IDENTIFIER = 81;
- /** The called user for the incoming CUG call is not a member of the specified CUG */
+ /**
+ * The called user for the incoming CUG call is not a member of the specified calling user
+ * group (CUG).
+ */
public static final int USER_NOT_MEMBER_OF_CUG = 87;
- /** The equipment sending this cause has received a request which can't be accomodated */
+ /** The equipment sending this cause has received a request which can't be accomodated. */
public static final int INCOMPATIBLE_DESTINATION = 88;
- /** This cause is used to report receipt of a message with semantically incorrect contents */
+ /** This cause is used to report receipt of a message with semantically incorrect contents. */
public static final int SEMANTICALLY_INCORRECT_MESSAGE = 95;
- /** The equipment sending this cause has received a message with a non-semantical
- * mandatory IE error
+ /**
+ * The equipment sending this cause has received a message with a non-semantical mandatory
+ * information element (IE) error.
*/
public static final int INVALID_MANDATORY_INFORMATION = 96;
- /** This is sent in response to a message which is not defined, or defined but not
- * implemented by the equipment sending this cause
+ /**
+ * This is sent in response to a message which is not defined, or defined but not implemented
+ * by the equipment sending this cause.
*/
public static final int MESSAGE_TYPE_NON_IMPLEMENTED = 97;
- /** The equipment sending this cause has received a message not compatible with the
- * protocol state
+ /**
+ * The equipment sending this cause has received a message not compatible with the protocol
+ * state.
*/
public static final int MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 98;
- /** The equipment sending this cause has received a message which includes information
- * elements not recognized because its identifier is not defined or it is defined but not
- * implemented by the equipment sending the cause
+ /**
+ * The equipment sending this cause has received a message which includes information
+ * elements not recognized because its identifier is not defined or it is defined but not
+ * implemented by the equipment sending the cause.
*/
public static final int INFORMATION_ELEMENT_NON_EXISTENT = 99;
- /** The equipment sending this cause has received a message with conditional IE errors */
+ /** The equipment sending this cause has received a message with conditional IE errors. */
public static final int CONDITIONAL_IE_ERROR = 100;
- /** The message has been received which is incompatible with the protocol state */
+ /** The message has been received which is incompatible with the protocol state. */
public static final int MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 101;
- /** The procedure has been initiated by the expiry of a timer in association with
- * 3GPP TS 24.008 error handling procedures
+ /**
+ * The procedure has been initiated by the expiry of a timer in association with
+ * 3GPP TS 24.008 error handling procedures.
*/
public static final int RECOVERY_ON_TIMER_EXPIRED = 102;
- /** This protocol error event is reported only when no other cause in the protocol
- * error class applies
+ /**
+ * This protocol error event is reported only when no other cause in the protocol error class
+ * applies.
*/
public static final int PROTOCOL_ERROR_UNSPECIFIED = 111;
- /** interworking with a network which does not provide causes for actions it takes
- * thus, the precise cause for a message which is being sent cannot be ascertained
+ /**
+ * Interworking with a network which does not provide causes for actions it takes thus, the
+ * precise cause for a message which is being sent cannot be ascertained.
*/
public static final int INTERWORKING_UNSPECIFIED = 127;
- /** The call is restricted */
+ /** The call is restricted. */
public static final int CALL_BARRED = 240;
- /** The call is blocked by the Fixed Dialing Number list */
+ /** The call is blocked by the Fixed Dialing Number list. */
public static final int FDN_BLOCKED = 241;
- /** The given IMSI is not known at the VLR */
- /** TS 24.008 cause 4 */
+ /** The given IMSI is not known at the Visitor Location Register (VLR) TS 24.008 cause . */
public static final int IMSI_UNKNOWN_IN_VLR = 242;
/**
* The network does not accept emergency call establishment using an IMEI or not accept attach
- * procedure for emergency services using an IMEI
+ * procedure for emergency services using an IMEI.
*/
public static final int IMEI_NOT_ACCEPTED = 243;
- /** The call cannot be established because RADIO is OFF */
+ /** The call cannot be established because RADIO is OFF. */
public static final int RADIO_OFF = 247;
- /** The call cannot be established because of no cell coverage */
+ /** The call cannot be established because of no cell coverage. */
public static final int OUT_OF_SRV = 248;
- /** The call cannot be established because of no valid SIM */
+ /** The call cannot be established because of no valid SIM. */
public static final int NO_VALID_SIM = 249;
- /** The call is dropped or failed internally by modem */
+ /** The call is dropped or failed internally by modem. */
public static final int RADIO_INTERNAL_ERROR = 250;
- /** Call failed because of UE timer expired while waiting for a response from network */
+ /** Call failed because of UE timer expired while waiting for a response from network. */
public static final int NETWORK_RESP_TIMEOUT = 251;
- /** Call failed because of a network reject */
+ /** Call failed because of a network reject. */
public static final int NETWORK_REJECT = 252;
- /** Call failed because of radio access failure. ex. RACH failure */
+ /** Call failed because of radio access failure. ex. RACH failure. */
public static final int RADIO_ACCESS_FAILURE = 253;
- /** Call failed/dropped because of a RLF */
+ /** Call failed/dropped because of a Radio Link Failure (RLF). */
public static final int RADIO_LINK_FAILURE = 254;
- /** Call failed/dropped because of radio link lost */
+ /** Call failed/dropped because of radio link lost. */
public static final int RADIO_LINK_LOST = 255;
- /** Call failed because of a radio uplink issue */
+ /** Call failed because of a radio uplink issue. */
public static final int RADIO_UPLINK_FAILURE = 256;
- /** Call failed because of a RRC connection setup failure */
+ /** Call failed because of a RRC (Radio Resource Control) connection setup failure. */
public static final int RADIO_SETUP_FAILURE = 257;
- /** Call failed/dropped because of RRC connection release from NW */
+ /** Call failed/dropped because of RRC (Radio Resource Control) connection release from NW. */
public static final int RADIO_RELEASE_NORMAL = 258;
- /** Call failed/dropped because of RRC abnormally released by modem/network */
+ /**
+ * Call failed/dropped because of RRC (Radio Resource Control) abnormally released by
+ * modem/network.
+ */
public static final int RADIO_RELEASE_ABNORMAL = 259;
- /** Call setup failed because of access class barring */
+ /** Call setup failed because of access class barring. */
public static final int ACCESS_CLASS_BLOCKED = 260;
- /** Call failed/dropped because of a network detach */
+ /** Call failed/dropped because of a network detach. */
public static final int NETWORK_DETACH = 261;
- /** MS is locked until next power cycle */
+ /** Mobile station (MS) is locked until next power cycle. */
public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 1000;
- /** Drop call*/
+ /** Drop call. */
public static final int CDMA_DROP = 1001;
- /** INTERCEPT order received, MS state idle entered */
+ /** INTERCEPT order received, Mobile station (MS) state idle entered. */
public static final int CDMA_INTERCEPT = 1002;
- /** MS has been redirected, call is cancelled */
+ /** Mobile station (MS) has been redirected, call is cancelled. */
public static final int CDMA_REORDER = 1003;
- /** Service option rejection */
+ /** Service option rejection. */
public static final int CDMA_SO_REJECT = 1004;
- /** Requested service is rejected, retry delay is set */
+ /** Requested service is rejected, retry delay is set. */
public static final int CDMA_RETRY_ORDER = 1005;
- /** Unable to obtain access to the CDMA system */
+ /** Unable to obtain access to the CDMA system. */
public static final int CDMA_ACCESS_FAILURE = 1006;
- /** Not a preempted call */
+ /** Not a preempted call. */
public static final int CDMA_PREEMPTED = 1007;
- /** Not an emergency call */
+ /** Not an emergency call. */
public static final int CDMA_NOT_EMERGENCY = 1008;
- /** Access Blocked by CDMA network */
+ /** Access Blocked by CDMA network. */
public static final int CDMA_ACCESS_BLOCKED = 1009;
/** Mapped from ImsReasonInfo */
+ // TODO: remove ImsReasonInfo from preciseDisconnectCause
/* The passed argument is an invalid */
+ /** @hide */
public static final int LOCAL_ILLEGAL_ARGUMENT = 1200;
// The operation is invoked in invalid call state
+ /** @hide */
public static final int LOCAL_ILLEGAL_STATE = 1201;
// IMS service internal error
+ /** @hide */
public static final int LOCAL_INTERNAL_ERROR = 1202;
// IMS service goes down (service connection is lost)
+ /** @hide */
public static final int LOCAL_IMS_SERVICE_DOWN = 1203;
// No pending incoming call exists
+ /** @hide */
public static final int LOCAL_NO_PENDING_CALL = 1204;
// Service unavailable; by power off
+ /** @hide */
public static final int LOCAL_POWER_OFF = 1205;
// Service unavailable; by low battery
+ /** @hide */
public static final int LOCAL_LOW_BATTERY = 1206;
// Service unavailable; by out of service (data service state)
+ /** @hide */
public static final int LOCAL_NETWORK_NO_SERVICE = 1207;
/* Service unavailable; by no LTE coverage
* (VoLTE is not supported even though IMS is registered)
*/
+ /** @hide */
public static final int LOCAL_NETWORK_NO_LTE_COVERAGE = 1208;
/** Service unavailable; by located in roaming area */
+ /** @hide */
public static final int LOCAL_NETWORK_ROAMING = 1209;
/** Service unavailable; by IP changed */
+ /** @hide */
public static final int LOCAL_NETWORK_IP_CHANGED = 1210;
/** Service unavailable; other */
+ /** @hide */
public static final int LOCAL_SERVICE_UNAVAILABLE = 1211;
/* Service unavailable; IMS connection is lost (IMS is not registered) */
+ /** @hide */
public static final int LOCAL_NOT_REGISTERED = 1212;
/** Max call exceeded */
+ /** @hide */
public static final int LOCAL_MAX_CALL_EXCEEDED = 1213;
/** Call decline */
+ /** @hide */
public static final int LOCAL_CALL_DECLINE = 1214;
/** SRVCC is in progress */
+ /** @hide */
public static final int LOCAL_CALL_VCC_ON_PROGRESSING = 1215;
/** Resource reservation is failed (QoS precondition) */
+ /** @hide */
public static final int LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 1216;
/** Retry CS call; VoLTE service can't be provided by the network or remote end
* Resolve the extra code(EXTRA_CODE_CALL_RETRY_*) if the below code is set
+ * @hide
*/
public static final int LOCAL_CALL_CS_RETRY_REQUIRED = 1217;
/** Retry VoLTE call; VoLTE service can't be provided by the network temporarily */
+ /** @hide */
public static final int LOCAL_CALL_VOLTE_RETRY_REQUIRED = 1218;
/** IMS call is already terminated (in TERMINATED state) */
+ /** @hide */
public static final int LOCAL_CALL_TERMINATED = 1219;
/** Handover not feasible */
+ /** @hide */
public static final int LOCAL_HO_NOT_FEASIBLE = 1220;
/** 1xx waiting timer is expired after sending INVITE request (MO only) */
+ /** @hide */
public static final int TIMEOUT_1XX_WAITING = 1221;
/** User no answer during call setup operation (MO/MT)
* MO : 200 OK to INVITE request is not received,
* MT : No action from user after alerting the call
+ * @hide
*/
public static final int TIMEOUT_NO_ANSWER = 1222;
/** User no answer during call update operation (MO/MT)
* MO : 200 OK to re-INVITE request is not received,
* MT : No action from user after alerting the call
+ * @hide
*/
public static final int TIMEOUT_NO_ANSWER_CALL_UPDATE = 1223;
@@ -296,102 +346,142 @@
* STATUSCODE (SIP response code) (IMS -> Telephony)
*/
/** SIP request is redirected */
+ /** @hide */
public static final int SIP_REDIRECTED = 1300;
/** 4xx responses */
/** 400 : Bad Request */
+ /** @hide */
public static final int SIP_BAD_REQUEST = 1310;
/** 403 : Forbidden */
+ /** @hide */
public static final int SIP_FORBIDDEN = 1311;
/** 404 : Not Found */
+ /** @hide */
public static final int SIP_NOT_FOUND = 1312;
/** 415 : Unsupported Media Type
* 416 : Unsupported URI Scheme
* 420 : Bad Extension
*/
+ /** @hide */
public static final int SIP_NOT_SUPPORTED = 1313;
/** 408 : Request Timeout */
+ /** @hide */
public static final int SIP_REQUEST_TIMEOUT = 1314;
/** 480 : Temporarily Unavailable */
+ /** @hide */
public static final int SIP_TEMPRARILY_UNAVAILABLE = 1315;
/** 484 : Address Incomplete */
+ /** @hide */
public static final int SIP_BAD_ADDRESS = 1316;
/** 486 : Busy Here
* 600 : Busy Everywhere
*/
+ /** @hide */
public static final int SIP_BUSY = 1317;
/** 487 : Request Terminated */
+ /** @hide */
public static final int SIP_REQUEST_CANCELLED = 1318;
/** 406 : Not Acceptable
* 488 : Not Acceptable Here
* 606 : Not Acceptable
*/
+ /** @hide */
public static final int SIP_NOT_ACCEPTABLE = 1319;
/** 410 : Gone
* 604 : Does Not Exist Anywhere
*/
+ /** @hide */
public static final int SIP_NOT_REACHABLE = 1320;
/** Others */
+ /** @hide */
public static final int SIP_CLIENT_ERROR = 1321;
/** 481 : Transaction Does Not Exist */
+ /** @hide */
public static final int SIP_TRANSACTION_DOES_NOT_EXIST = 1322;
/** 5xx responses
* 501 : Server Internal Error
*/
+ /** @hide */
public static final int SIP_SERVER_INTERNAL_ERROR = 1330;
/** 503 : Service Unavailable */
+ /** @hide */
public static final int SIP_SERVICE_UNAVAILABLE = 1331;
/** 504 : Server Time-out */
+ /** @hide */
public static final int SIP_SERVER_TIMEOUT = 1332;
/** Others */
+ /** @hide */
public static final int SIP_SERVER_ERROR = 1333;
/** 6xx responses
* 603 : Decline
*/
+ /** @hide */
public static final int SIP_USER_REJECTED = 1340;
/** Others */
+ /** @hide */
public static final int SIP_GLOBAL_ERROR = 1341;
/** Emergency failure */
+ /** @hide */
public static final int EMERGENCY_TEMP_FAILURE = 1342;
+ /** @hide */
public static final int EMERGENCY_PERM_FAILURE = 1343;
/** Media resource initialization failed */
+ /** @hide */
public static final int MEDIA_INIT_FAILED = 1400;
/** RTP timeout (no audio / video traffic in the session) */
+ /** @hide */
public static final int MEDIA_NO_DATA = 1401;
/** Media is not supported; so dropped the call */
+ /** @hide */
public static final int MEDIA_NOT_ACCEPTABLE = 1402;
/** Unknown media related errors */
+ /** @hide */
public static final int MEDIA_UNSPECIFIED = 1403;
/** User triggers the call end */
+ /** @hide */
public static final int USER_TERMINATED = 1500;
/** No action while an incoming call is ringing */
+ /** @hide */
public static final int USER_NOANSWER = 1501;
/** User ignores an incoming call */
+ /** @hide */
public static final int USER_IGNORE = 1502;
/** User declines an incoming call */
+ /** @hide */
public static final int USER_DECLINE = 1503;
/** Device declines/ends a call due to low battery */
+ /** @hide */
public static final int LOW_BATTERY = 1504;
/** Device declines call due to blacklisted call ID */
+ /** @hide */
public static final int BLACKLISTED_CALL_ID = 1505;
/** The call is terminated by the network or remote user */
+ /** @hide */
public static final int USER_TERMINATED_BY_REMOTE = 1510;
/**
* UT
*/
+ /** @hide */
public static final int UT_NOT_SUPPORTED = 1800;
+ /** @hide */
public static final int UT_SERVICE_UNAVAILABLE = 1801;
+ /** @hide */
public static final int UT_OPERATION_NOT_ALLOWED = 1802;
+ /** @hide */
public static final int UT_NETWORK_ERROR = 1803;
+ /** @hide */
public static final int UT_CB_PASSWORD_MISMATCH = 1804;
/**
* ECBM
+ * @hide
*/
public static final int ECBM_NOT_SUPPORTED = 1900;
/**
* Fail code used to indicate that Multi-endpoint is not supported by the Ims framework.
+ * @hide
*/
public static final int MULTIENDPOINT_NOT_SUPPORTED = 1901;
@@ -405,56 +495,68 @@
* active wifi call and at the edge of coverage and there is no qualified LTE network available
* to handover the call to. We get a handover NOT_TRIGERRED message from the modem. This error
* code is received as part of the handover message.
+ * @hide
*/
public static final int CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 2000;
/**
* MT call has ended due to a release from the network
* because the call was answered elsewhere
+ * @hide
*/
public static final int ANSWERED_ELSEWHERE = 2100;
/**
* For MultiEndpoint - Call Pull request has failed
+ * @hide
*/
public static final int CALL_PULL_OUT_OF_SYNC = 2101;
/**
* For MultiEndpoint - Call has been pulled from primary to secondary
+ * @hide
*/
public static final int CALL_PULLED = 2102;
/**
* Supplementary services (HOLD/RESUME) failure error codes.
* Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision.
+ * @hide
*/
public static final int SUPP_SVC_FAILED = 2300;
+ /** @hide */
public static final int SUPP_SVC_CANCELLED = 2301;
+ /** @hide */
public static final int SUPP_SVC_REINVITE_COLLISION = 2302;
/**
* DPD Procedure received no response or send failed
+ * @hide
*/
public static final int IWLAN_DPD_FAILURE = 2400;
/**
* Establishment of the ePDG Tunnel Failed
+ * @hide
*/
public static final int EPDG_TUNNEL_ESTABLISH_FAILURE = 2500;
/**
* Re-keying of the ePDG Tunnel Failed; may not always result in teardown
+ * @hide
*/
public static final int EPDG_TUNNEL_REKEY_FAILURE = 2501;
/**
* Connection to the packet gateway is lost
+ * @hide
*/
public static final int EPDG_TUNNEL_LOST_CONNECTION = 2502;
/**
* The maximum number of calls allowed has been reached. Used in a multi-endpoint scenario
* where the number of calls across all connected devices has reached the maximum.
+ * @hide
*/
public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 2503;
@@ -462,21 +564,25 @@
* Similar to {@link #CODE_LOCAL_CALL_DECLINE}, except indicates that a remote device has
* declined the call. Used in a multi-endpoint scenario where a remote device declined an
* incoming call.
+ * @hide
*/
public static final int REMOTE_CALL_DECLINE = 2504;
/**
* Indicates the call was disconnected due to the user reaching their data limit.
+ * @hide
*/
public static final int DATA_LIMIT_REACHED = 2505;
/**
* Indicates the call was disconnected due to the user disabling cellular data.
+ * @hide
*/
public static final int DATA_DISABLED = 2506;
/**
* Indicates a call was disconnected due to loss of wifi signal.
+ * @hide
*/
public static final int WIFI_LOST = 2507;
@@ -499,7 +605,7 @@
public static final int OEM_CAUSE_14 = 0xf00e;
public static final int OEM_CAUSE_15 = 0xf00f;
- /** Disconnected due to unspecified reasons */
+ /** Disconnected due to unspecified reasons. */
public static final int ERROR_UNSPECIFIED = 0xffff;
/** Private constructor to avoid class instantiation. */
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index a7e8e8a..a1e8b19 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -138,10 +138,15 @@
private UiccAccessRule[] mAccessRules;
/**
- * The ID of the SIM card. It is the ICCID of the active profile for a UICC card and the EID
- * for an eUICC card.
+ * The string ID of the SIM card. It is the ICCID of the active profile for a UICC card and the
+ * EID for an eUICC card.
*/
- private String mCardId;
+ private String mCardString;
+
+ /**
+ * The card ID of the SIM card. This maps uniquely to the card string.
+ */
+ private int mCardId;
/**
* Whether the subscription is opportunistic.
@@ -174,9 +179,9 @@
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
- @Nullable UiccAccessRule[] accessRules, String cardId) {
+ @Nullable UiccAccessRule[] accessRules, String cardString) {
this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
- roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardId,
+ roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString,
false, null, true, TelephonyManager.UNKNOWN_CARRIER_ID);
}
@@ -186,20 +191,22 @@
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
- @Nullable UiccAccessRule[] accessRules, String cardId, boolean isOpportunistic,
+ @Nullable UiccAccessRule[] accessRules, String cardString, boolean isOpportunistic,
@Nullable String groupUUID, boolean isMetered, int carrierId) {
this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
- roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardId,
+ roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString, -1,
isOpportunistic, groupUUID, isMetered, false, carrierId);
}
+
/**
* @hide
*/
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
- @Nullable UiccAccessRule[] accessRules, String cardId, boolean isOpportunistic,
- @Nullable String groupUUID, boolean isMetered, boolean isGroupDisabled, int carrierid) {
+ @Nullable UiccAccessRule[] accessRules, String cardString, int cardId,
+ boolean isOpportunistic, @Nullable String groupUUID, boolean isMetered,
+ boolean isGroupDisabled, int carrierid) {
this.mId = id;
this.mIccId = iccId;
this.mSimSlotIndex = simSlotIndex;
@@ -215,6 +222,7 @@
this.mCountryIso = countryIso;
this.mIsEmbedded = isEmbedded;
this.mAccessRules = accessRules;
+ this.mCardString = cardString;
this.mCardId = cardId;
this.mIsOpportunistic = isOpportunistic;
this.mGroupUUID = groupUUID;
@@ -523,10 +531,21 @@
}
/**
- * @return the ID of the SIM card which contains the subscription.
+ * @return the card string of the SIM card which contains the subscription. The card string is
+ * the ICCID for UICCs or the EID for eUICCs.
+ * @hide
+ * //TODO rename usages in LPA: UiccSlotUtil.java, UiccSlotsManager.java, UiccSlotInfoTest.java
+ */
+ public String getCardString() {
+ return this.mCardString;
+ }
+
+ /**
+ * @return the cardId of the SIM card which contains the subscription.
* @hide
*/
- public String getCardId() {
+ @SystemApi
+ public int getCardId() {
return this.mCardId;
}
@@ -564,7 +583,8 @@
Bitmap iconBitmap = Bitmap.CREATOR.createFromParcel(source);
boolean isEmbedded = source.readBoolean();
UiccAccessRule[] accessRules = source.createTypedArray(UiccAccessRule.CREATOR);
- String cardId = source.readString();
+ String cardString = source.readString();
+ int cardId = source.readInt();
boolean isOpportunistic = source.readBoolean();
String groupUUID = source.readString();
boolean isMetered = source.readBoolean();
@@ -573,8 +593,8 @@
return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso,
- isEmbedded, accessRules, cardId, isOpportunistic, groupUUID, isMetered,
- isGroupDisabled, carrierid);
+ isEmbedded, accessRules, cardString, cardId, isOpportunistic, groupUUID,
+ isMetered, isGroupDisabled, carrierid);
}
@Override
@@ -600,7 +620,8 @@
mIconBitmap.writeToParcel(dest, flags);
dest.writeBoolean(mIsEmbedded);
dest.writeTypedArray(mAccessRules, flags);
- dest.writeString(mCardId);
+ dest.writeString(mCardString);
+ dest.writeInt(mCardId);
dest.writeBoolean(mIsOpportunistic);
dest.writeString(mGroupUUID);
dest.writeBoolean(mIsMetered);
@@ -631,7 +652,7 @@
@Override
public String toString() {
String iccIdToPrint = givePrintableIccid(mIccId);
- String cardIdToPrint = givePrintableIccid(mCardId);
+ String cardStringToPrint = givePrintableIccid(mCardString);
return "{id=" + mId + ", iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex
+ " carrierId=" + mCarrierId + " displayName=" + mDisplayName
+ " carrierName=" + mCarrierName + " nameSource=" + mNameSource
@@ -639,17 +660,17 @@
+ " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
+ " mnc " + mMnc + "mCountryIso=" + mCountryIso + " isEmbedded " + mIsEmbedded
+ " accessRules " + Arrays.toString(mAccessRules)
- + " cardId=" + cardIdToPrint + " isOpportunistic " + mIsOpportunistic
- + " mGroupUUID=" + mGroupUUID + " isMetered=" + mIsMetered
- + " mIsGroupDisabled=" + mIsGroupDisabled + "}";
+ + " cardString=" + cardStringToPrint + " cardId=" + mCardId
+ + " isOpportunistic " + mIsOpportunistic + " mGroupUUID=" + mGroupUUID
+ + " isMetered=" + mIsMetered + " mIsGroupDisabled=" + mIsGroupDisabled + "}";
}
@Override
public int hashCode() {
return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
mIsOpportunistic, mGroupUUID, mIsMetered, mIccId, mNumber, mMcc, mMnc,
- mCountryIso, mCardId, mDisplayName, mCarrierName, mAccessRules, mIsGroupDisabled,
- mCarrierId);
+ mCountryIso, mCardString, mCardId, mDisplayName, mCarrierName, mAccessRules,
+ mIsGroupDisabled, mCarrierId);
}
@Override
@@ -680,6 +701,7 @@
&& Objects.equals(mMcc, toCompare.mMcc)
&& Objects.equals(mMnc, toCompare.mMnc)
&& Objects.equals(mCountryIso, toCompare.mCountryIso)
+ && Objects.equals(mCardString, toCompare.mCardString)
&& Objects.equals(mCardId, toCompare.mCardId)
&& TextUtils.equals(mDisplayName, toCompare.mDisplayName)
&& TextUtils.equals(mCarrierName, toCompare.mCarrierName)
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e0632b1..c7df36b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -625,8 +625,6 @@
* The {@link #EXTRA_RINGING_CALL_STATE} extra indicates the ringing call state.
* The {@link #EXTRA_FOREGROUND_CALL_STATE} extra indicates the foreground call state.
* The {@link #EXTRA_BACKGROUND_CALL_STATE} extra indicates the background call state.
- * The {@link #EXTRA_DISCONNECT_CAUSE} extra indicates the disconnect cause.
- * The {@link #EXTRA_PRECISE_DISCONNECT_CAUSE} extra indicates the precise disconnect cause.
*
* <p class="note">
* Requires the READ_PRECISE_PHONE_STATE permission.
@@ -634,12 +632,10 @@
* @see #EXTRA_RINGING_CALL_STATE
* @see #EXTRA_FOREGROUND_CALL_STATE
* @see #EXTRA_BACKGROUND_CALL_STATE
- * @see #EXTRA_DISCONNECT_CAUSE
- * @see #EXTRA_PRECISE_DISCONNECT_CAUSE
*
* <p class="note">
* Requires the READ_PRECISE_PHONE_STATE permission.
- *
+ * @deprecated use {@link PhoneStateListener#LISTEN_PRECISE_CALL_STATE} instead
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@@ -647,8 +643,28 @@
"android.intent.action.PRECISE_CALL_STATE";
/**
- * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
- * for an integer containing the state of the current ringing call.
+ * Broadcast intent action indicating that call disconnect cause has changed.
+ *
+ * <p>
+ * The {@link #EXTRA_DISCONNECT_CAUSE} extra indicates the disconnect cause.
+ * The {@link #EXTRA_PRECISE_DISCONNECT_CAUSE} extra indicates the precise disconnect cause.
+ *
+ * <p class="note">
+ * Requires the READ_PRECISE_PHONE_STATE permission.
+ *
+ * @see #EXTRA_DISCONNECT_CAUSE
+ * @see #EXTRA_PRECISE_DISCONNECT_CAUSE
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CALL_DISCONNECT_CAUSE_CHANGED =
+ "android.intent.action.CALL_DISCONNECT_CAUSE";
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
+ * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
+ * containing the state of the current ringing call.
*
* @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
* @see PreciseCallState#PRECISE_CALL_STATE_IDLE
@@ -670,8 +686,9 @@
public static final String EXTRA_RINGING_CALL_STATE = "ringing_state";
/**
- * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
- * for an integer containing the state of the current foreground call.
+ * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
+ * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
+ * containing the state of the current foreground call.
*
* @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
* @see PreciseCallState#PRECISE_CALL_STATE_IDLE
@@ -693,8 +710,9 @@
public static final String EXTRA_FOREGROUND_CALL_STATE = "foreground_state";
/**
- * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
- * for an integer containing the state of the current background call.
+ * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
+ * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
+ * containing the state of the current background call.
*
* @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
* @see PreciseCallState#PRECISE_CALL_STATE_IDLE
@@ -716,8 +734,9 @@
public static final String EXTRA_BACKGROUND_CALL_STATE = "background_state";
/**
- * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
- * for an integer containing the disconnect cause.
+ * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
+ * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
+ * containing the disconnect cause.
*
* @see DisconnectCause
*
@@ -730,8 +749,9 @@
public static final String EXTRA_DISCONNECT_CAUSE = "disconnect_cause";
/**
- * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
- * for an integer containing the disconnect cause provided by the RIL.
+ * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
+ * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
+ * containing the disconnect cause provided by the RIL.
*
* @see PreciseDisconnectCause
*
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 78fc0bc4..00cf9c3 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -55,5 +55,6 @@
void onPreferredDataSubIdChanged(in int subId);
void onRadioPowerStateChanged(in int state);
void onEmergencyNumberListChanged(in Map emergencyNumberList);
+ void onCallDisconnectCauseChanged(in int disconnectCause, in int preciseDisconnectCause);
}