Merge "Added missing protobuf for PeopleService data persistence."
diff --git a/api/current.txt b/api/current.txt
index c2bbb6d..7bfd9a4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -289,7 +289,7 @@
field public static final int allowBackup = 16843392; // 0x1010280
field public static final int allowClearUserData = 16842757; // 0x1010005
field public static final int allowEmbedded = 16843765; // 0x10103f5
- field public static final int allowNativeHeapPointerTagging = 16844311; // 0x1010617
+ field public static final int allowNativeHeapPointerTagging = 16844307; // 0x1010613
field public static final int allowParallelSyncs = 16843570; // 0x1010332
field public static final int allowSingleTap = 16843353; // 0x1010259
field public static final int allowTaskReparenting = 16843268; // 0x1010204
@@ -335,9 +335,6 @@
field public static final int autoUrlDetect = 16843404; // 0x101028c
field public static final int autoVerify = 16844014; // 0x10104ee
field public static final int autofillHints = 16844118; // 0x1010556
- field public static final int autofillInlineSuggestionChip = 16844307; // 0x1010613
- field public static final int autofillInlineSuggestionSubtitle = 16844309; // 0x1010615
- field public static final int autofillInlineSuggestionTitle = 16844308; // 0x1010614
field public static final int autofilledHighlight = 16844136; // 0x1010568
field public static final int background = 16842964; // 0x10100d4
field public static final int backgroundDimAmount = 16842802; // 0x1010032
@@ -1082,6 +1079,7 @@
field public static final int preferenceScreenStyle = 16842891; // 0x101008b
field public static final int preferenceStyle = 16842894; // 0x101008e
field public static final int presentationTheme = 16843712; // 0x10103c0
+ field public static final int preserveLegacyExternalStorage = 16844308; // 0x1010614
field public static final int previewImage = 16843482; // 0x10102da
field public static final int primaryContentAlpha = 16844114; // 0x1010552
field public static final int priority = 16842780; // 0x101001c
@@ -2259,7 +2257,6 @@
field public static final int ThemeOverlay_Material_Dialog = 16974550; // 0x10302d6
field public static final int ThemeOverlay_Material_Dialog_Alert = 16974551; // 0x10302d7
field public static final int ThemeOverlay_Material_Light = 16974410; // 0x103024a
- field public static final int Theme_AutofillInlineSuggestion = 16974565; // 0x10302e5
field public static final int Theme_Black = 16973832; // 0x1030008
field public static final int Theme_Black_NoTitleBar = 16973833; // 0x1030009
field public static final int Theme_Black_NoTitleBar_Fullscreen = 16973834; // 0x103000a
@@ -43970,7 +43967,7 @@
package android.service.quickaccesswallet {
- public final class GetWalletCardsCallback {
+ public interface GetWalletCardsCallback {
method public void onFailure(@NonNull android.service.quickaccesswallet.GetWalletCardsError);
method public void onSuccess(@NonNull android.service.quickaccesswallet.GetWalletCardsResponse);
}
@@ -44011,7 +44008,6 @@
method public abstract void onWalletCardsRequested(@NonNull android.service.quickaccesswallet.GetWalletCardsRequest, @NonNull android.service.quickaccesswallet.GetWalletCardsCallback);
method public abstract void onWalletDismissed();
method public final void sendWalletServiceEvent(@NonNull android.service.quickaccesswallet.WalletServiceEvent);
- field public static final String ACTION_DISMISS_WALLET = "android.service.quickaccesswallet.action.DISMISS_WALLET";
field public static final String ACTION_VIEW_WALLET = "android.service.quickaccesswallet.action.VIEW_WALLET";
field public static final String ACTION_VIEW_WALLET_SETTINGS = "android.service.quickaccesswallet.action.VIEW_WALLET_SETTINGS";
field public static final String SERVICE_INTERFACE = "android.service.quickaccesswallet.QuickAccessWalletService";
diff --git a/api/system-current.txt b/api/system-current.txt
index c6d4eb3..773cd4d 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -257,7 +257,6 @@
public static final class R.attr {
field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
- field public static final int isAutofillInlineSuggestionTheme = 16844310; // 0x1010616
field public static final int isVrOnly = 16844152; // 0x1010578
field public static final int minExtensionVersion = 16844306; // 0x1010612
field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
@@ -4912,8 +4911,10 @@
}
public final class TunerConstants {
- field public static final int INVALID_STREAM_ID = -1; // 0xffffffff
- field public static final int INVALID_TS_PID = -1; // 0xffffffff
+ field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff
+ field public static final int INVALID_FILTER_ID = -1; // 0xffffffff
+ field public static final int INVALID_STREAM_ID = 65535; // 0xffff
+ field public static final int INVALID_TS_PID = 65535; // 0xffff
field public static final int RESULT_INVALID_ARGUMENT = 4; // 0x4
field public static final int RESULT_INVALID_STATE = 3; // 0x3
field public static final int RESULT_NOT_INITIALIZED = 2; // 0x2
@@ -10202,6 +10203,7 @@
method public void onConnected();
method public void onDisconnected();
method public void onFillRequest(@NonNull android.service.autofill.augmented.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.augmented.FillController, @NonNull android.service.autofill.augmented.FillCallback);
+ method public final boolean requestAutofill(@NonNull android.content.ComponentName, @NonNull android.view.autofill.AutofillId);
field public static final String SERVICE_INTERFACE = "android.service.autofill.augmented.AugmentedAutofillService";
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 7284fe6..0a821e4 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2976,6 +2976,9 @@
field public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
field public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis";
field public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis";
+ field public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications";
+ field public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications";
+ field public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
field public static final String NOTIFICATION_BADGING = "notification_badging";
field @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
field public static final String USER_SETUP_COMPLETE = "user_setup_complete";
@@ -3202,6 +3205,7 @@
method public void onConnected();
method public void onDisconnected();
method public void onFillRequest(@NonNull android.service.autofill.augmented.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.augmented.FillController, @NonNull android.service.autofill.augmented.FillCallback);
+ method public final boolean requestAutofill(@NonNull android.content.ComponentName, @NonNull android.view.autofill.AutofillId);
field public static final String SERVICE_INTERFACE = "android.service.autofill.augmented.AugmentedAutofillService";
}
@@ -3399,6 +3403,34 @@
}
+package android.service.quickaccesswallet {
+
+ public interface QuickAccessWalletClient {
+ method public void addWalletServiceEventListener(@NonNull android.service.quickaccesswallet.QuickAccessWalletClient.WalletServiceEventListener);
+ method @NonNull public static android.service.quickaccesswallet.QuickAccessWalletClient create(@NonNull android.content.Context);
+ method @Nullable public android.content.Intent createWalletIntent();
+ method @Nullable public android.content.Intent createWalletSettingsIntent();
+ method public void disconnect();
+ method public void getWalletCards(@NonNull android.service.quickaccesswallet.GetWalletCardsRequest, @NonNull android.service.quickaccesswallet.QuickAccessWalletClient.OnWalletCardsRetrievedCallback);
+ method public boolean isWalletFeatureAvailable();
+ method public boolean isWalletFeatureAvailableWhenDeviceLocked();
+ method public boolean isWalletServiceAvailable();
+ method public void notifyWalletDismissed();
+ method public void removeWalletServiceEventListener(@NonNull android.service.quickaccesswallet.QuickAccessWalletClient.WalletServiceEventListener);
+ method public void selectWalletCard(@NonNull android.service.quickaccesswallet.SelectWalletCardRequest);
+ }
+
+ public static interface QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
+ method public void onWalletCardRetrievalError(@NonNull android.service.quickaccesswallet.GetWalletCardsError);
+ method public void onWalletCardsRetrieved(@NonNull android.service.quickaccesswallet.GetWalletCardsResponse);
+ }
+
+ public static interface QuickAccessWalletClient.WalletServiceEventListener {
+ method public void onWalletServiceEvent(@NonNull android.service.quickaccesswallet.WalletServiceEvent);
+ }
+
+}
+
package android.service.quicksettings {
public class TileService extends android.app.Service {
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 956fd29..2ee055d 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -123,11 +123,11 @@
"libutils",
],
shared_libs: [
- "libbinder",
+ "libbinder_ndk",
"libincident",
"liblog",
"libstatssocket",
- "statsd-aidl-cpp",
+ "statsd-aidl-ndk_platform",
],
}
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 6b9d0e4..d95d594 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -25,6 +25,7 @@
using std::string;
using std::vector;
+using android::base::StringPrintf;
// These constants must be kept in sync with those in StatsDimensionsValue.java
const static int STATS_DIMENSIONS_VALUE_STRING_TYPE = 2;
@@ -73,7 +74,7 @@
break;
case STRING:
childParcel.valueType = STATS_DIMENSIONS_VALUE_STRING_TYPE;
- childParcel.stringValue = String16(dim.mValue.str_value.c_str());
+ childParcel.stringValue = dim.mValue.str_value;
break;
default:
ALOGE("Encountered FieldValue with unsupported value type.");
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 4adcf96..a766bba 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -16,7 +16,7 @@
#pragma once
-#include <android/os/StatsDimensionsValueParcel.h>
+#include <aidl/android/os/StatsDimensionsValueParcel.h>
#include <utils/JenkinsHash.h>
#include <vector>
#include "android-base/stringprintf.h"
@@ -27,7 +27,7 @@
namespace os {
namespace statsd {
-using android::base::StringPrintf;
+using ::aidl::android::os::StatsDimensionsValueParcel;
struct Metric2Condition {
int64_t conditionId;
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 168833f..7087c68 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -28,8 +28,6 @@
#include <android-base/file.h>
#include <android-base/strings.h>
-#include <binder/IPCThreadState.h>
-#include <binder/PermissionController.h>
#include <cutils/multiuser.h>
#include <frameworks/base/cmds/statsd/src/statsd_config.pb.h>
#include <frameworks/base/cmds/statsd/src/uid_data.pb.h>
@@ -47,85 +45,42 @@
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_MESSAGE;
+using Status = ::ndk::ScopedAStatus;
+
namespace android {
namespace os {
namespace statsd {
constexpr const char* kPermissionDump = "android.permission.DUMP";
-constexpr const char* kPermissionUsage = "android.permission.PACKAGE_USAGE_STATS";
-
-constexpr const char* kOpUsage = "android:get_usage_stats";
#define STATS_SERVICE_DIR "/data/misc/stats-service"
// for StatsDataDumpProto
const int FIELD_ID_REPORTS_LIST = 1;
-static binder::Status ok() {
- return binder::Status::ok();
-}
-
-static binder::Status exception(uint32_t code, const std::string& msg) {
+static Status exception(int32_t code, const std::string& msg) {
ALOGE("%s (%d)", msg.c_str(), code);
- return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
+ return ::ndk::ScopedAStatus(AStatus_fromExceptionCodeWithMessage(code, msg.c_str()));
}
static bool checkPermission(const char* permission) {
- pid_t pid = IPCThreadState::self()->getCallingPid();
- uid_t uid = IPCThreadState::self()->getCallingUid();
+ pid_t pid = AIBinder_getCallingPid();
+ uid_t uid = AIBinder_getCallingUid();
return checkPermissionForIds(permission, pid, uid);
}
-binder::Status checkUid(uid_t expectedUid) {
- uid_t uid = IPCThreadState::self()->getCallingUid();
+Status checkUid(uid_t expectedUid) {
+ uid_t uid = AIBinder_getCallingUid();
if (uid == expectedUid || uid == AID_ROOT) {
- return ok();
+ return Status::ok();
} else {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d is not expected UID %d", uid, expectedUid));
- }
-}
-
-binder::Status checkDumpAndUsageStats(const String16& packageName) {
- pid_t pid = IPCThreadState::self()->getCallingPid();
- uid_t uid = IPCThreadState::self()->getCallingUid();
-
- // Root, system, and shell always have access
- if (uid == AID_ROOT || uid == AID_SYSTEM || uid == AID_SHELL) {
- return ok();
- }
-
- // Caller must be granted these permissions
- if (!checkPermission(kPermissionDump)) {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, kPermissionDump));
- }
- if (!checkPermission(kPermissionUsage)) {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, kPermissionUsage));
- }
-
- // Caller must also have usage stats op granted
- PermissionController pc;
- switch (pc.noteOp(String16(kOpUsage), uid, packageName)) {
- case PermissionController::MODE_ALLOWED:
- case PermissionController::MODE_DEFAULT:
- return ok();
- default:
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d / PID %d lacks app-op %s", uid, pid, kOpUsage));
+ return exception(EX_SECURITY,
+ StringPrintf("UID %d is not expected UID %d", uid, expectedUid));
}
}
#define ENFORCE_UID(uid) { \
- binder::Status status = checkUid((uid)); \
- if (!status.isOk()) { \
- return status; \
- } \
-}
-
-#define ENFORCE_DUMP_AND_USAGE_STATS(packageName) { \
- binder::Status status = checkDumpAndUsageStats(packageName); \
+ Status status = checkUid((uid)); \
if (!status.isOk()) { \
return status; \
} \
@@ -134,13 +89,13 @@
StatsService::StatsService(const sp<Looper>& handlerLooper, shared_ptr<LogEventQueue> queue)
: mAnomalyAlarmMonitor(new AlarmMonitor(
MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS,
- [](const sp<IStatsCompanionService>& sc, int64_t timeMillis) {
+ [](const shared_ptr<IStatsCompanionService>& sc, int64_t timeMillis) {
if (sc != nullptr) {
sc->setAnomalyAlarm(timeMillis);
StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
}
},
- [](const sp<IStatsCompanionService>& sc) {
+ [](const shared_ptr<IStatsCompanionService>& sc) {
if (sc != nullptr) {
sc->cancelAnomalyAlarm();
StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
@@ -148,19 +103,21 @@
})),
mPeriodicAlarmMonitor(new AlarmMonitor(
MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS,
- [](const sp<IStatsCompanionService>& sc, int64_t timeMillis) {
+ [](const shared_ptr<IStatsCompanionService>& sc, int64_t timeMillis) {
if (sc != nullptr) {
sc->setAlarmForSubscriberTriggering(timeMillis);
StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged();
}
},
- [](const sp<IStatsCompanionService>& sc) {
+ [](const shared_ptr<IStatsCompanionService>& sc) {
if (sc != nullptr) {
sc->cancelAlarmForSubscriberTriggering();
StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged();
}
})),
- mEventQueue(queue) {
+ mEventQueue(queue),
+ mStatsCompanionServiceDeathRecipient(AIBinder_DeathRecipient_new(
+ StatsService::statsCompanionServiceDied)) {
mUidMap = UidMap::getInstance();
mPullerManager = new StatsPullerManager();
StatsPuller::SetUidMap(mUidMap);
@@ -169,22 +126,20 @@
mUidMap, mPullerManager, mAnomalyAlarmMonitor, mPeriodicAlarmMonitor,
getElapsedRealtimeNs(),
[this](const ConfigKey& key) {
- sp<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key);
+ shared_ptr<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key);
if (receiver == nullptr) {
- VLOG("Could not find a broadcast receiver for %s",
- key.ToString().c_str());
+ VLOG("Could not find a broadcast receiver for %s", key.ToString().c_str());
return false;
} else if (receiver->sendDataBroadcast(
mProcessor->getLastReportTimeNs(key)).isOk()) {
return true;
} else {
- VLOG("Failed to send a broadcast for receiver %s",
- key.ToString().c_str());
+ VLOG("Failed to send a broadcast for receiver %s", key.ToString().c_str());
return false;
}
},
[this](const int& uid, const vector<int64_t>& activeConfigs) {
- sp<IPendingIntentRef> receiver =
+ shared_ptr<IPendingIntentRef> receiver =
mConfigManager->GetActiveConfigsChangedReceiver(uid);
if (receiver == nullptr) {
VLOG("Could not find receiver for uid %d", uid);
@@ -245,36 +200,6 @@
}
/**
- * Implement our own because the default binder implementation isn't
- * properly handling SHELL_COMMAND_TRANSACTION.
- */
-status_t StatsService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags) {
- switch (code) {
- case SHELL_COMMAND_TRANSACTION: {
- int in = data.readFileDescriptor();
- int out = data.readFileDescriptor();
- int err = data.readFileDescriptor();
- int argc = data.readInt32();
- Vector<String8> args;
- for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
- args.add(String8(data.readString16()));
- }
- sp<IShellCallback> shellCallback = IShellCallback::asInterface(data.readStrongBinder());
- sp<IResultReceiver> resultReceiver =
- IResultReceiver::asInterface(data.readStrongBinder());
-
- err = command(in, out, err, args, resultReceiver);
- if (resultReceiver != nullptr) {
- resultReceiver->send(err);
- }
- return NO_ERROR;
- }
- default: { return BnStatsd::onTransact(code, data, reply, flags); }
- }
-}
-
-/**
* Write data from statsd.
* Format for statsdStats: adb shell dumpsys stats --metadata [-v] [--proto]
* Format for data report: adb shell dumpsys stats [anything other than --metadata] [--proto]
@@ -283,20 +208,21 @@
* (bugreports call "adb shell dumpsys stats --dump-priority NORMAL -a --proto")
* TODO: Come up with a more robust method of enacting <serviceutils/PriorityDumper.h>.
*/
-status_t StatsService::dump(int fd, const Vector<String16>& args) {
+status_t StatsService::dump(int fd, const char** args, uint32_t numArgs) {
if (!checkPermission(kPermissionDump)) {
return PERMISSION_DENIED;
}
- int lastArg = args.size() - 1;
+
+ int lastArg = numArgs - 1;
bool asProto = false;
- if (lastArg >= 0 && !args[lastArg].compare(String16("--proto"))) { // last argument
+ if (lastArg >= 0 && string(args[lastArg]) == "--proto") { // last argument
asProto = true;
lastArg--;
}
- if (args.size() > 0 && !args[0].compare(String16("--metadata"))) { // first argument
+ if (numArgs > 0 && string(args[0]) == "--metadata") { // first argument
// Request is to dump statsd stats.
bool verbose = false;
- if (lastArg >= 0 && !args[lastArg].compare(String16("-v"))) {
+ if (lastArg >= 0 && string(args[lastArg]) == "-v") {
verbose = true;
lastArg--;
}
@@ -351,67 +277,72 @@
/**
* Implementation of the adb shell cmd stats command.
*/
-status_t StatsService::command(int in, int out, int err, Vector<String8>& args,
- sp<IResultReceiver> resultReceiver) {
- uid_t uid = IPCThreadState::self()->getCallingUid();
- if (uid != AID_ROOT && uid != AID_SHELL) {
+status_t StatsService::handleShellCommand(int in, int out, int err, const char** argv,
+ uint32_t argc) {
+ uid_t uid = AIBinder_getCallingUid();
+ if (uid != AID_ROOT || uid != AID_SHELL) {
return PERMISSION_DENIED;
}
- const int argCount = args.size();
- if (argCount >= 1) {
+ Vector<String8> utf8Args;
+ utf8Args.setCapacity(argc);
+ for (uint32_t i = 0; i < argc; i++) {
+ utf8Args.push(String8(argv[i]));
+ }
+
+ if (argc >= 1) {
// adb shell cmd stats config ...
- if (!args[0].compare(String8("config"))) {
- return cmd_config(in, out, err, args);
+ if (!utf8Args[0].compare(String8("config"))) {
+ return cmd_config(in, out, err, utf8Args);
}
- if (!args[0].compare(String8("print-uid-map"))) {
- return cmd_print_uid_map(out, args);
+ if (!utf8Args[0].compare(String8("print-uid-map"))) {
+ return cmd_print_uid_map(out, utf8Args);
}
- if (!args[0].compare(String8("dump-report"))) {
- return cmd_dump_report(out, args);
+ if (!utf8Args[0].compare(String8("dump-report"))) {
+ return cmd_dump_report(out, utf8Args);
}
- if (!args[0].compare(String8("pull-source")) && args.size() > 1) {
- return cmd_print_pulled_metrics(out, args);
+ if (!utf8Args[0].compare(String8("pull-source")) && argc > 1) {
+ return cmd_print_pulled_metrics(out, utf8Args);
}
- if (!args[0].compare(String8("send-broadcast"))) {
- return cmd_trigger_broadcast(out, args);
+ if (!utf8Args[0].compare(String8("send-broadcast"))) {
+ return cmd_trigger_broadcast(out, utf8Args);
}
- if (!args[0].compare(String8("print-stats"))) {
- return cmd_print_stats(out, args);
+ if (!utf8Args[0].compare(String8("print-stats"))) {
+ return cmd_print_stats(out, utf8Args);
}
- if (!args[0].compare(String8("meminfo"))) {
+ if (!utf8Args[0].compare(String8("meminfo"))) {
return cmd_dump_memory_info(out);
}
- if (!args[0].compare(String8("write-to-disk"))) {
+ if (!utf8Args[0].compare(String8("write-to-disk"))) {
return cmd_write_data_to_disk(out);
}
- if (!args[0].compare(String8("log-app-breadcrumb"))) {
- return cmd_log_app_breadcrumb(out, args);
+ if (!utf8Args[0].compare(String8("log-app-breadcrumb"))) {
+ return cmd_log_app_breadcrumb(out, utf8Args);
}
- if (!args[0].compare(String8("log-binary-push"))) {
- return cmd_log_binary_push(out, args);
+ if (!utf8Args[0].compare(String8("log-binary-push"))) {
+ return cmd_log_binary_push(out, utf8Args);
}
- if (!args[0].compare(String8("clear-puller-cache"))) {
+ if (!utf8Args[0].compare(String8("clear-puller-cache"))) {
return cmd_clear_puller_cache(out);
}
- if (!args[0].compare(String8("print-logs"))) {
- return cmd_print_logs(out, args);
+ if (!utf8Args[0].compare(String8("print-logs"))) {
+ return cmd_print_logs(out, utf8Args);
}
- if (!args[0].compare(String8("send-active-configs"))) {
- return cmd_trigger_active_config_broadcast(out, args);
+ if (!utf8Args[0].compare(String8("send-active-configs"))) {
+ return cmd_trigger_active_config_broadcast(out, utf8Args);
}
- if (!args[0].compare(String8("data-subscribe"))) {
+ if (!utf8Args[0].compare(String8("data-subscribe"))) {
{
std::lock_guard<std::mutex> lock(mShellSubscriberMutex);
if (mShellSubscriber == nullptr) {
@@ -419,14 +350,10 @@
}
}
int timeoutSec = -1;
- if (argCount >= 2) {
- timeoutSec = atoi(args[1].c_str());
+ if (argc >= 2) {
+ timeoutSec = atoi(utf8Args[1].c_str());
}
- if (resultReceiver == nullptr) {
- ALOGI("Null resultReceiver given, no subscription will be started");
- return UNEXPECTED_NULL;
- }
- mShellSubscriber->startNewSubscription(in, out, resultReceiver, timeoutSec);
+ mShellSubscriber->startNewSubscription(in, out, timeoutSec);
return NO_ERROR;
}
}
@@ -556,7 +483,7 @@
const int argCount = args.size();
if (argCount == 2) {
// Automatically pick the UID
- uid = IPCThreadState::self()->getCallingUid();
+ uid = AIBinder_getCallingUid();
name.assign(args[1].c_str(), args[1].size());
good = true;
} else if (argCount == 3) {
@@ -572,17 +499,15 @@
return UNKNOWN_ERROR;
}
ConfigKey key(uid, StrToInt64(name));
- sp<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key);
+ shared_ptr<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key);
if (receiver == nullptr) {
VLOG("Could not find receiver for %s, %s", args[1].c_str(), args[2].c_str());
return UNKNOWN_ERROR;
- } else if (receiver->sendDataBroadcast(
- mProcessor->getLastReportTimeNs(key)).isOk()) {
+ } else if (receiver->sendDataBroadcast(mProcessor->getLastReportTimeNs(key)).isOk()) {
VLOG("StatsService::trigger broadcast succeeded to %s, %s", args[1].c_str(),
args[2].c_str());
} else {
- VLOG("StatsService::trigger broadcast failed to %s, %s", args[1].c_str(),
- args[2].c_str());
+ VLOG("StatsService::trigger broadcast failed to %s, %s", args[1].c_str(), args[2].c_str());
return UNKNOWN_ERROR;
}
return NO_ERROR;
@@ -594,7 +519,7 @@
vector<int64_t> configIds;
if (argCount == 1) {
// Automatically pick the uid and send a broadcast that has no active configs.
- uid = IPCThreadState::self()->getCallingUid();
+ uid = AIBinder_getCallingUid();
mProcessor->GetActiveConfigs(uid, configIds);
} else {
int curArg = 1;
@@ -608,7 +533,7 @@
}
curArg++;
} else {
- uid = IPCThreadState::self()->getCallingUid();
+ uid = AIBinder_getCallingUid();
}
if (curArg == argCount || args[curArg] != "--configs") {
VLOG("Reached end of args, or specify configs not set. Sending actual active configs,");
@@ -628,7 +553,7 @@
}
}
}
- sp<IPendingIntentRef> receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid);
+ shared_ptr<IPendingIntentRef> receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid);
if (receiver == nullptr) {
VLOG("Could not find receiver for uid %d", uid);
return UNKNOWN_ERROR;
@@ -651,7 +576,7 @@
if (argCount == 3) {
// Automatically pick the UID
- uid = IPCThreadState::self()->getCallingUid();
+ uid = AIBinder_getCallingUid();
name.assign(args[2].c_str(), args[2].size());
good = true;
} else if (argCount == 4) {
@@ -734,7 +659,7 @@
}
if (argCount == 2) {
// Automatically pick the UID
- uid = IPCThreadState::self()->getCallingUid();
+ uid = AIBinder_getCallingUid();
name.assign(args[1].c_str(), args[1].size());
good = true;
} else if (argCount == 3) {
@@ -826,7 +751,7 @@
const int argCount = args.size();
if (argCount == 3) {
// Automatically pick the UID
- uid = IPCThreadState::self()->getCallingUid();
+ uid = AIBinder_getCallingUid();
label = atoi(args[1].c_str());
state = atoi(args[2].c_str());
good = true;
@@ -903,9 +828,8 @@
}
status_t StatsService::cmd_clear_puller_cache(int out) {
- IPCThreadState* ipc = IPCThreadState::self();
VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i",
- ipc->getCallingPid(), ipc->getCallingUid());
+ AIBinder_getCallingPid(), AIBinder_getCallingUid());
if (checkPermission(kPermissionDump)) {
int cleared = mPullerManager->ForceClearPullerCache();
dprintf(out, "Puller removed %d cached data!\n", cleared);
@@ -916,9 +840,8 @@
}
status_t StatsService::cmd_print_logs(int out, const Vector<String8>& args) {
- IPCThreadState* ipc = IPCThreadState::self();
- VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", ipc->getCallingPid(),
- ipc->getCallingUid());
+ VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", AIBinder_getCallingPid(),
+ AIBinder_getCallingUid());
if (checkPermission(kPermissionDump)) {
bool enabled = true;
if (args.size() >= 2) {
@@ -950,24 +873,24 @@
}
uid = goodUid;
- int32_t callingUid = IPCThreadState::self()->getCallingUid();
+ int32_t callingUid = AIBinder_getCallingUid();
return mEngBuild // UserDebug/EngBuild are allowed to impersonate uids.
|| (callingUid == goodUid) // Anyone can 'impersonate' themselves.
|| (callingUid == AID_ROOT && goodUid == AID_SHELL); // ROOT can impersonate SHELL.
}
-Status StatsService::informAllUidData(const ParcelFileDescriptor& fd) {
+Status StatsService::informAllUidData(const ScopedFileDescriptor& fd) {
ENFORCE_UID(AID_SYSTEM);
// Read stream into buffer.
string buffer;
if (!android::base::ReadFdToString(fd.get(), &buffer)) {
- return exception(Status::EX_ILLEGAL_ARGUMENT, "Failed to read all data from the pipe.");
+ return exception(EX_ILLEGAL_ARGUMENT, "Failed to read all data from the pipe.");
}
// Parse buffer.
UidData uidData;
if (!uidData.ParseFromString(buffer)) {
- return exception(Status::EX_ILLEGAL_ARGUMENT, "Error parsing proto stream for UidData.");
+ return exception(EX_ILLEGAL_ARGUMENT, "Error parsing proto stream for UidData.");
}
vector<String16> versionStrings;
@@ -1002,20 +925,28 @@
return Status::ok();
}
-Status StatsService::informOnePackage(const String16& app, int32_t uid, int64_t version,
- const String16& version_string, const String16& installer) {
+Status StatsService::informOnePackage(const string& app, int32_t uid, int64_t version,
+ const string& versionString, const string& installer) {
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::informOnePackage was called");
- mUidMap->updateApp(getElapsedRealtimeNs(), app, uid, version, version_string, installer);
+ // TODO(b/149254662): This is gross. We should consider changing statsd
+ // internals to use std::string.
+ String16 utf16App = String16(app.c_str());
+ String16 utf16VersionString = String16(versionString.c_str());
+ String16 utf16Installer = String16(installer.c_str());
+
+ mUidMap->updateApp(getElapsedRealtimeNs(), utf16App, uid, version, utf16VersionString,
+ utf16Installer);
return Status::ok();
}
-Status StatsService::informOnePackageRemoved(const String16& app, int32_t uid) {
+Status StatsService::informOnePackageRemoved(const string& app, int32_t uid) {
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::informOnePackageRemoved was called");
- mUidMap->removeApp(getElapsedRealtimeNs(), app, uid);
+ String16 utf16App = String16(app.c_str());
+ mUidMap->removeApp(getElapsedRealtimeNs(), utf16App, uid);
mConfigManager->RemoveConfigs(uid);
return Status::ok();
}
@@ -1079,7 +1010,7 @@
}
void StatsService::sayHiToStatsCompanion() {
- sp<IStatsCompanionService> statsCompanion = getStatsCompanionService();
+ shared_ptr<IStatsCompanionService> statsCompanion = getStatsCompanionService();
if (statsCompanion != nullptr) {
VLOG("Telling statsCompanion that statsd is ready");
statsCompanion->statsdReady();
@@ -1092,14 +1023,14 @@
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::statsCompanionReady was called");
- sp<IStatsCompanionService> statsCompanion = getStatsCompanionService();
+ shared_ptr<IStatsCompanionService> statsCompanion = getStatsCompanionService();
if (statsCompanion == nullptr) {
- return Status::fromExceptionCode(
- Status::EX_NULL_POINTER,
- "statscompanion unavailable despite it contacting statsd!");
+ return exception(EX_NULL_POINTER,
+ "StatsCompanion unavailable despite it contacting statsd.");
}
VLOG("StatsService::statsCompanionReady linking to statsCompanion.");
- IInterface::asBinder(statsCompanion)->linkToDeath(this);
+ AIBinder_linkToDeath(statsCompanion->asBinder().get(),
+ mStatsCompanionServiceDeathRecipient.get(), this);
mPullerManager->SetStatsCompanionService(statsCompanion);
mAnomalyAlarmMonitor->setStatsCompanionService(statsCompanion);
mPeriodicAlarmMonitor->setStatsCompanionService(statsCompanion);
@@ -1127,39 +1058,47 @@
}
}
-Status StatsService::getData(int64_t key, const int32_t callingUid, vector<uint8_t>* output) {
+Status StatsService::getData(int64_t key, const int32_t callingUid, vector<int8_t>* output) {
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::getData with Uid %i", callingUid);
ConfigKey configKey(callingUid, key);
+ // TODO(b/149254662): Since libbinder_ndk uses int8_t instead of uint8_t,
+ // there are inconsistencies with internal statsd logic. Instead of
+ // modifying lots of files, we create a temporary output array of int8_t and
+ // copy its data into output. This is a bad hack, but hopefully
+ // libbinder_ndk will transition to using uint8_t soon: progress is tracked
+ // in b/144957764. Same applies to StatsService::getMetadata.
+ vector<uint8_t> unsignedOutput;
// The dump latency does not matter here since we do not include the current bucket, we do not
// need to pull any new data anyhow.
mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), false /* include_current_bucket*/,
- true /* erase_data */, GET_DATA_CALLED, FAST, output);
+ true /* erase_data */, GET_DATA_CALLED, FAST, &unsignedOutput);
+ *output = vector<int8_t>(unsignedOutput.begin(), unsignedOutput.end());
return Status::ok();
}
-Status StatsService::getMetadata(vector<uint8_t>* output) {
+Status StatsService::getMetadata(vector<int8_t>* output) {
ENFORCE_UID(AID_SYSTEM);
- StatsdStats::getInstance().dumpStats(output, false); // Don't reset the counters.
+ vector<uint8_t> unsignedOutput;
+ StatsdStats::getInstance().dumpStats(&unsignedOutput, false); // Don't reset the counters.
+ *output = vector<int8_t>(unsignedOutput.begin(), unsignedOutput.end());
return Status::ok();
}
-Status StatsService::addConfiguration(int64_t key, const vector <uint8_t>& config,
+Status StatsService::addConfiguration(int64_t key, const vector <int8_t>& config,
const int32_t callingUid) {
ENFORCE_UID(AID_SYSTEM);
if (addConfigurationChecked(callingUid, key, config)) {
return Status::ok();
} else {
- ALOGE("Could not parse malformatted StatsdConfig");
- return Status::fromExceptionCode(binder::Status::EX_ILLEGAL_ARGUMENT,
- "config does not correspond to a StatsdConfig proto");
+ return exception(EX_ILLEGAL_ARGUMENT, "Could not parse malformatted StatsdConfig.");
}
}
-bool StatsService::addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config) {
+bool StatsService::addConfigurationChecked(int uid, int64_t key, const vector<int8_t>& config) {
ConfigKey configKey(uid, key);
StatsdConfig cfg;
if (config.size() > 0) { // If the config is empty, skip parsing.
@@ -1180,7 +1119,7 @@
}
Status StatsService::setDataFetchOperation(int64_t key,
- const sp<IPendingIntentRef>& pir,
+ const shared_ptr<IPendingIntentRef>& pir,
const int32_t callingUid) {
ENFORCE_UID(AID_SYSTEM);
@@ -1194,7 +1133,7 @@
return Status::ok();
}
-Status StatsService::setActiveConfigsChangedOperation(const sp<IPendingIntentRef>& pir,
+Status StatsService::setActiveConfigsChangedOperation(const shared_ptr<IPendingIntentRef>& pir,
const int32_t callingUid,
vector<int64_t>* output) {
ENFORCE_UID(AID_SYSTEM);
@@ -1225,7 +1164,7 @@
Status StatsService::setBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
- const sp<IPendingIntentRef>& pir,
+ const shared_ptr<IPendingIntentRef>& pir,
const int32_t callingUid) {
ENFORCE_UID(AID_SYSTEM);
@@ -1252,14 +1191,14 @@
// Permission check not necessary as it's meant for applications to write to
// statsd.
android::util::stats_write(util::APP_BREADCRUMB_REPORTED,
- (int32_t) IPCThreadState::self()->getCallingUid(), label,
+ (int32_t) AIBinder_getCallingUid(), label,
state);
return Status::ok();
}
Status StatsService::registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownNs,
int64_t timeoutNs, const std::vector<int32_t>& additiveFields,
- const sp<android::os::IPullAtomCallback>& pullerCallback) {
+ const shared_ptr<IPullAtomCallback>& pullerCallback) {
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::registerPullAtomCallback called.");
@@ -1270,10 +1209,10 @@
Status StatsService::registerNativePullAtomCallback(int32_t atomTag, int64_t coolDownNs,
int64_t timeoutNs, const std::vector<int32_t>& additiveFields,
- const sp<android::os::IPullAtomCallback>& pullerCallback) {
+ const shared_ptr<IPullAtomCallback>& pullerCallback) {
VLOG("StatsService::registerNativePullAtomCallback called.");
- int32_t uid = IPCThreadState::self()->getCallingUid();
+ int32_t uid = AIBinder_getCallingUid();
mPullerManager->RegisterPullAtomCallback(uid, atomTag, coolDownNs, timeoutNs, additiveFields,
pullerCallback);
return Status::ok();
@@ -1288,7 +1227,7 @@
Status StatsService::unregisterNativePullAtomCallback(int32_t atomTag) {
VLOG("StatsService::unregisterNativePullAtomCallback called.");
- int32_t uid = IPCThreadState::self()->getCallingUid();
+ int32_t uid = AIBinder_getCallingUid();
mPullerManager->UnregisterPullAtomCallback(uid, atomTag);
return Status::ok();
}
@@ -1314,7 +1253,13 @@
return Status::ok();
}
-void StatsService::binderDied(const wp <IBinder>& who) {
+
+void StatsService::statsCompanionServiceDied(void* cookie) {
+ auto thiz = static_cast<StatsService*>(cookie);
+ thiz->statsCompanionServiceDiedImpl();
+}
+
+void StatsService::statsCompanionServiceDiedImpl() {
ALOGW("statscompanion service died");
StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec());
if (mProcessor != nullptr) {
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 0527d43..e6723c8 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -27,27 +27,30 @@
#include "shell/ShellSubscriber.h"
#include "statscompanion_util.h"
-#include <android/os/BnStatsd.h>
-#include <android/os/IPendingIntentRef.h>
-#include <android/os/IStatsCompanionService.h>
-#include <android/os/IStatsd.h>
-#include <binder/IResultReceiver.h>
-#include <binder/ParcelFileDescriptor.h>
+#include <aidl/android/os/BnStatsd.h>
+#include <aidl/android/os/IPendingIntentRef.h>
+#include <aidl/android/os/IPullAtomCallback.h>
#include <utils/Looper.h>
#include <mutex>
using namespace android;
-using namespace android::binder;
using namespace android::os;
using namespace std;
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::os::BnStatsd;
+using aidl::android::os::IPendingIntentRef;
+using aidl::android::os::IPullAtomCallback;
+using ::ndk::ScopedAIBinder_DeathRecipient;
+using ::ndk::ScopedFileDescriptor;
+using std::shared_ptr;
+
namespace android {
namespace os {
namespace statsd {
-class StatsService : public BnStatsd,
- public IBinder::DeathRecipient {
+class StatsService : public BnStatsd {
public:
StatsService(const sp<Looper>& handlerLooper, std::shared_ptr<LogEventQueue> queue);
virtual ~StatsService();
@@ -55,10 +58,9 @@
/** The anomaly alarm registered with AlarmManager won't be updated by less than this. */
const uint32_t MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS = 5;
- virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
- virtual status_t dump(int fd, const Vector<String16>& args);
- virtual status_t command(int inFd, int outFd, int err, Vector<String8>& args,
- sp<IResultReceiver> resultReceiver);
+ virtual status_t dump(int fd, const char** args, uint32_t numArgs) override;
+ virtual status_t handleShellCommand(int in, int out, int err, const char** argv,
+ uint32_t argc) override;
virtual Status systemRunning();
virtual Status statsCompanionReady();
@@ -66,10 +68,10 @@
virtual Status informPollAlarmFired();
virtual Status informAlarmForSubscriberTriggeringFired();
- virtual Status informAllUidData(const ParcelFileDescriptor& fd);
- virtual Status informOnePackage(const String16& app, int32_t uid, int64_t version,
- const String16& version_string, const String16& installer);
- virtual Status informOnePackageRemoved(const String16& app, int32_t uid);
+ virtual Status informAllUidData(const ScopedFileDescriptor& fd);
+ virtual Status informOnePackage(const string& app, int32_t uid, int64_t version,
+ const string& versionString, const string& installer);
+ virtual Status informOnePackageRemoved(const string& app, int32_t uid);
virtual Status informDeviceShutdown();
/**
@@ -92,13 +94,13 @@
*/
virtual Status getData(int64_t key,
const int32_t callingUid,
- vector<uint8_t>* output) override;
+ vector<int8_t>* output) override;
/**
* Binder call for clients to get metadata across all configs in statsd.
*/
- virtual Status getMetadata(vector<uint8_t>* output) override;
+ virtual Status getMetadata(vector<int8_t>* output) override;
/**
@@ -106,14 +108,14 @@
* should requestData for this configuration.
*/
virtual Status addConfiguration(int64_t key,
- const vector<uint8_t>& config,
+ const vector<int8_t>& config,
const int32_t callingUid) override;
/**
* Binder call to let clients register the data fetch operation for a configuration.
*/
virtual Status setDataFetchOperation(int64_t key,
- const sp<IPendingIntentRef>& pir,
+ const shared_ptr<IPendingIntentRef>& pir,
const int32_t callingUid) override;
/**
@@ -125,7 +127,7 @@
/**
* Binder call to let clients register the active configs changed operation.
*/
- virtual Status setActiveConfigsChangedOperation(const sp<IPendingIntentRef>& pir,
+ virtual Status setActiveConfigsChangedOperation(const shared_ptr<IPendingIntentRef>& pir,
const int32_t callingUid,
vector<int64_t>* output) override;
@@ -144,7 +146,7 @@
*/
virtual Status setBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
- const sp<IPendingIntentRef>& pir,
+ const shared_ptr<IPendingIntentRef>& pir,
const int32_t callingUid) override;
/**
@@ -167,14 +169,14 @@
*/
virtual Status registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownNs,
int64_t timeoutNs, const std::vector<int32_t>& additiveFields,
- const sp<android::os::IPullAtomCallback>& pullerCallback) override;
+ const shared_ptr<IPullAtomCallback>& pullerCallback) override;
/**
* Binder call to register a callback function for a pulled atom.
*/
virtual Status registerNativePullAtomCallback(int32_t atomTag, int64_t coolDownNs,
int64_t timeoutNs, const std::vector<int32_t>& additiveFields,
- const sp<android::os::IPullAtomCallback>& pullerCallback) override;
+ const shared_ptr<IPullAtomCallback>& pullerCallback) override;
/**
* Binder call to unregister any existing callback for the given uid and atom.
@@ -191,9 +193,6 @@
*/
virtual Status getRegisteredExperimentIds(std::vector<int64_t>* expIdsOut);
- /** IBinder::DeathRecipient */
- virtual void binderDied(const wp<IBinder>& who) override;
-
private:
/**
* Load system properties at init.
@@ -317,7 +316,7 @@
/**
* Adds a configuration after checking permissions and obtaining UID from binder call.
*/
- bool addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config);
+ bool addConfigurationChecked(int uid, int64_t key, const vector<int8_t>& config);
/**
* Update a configuration.
@@ -325,6 +324,17 @@
void set_config(int uid, const string& name, const StatsdConfig& config);
/**
+ * Death recipient callback that is called when StatsCompanionService dies.
+ * The cookie is a pointer to a StatsService object.
+ */
+ static void statsCompanionServiceDied(void* cookie);
+
+ /**
+ * Implementation of statsCompanionServiceDied.
+ */
+ void statsCompanionServiceDiedImpl();
+
+ /**
* Tracks the uid <--> package name mapping.
*/
sp<UidMap> mUidMap;
@@ -367,6 +377,8 @@
mutable mutex mShellSubscriberMutex;
std::shared_ptr<LogEventQueue> mEventQueue;
+ ScopedAIBinder_DeathRecipient mStatsCompanionServiceDeathRecipient;
+
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
FRIEND_TEST(StatsServiceTest, TestAddConfig_simple);
FRIEND_TEST(StatsServiceTest, TestAddConfig_empty);
diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.cpp b/cmds/statsd/src/anomaly/AlarmMonitor.cpp
index bc36dad..02291181 100644
--- a/cmds/statsd/src/anomaly/AlarmMonitor.cpp
+++ b/cmds/statsd/src/anomaly/AlarmMonitor.cpp
@@ -26,17 +26,21 @@
AlarmMonitor::AlarmMonitor(
uint32_t minDiffToUpdateRegisteredAlarmTimeSec,
- const std::function<void(const sp<IStatsCompanionService>&, int64_t)>& updateAlarm,
- const std::function<void(const sp<IStatsCompanionService>&)>& cancelAlarm)
- : mRegisteredAlarmTimeSec(0), mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec),
+ const std::function<void(const shared_ptr<IStatsCompanionService>&, int64_t)>& updateAlarm,
+ const std::function<void(const shared_ptr<IStatsCompanionService>&)>& cancelAlarm)
+ : mRegisteredAlarmTimeSec(0),
+ mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec),
mUpdateAlarm(updateAlarm),
mCancelAlarm(cancelAlarm) {}
AlarmMonitor::~AlarmMonitor() {}
-void AlarmMonitor::setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) {
+void AlarmMonitor::setStatsCompanionService(
+ shared_ptr<IStatsCompanionService> statsCompanionService) {
std::lock_guard<std::mutex> lock(mLock);
- sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
+ // TODO(b/149254662): determine if tmpForLock is needed now that we have moved
+ // from sp to shared_ptr
+ shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService;
mStatsCompanionService = statsCompanionService;
if (statsCompanionService == nullptr) {
VLOG("Erasing link to statsCompanionService");
diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.h b/cmds/statsd/src/anomaly/AlarmMonitor.h
index 219695e..5c34e38 100644
--- a/cmds/statsd/src/anomaly/AlarmMonitor.h
+++ b/cmds/statsd/src/anomaly/AlarmMonitor.h
@@ -18,7 +18,7 @@
#include "anomaly/indexed_priority_queue.h"
-#include <android/os/IStatsCompanionService.h>
+#include <aidl/android/os/IStatsCompanionService.h>
#include <utils/RefBase.h>
#include <unordered_set>
@@ -26,7 +26,9 @@
using namespace android;
-using android::os::IStatsCompanionService;
+using aidl::android::os::IStatsCompanionService;
+using std::function;
+using std::shared_ptr;
using std::unordered_set;
namespace android {
@@ -64,8 +66,9 @@
* alarm.
*/
AlarmMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec,
- const std::function<void(const sp<IStatsCompanionService>&, int64_t)>& updateAlarm,
- const std::function<void(const sp<IStatsCompanionService>&)>& cancelAlarm);
+ const function<void(const shared_ptr<IStatsCompanionService>&, int64_t)>&
+ updateAlarm,
+ const function<void(const shared_ptr<IStatsCompanionService>&)>& cancelAlarm);
~AlarmMonitor();
/**
@@ -74,7 +77,7 @@
* If nullptr, AnomalyMonitor will continue to add/remove alarms, but won't
* update IStatsCompanionService (until such time as it is set non-null).
*/
- void setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService);
+ void setStatsCompanionService(shared_ptr<IStatsCompanionService> statsCompanionService);
/**
* Adds the given alarm (reference) to the queue.
@@ -123,7 +126,7 @@
/**
* Binder interface for communicating with StatsCompanionService.
*/
- sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+ shared_ptr<IStatsCompanionService> mStatsCompanionService = nullptr;
/**
* Amount by which the soonest projected alarm must differ from
@@ -147,10 +150,10 @@
int64_t secToMs(uint32_t timeSec);
// Callback function to update the alarm via StatsCompanionService.
- std::function<void(const sp<IStatsCompanionService>, int64_t)> mUpdateAlarm;
+ std::function<void(const shared_ptr<IStatsCompanionService>, int64_t)> mUpdateAlarm;
// Callback function to cancel the alarm via StatsCompanionService.
- std::function<void(const sp<IStatsCompanionService>)> mCancelAlarm;
+ std::function<void(const shared_ptr<IStatsCompanionService>)> mCancelAlarm;
};
diff --git a/cmds/statsd/src/anomaly/AlarmTracker.h b/cmds/statsd/src/anomaly/AlarmTracker.h
index 2fb3e3b..2da4a18 100644
--- a/cmds/statsd/src/anomaly/AlarmTracker.h
+++ b/cmds/statsd/src/anomaly/AlarmTracker.h
@@ -22,12 +22,9 @@
#include "config/ConfigKey.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alarm
-#include <android/os/IStatsCompanionService.h>
#include <stdlib.h>
#include <utils/RefBase.h>
-using android::os::IStatsCompanionService;
-
namespace android {
namespace os {
namespace statsd {
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 986955b..9bdb588 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -42,42 +42,67 @@
using android::base::StringPrintf;
using std::unique_ptr;
-class ConfigReceiverDeathRecipient : public android::IBinder::DeathRecipient {
- public:
- ConfigReceiverDeathRecipient(sp<ConfigManager> configManager, const ConfigKey& configKey):
- mConfigManager(configManager),
- mConfigKey(configKey) {}
- ~ConfigReceiverDeathRecipient() override = default;
- private:
- sp<ConfigManager> mConfigManager;
- ConfigKey mConfigKey;
+struct ConfigReceiverDeathCookie {
+ ConfigReceiverDeathCookie(sp<ConfigManager> configManager, const ConfigKey& configKey,
+ const shared_ptr<IPendingIntentRef>& pir):
+ mConfigManager(configManager),
+ mConfigKey(configKey),
+ mPir(pir) {}
- void binderDied(const android::wp<android::IBinder>& who) override {
- if (IInterface::asBinder(mConfigManager->GetConfigReceiver(mConfigKey)) == who.promote()) {
- mConfigManager->RemoveConfigReceiver(mConfigKey);
- }
- }
+ sp<ConfigManager> mConfigManager;
+ ConfigKey mConfigKey;
+ shared_ptr<IPendingIntentRef> mPir;
};
-class ActiveConfigChangedReceiverDeathRecipient : public android::IBinder::DeathRecipient {
- public:
- ActiveConfigChangedReceiverDeathRecipient(sp<ConfigManager> configManager, const int uid):
- mConfigManager(configManager),
- mUid(uid) {}
- ~ActiveConfigChangedReceiverDeathRecipient() override = default;
- private:
- sp<ConfigManager> mConfigManager;
- int mUid;
+static void configReceiverDied(void* cookie) {
+ auto cookie_ = static_cast<ConfigReceiverDeathCookie*>(cookie);
+ sp<ConfigManager> configManager = cookie_->mConfigManager;
+ ConfigKey configKey = cookie_->mConfigKey;
+ shared_ptr<IPendingIntentRef> pir = cookie_->mPir;
- void binderDied(const android::wp<android::IBinder>& who) override {
- if (IInterface::asBinder(mConfigManager->GetActiveConfigsChangedReceiver(mUid))
- == who.promote()) {
- mConfigManager->RemoveActiveConfigsChangedReceiver(mUid);
- }
+ // TODO(b/149254662): Fix threading. This currently fails if a new
+ // pir gets set between the get and the remove.
+ if (configManager->GetConfigReceiver(configKey) == pir) {
+ configManager->RemoveConfigReceiver(configKey);
}
+ // The death recipient corresponding to this specific pir can never
+ // be triggered again, so free up resources.
+ // TODO(b/149254662): Investigate other options to manage the memory.
+ delete cookie_;
+}
+
+struct ActiveConfigChangedReceiverDeathCookie {
+ ActiveConfigChangedReceiverDeathCookie(sp<ConfigManager> configManager, const int uid,
+ const shared_ptr<IPendingIntentRef>& pir):
+ mConfigManager(configManager),
+ mUid(uid),
+ mPir(pir) {}
+
+ sp<ConfigManager> mConfigManager;
+ int mUid;
+ shared_ptr<IPendingIntentRef> mPir;
};
-ConfigManager::ConfigManager() {
+static void activeConfigChangedReceiverDied(void* cookie) {
+ auto cookie_ = static_cast<ActiveConfigChangedReceiverDeathCookie*>(cookie);
+ sp<ConfigManager> configManager = cookie_->mConfigManager;
+ int uid = cookie_->mUid;
+ shared_ptr<IPendingIntentRef> pir = cookie_->mPir;
+
+ // TODO(b/149254662): Fix threading. This currently fails if a new
+ // pir gets set between the get and the remove.
+ if (configManager->GetActiveConfigsChangedReceiver(uid) == pir) {
+ configManager->RemoveActiveConfigsChangedReceiver(uid);
+ }
+ // The death recipient corresponding to this specific pir can never
+ // be triggered again, so free up resources.
+ delete cookie_;
+}
+
+ConfigManager::ConfigManager():
+ mConfigReceiverDeathRecipient(AIBinder_DeathRecipient_new(configReceiverDied)),
+ mActiveConfigChangedReceiverDeathRecipient(
+ AIBinder_DeathRecipient_new(activeConfigChangedReceiverDied)) {
}
ConfigManager::~ConfigManager() {
@@ -150,10 +175,11 @@
}
void ConfigManager::SetConfigReceiver(const ConfigKey& key,
- const sp<IPendingIntentRef>& pir) {
+ const shared_ptr<IPendingIntentRef>& pir) {
lock_guard<mutex> lock(mMutex);
mConfigReceivers[key] = pir;
- IInterface::asBinder(pir)->linkToDeath(new ConfigReceiverDeathRecipient(this, key));
+ AIBinder_linkToDeath(pir->asBinder().get(), mConfigReceiverDeathRecipient.get(),
+ new ConfigReceiverDeathCookie(this, key, pir));
}
void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) {
@@ -162,11 +188,11 @@
}
void ConfigManager::SetActiveConfigsChangedReceiver(const int uid,
- const sp<IPendingIntentRef>& pir) {
+ const shared_ptr<IPendingIntentRef>& pir) {
lock_guard<mutex> lock(mMutex);
mActiveConfigsChangedReceivers[uid] = pir;
- IInterface::asBinder(pir)->linkToDeath(
- new ActiveConfigChangedReceiverDeathRecipient(this, uid));
+ AIBinder_linkToDeath(pir->asBinder().get(), mActiveConfigChangedReceiverDeathRecipient.get(),
+ new ActiveConfigChangedReceiverDeathCookie(this, uid, pir));
}
void ConfigManager::RemoveActiveConfigsChangedReceiver(const int uid) {
@@ -279,7 +305,7 @@
return ret;
}
-const sp<IPendingIntentRef> ConfigManager::GetConfigReceiver(const ConfigKey& key) const {
+const shared_ptr<IPendingIntentRef> ConfigManager::GetConfigReceiver(const ConfigKey& key) const {
lock_guard<mutex> lock(mMutex);
auto it = mConfigReceivers.find(key);
@@ -290,7 +316,8 @@
}
}
-const sp<IPendingIntentRef> ConfigManager::GetActiveConfigsChangedReceiver(const int uid) const {
+const shared_ptr<IPendingIntentRef> ConfigManager::GetActiveConfigsChangedReceiver(const int uid)
+ const {
lock_guard<mutex> lock(mMutex);
auto it = mActiveConfigsChangedReceivers.find(uid);
diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h
index 2095173..824e588 100644
--- a/cmds/statsd/src/config/ConfigManager.h
+++ b/cmds/statsd/src/config/ConfigManager.h
@@ -19,12 +19,15 @@
#include "config/ConfigKey.h"
#include "config/ConfigListener.h"
-#include <android/os/IPendingIntentRef.h>
+#include <aidl/android/os/IPendingIntentRef.h>
#include <mutex>
#include <string>
#include <stdio.h>
+using aidl::android::os::IPendingIntentRef;
+using std::shared_ptr;
+
namespace android {
namespace os {
namespace statsd {
@@ -62,12 +65,12 @@
/**
* Sets the broadcast receiver for a configuration key.
*/
- void SetConfigReceiver(const ConfigKey& key, const sp<IPendingIntentRef>& pir);
+ void SetConfigReceiver(const ConfigKey& key, const shared_ptr<IPendingIntentRef>& pir);
/**
* Returns the package name and class name representing the broadcast receiver for this config.
*/
- const sp<IPendingIntentRef> GetConfigReceiver(const ConfigKey& key) const;
+ const shared_ptr<IPendingIntentRef> GetConfigReceiver(const ConfigKey& key) const;
/**
* Returns all config keys registered.
@@ -83,13 +86,13 @@
* Sets the broadcast receiver that is notified whenever the list of active configs
* changes for this uid.
*/
- void SetActiveConfigsChangedReceiver(const int uid, const sp<IPendingIntentRef>& pir);
+ void SetActiveConfigsChangedReceiver(const int uid, const shared_ptr<IPendingIntentRef>& pir);
/**
* Returns the broadcast receiver for active configs changed for this uid.
*/
- const sp<IPendingIntentRef> GetActiveConfigsChangedReceiver(const int uid) const;
+ const shared_ptr<IPendingIntentRef> GetActiveConfigsChangedReceiver(const int uid) const;
/**
* Erase any active configs changed broadcast receiver associated with this uid.
@@ -141,18 +144,23 @@
/**
* Each config key can be subscribed by up to one receiver, specified as IPendingIntentRef.
*/
- std::map<ConfigKey, sp<IPendingIntentRef>> mConfigReceivers;
+ std::map<ConfigKey, shared_ptr<IPendingIntentRef>> mConfigReceivers;
/**
* Each uid can be subscribed by up to one receiver to notify that the list of active configs
* for this uid has changed. The receiver is specified as IPendingIntentRef.
*/
- std::map<int, sp<IPendingIntentRef>> mActiveConfigsChangedReceivers;
+ std::map<int, shared_ptr<IPendingIntentRef>> mActiveConfigsChangedReceivers;
/**
* The ConfigListeners that will be told about changes.
*/
std::vector<sp<ConfigListener>> mListeners;
+
+ // Death recipients that are triggered when the host process holding an
+ // IPendingIntentRef dies.
+ ::ndk::ScopedAIBinder_DeathRecipient mConfigReceiverDeathRecipient;
+ ::ndk::ScopedAIBinder_DeathRecipient mActiveConfigChangedReceiverDeathRecipient;
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/PullResultReceiver.cpp b/cmds/statsd/src/external/PullResultReceiver.cpp
index 6b6fe7d..8aa4792 100644
--- a/cmds/statsd/src/external/PullResultReceiver.cpp
+++ b/cmds/statsd/src/external/PullResultReceiver.cpp
@@ -16,17 +16,12 @@
#include "PullResultReceiver.h"
-using namespace android::binder;
-using namespace android::util;
-using namespace std;
-
namespace android {
namespace os {
namespace statsd {
PullResultReceiver::PullResultReceiver(
- std::function<void(int32_t, bool, const vector<android::util::StatsEventParcel>&)>
- pullFinishCb)
+ std::function<void(int32_t, bool, const vector<StatsEventParcel>&)> pullFinishCb)
: pullFinishCallback(std::move(pullFinishCb)) {
}
diff --git a/cmds/statsd/src/external/PullResultReceiver.h b/cmds/statsd/src/external/PullResultReceiver.h
index 17d06e4..ceaae80 100644
--- a/cmds/statsd/src/external/PullResultReceiver.h
+++ b/cmds/statsd/src/external/PullResultReceiver.h
@@ -14,29 +14,33 @@
* limitations under the License.
*/
-#include <android/os/BnPullAtomResultReceiver.h>
+#include <aidl/android/os/BnPullAtomResultReceiver.h>
+#include <aidl/android/util/StatsEventParcel.h>
using namespace std;
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::os::BnPullAtomResultReceiver;
+using aidl::android::util::StatsEventParcel;
+
namespace android {
namespace os {
namespace statsd {
class PullResultReceiver : public BnPullAtomResultReceiver {
public:
- PullResultReceiver(function<void(int32_t, bool, const vector<android::util::StatsEventParcel>&)>
+ PullResultReceiver(function<void(int32_t, bool, const vector<StatsEventParcel>&)>
pullFinishCallback);
~PullResultReceiver();
/**
* Binder call for finishing a pull.
*/
- binder::Status pullFinished(int32_t atomTag, bool success,
- const vector<android::util::StatsEventParcel>& output) override;
+ Status pullFinished(int32_t atomTag, bool success,
+ const vector<StatsEventParcel>& output) override;
private:
- function<void(int32_t, bool, const vector<android::util::StatsEventParcel>&)>
- pullFinishCallback;
+ function<void(int32_t, bool, const vector<StatsEventParcel>&)> pullFinishCallback;
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp
index 1a11f0e..b6c8e34 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.cpp
+++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp
@@ -18,24 +18,24 @@
#include "Log.h"
#include "StatsCallbackPuller.h"
-
-#include <android/os/IPullAtomCallback.h>
-#include <android/util/StatsEventParcel.h>
-
#include "PullResultReceiver.h"
#include "StatsPullerManager.h"
#include "logd/LogEvent.h"
#include "stats_log_util.h"
-using namespace android::binder;
-using namespace android::util;
+#include <aidl/android/util/StatsEventParcel.h>
+
using namespace std;
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::util::StatsEventParcel;
+using ::ndk::SharedRefBase;
+
namespace android {
namespace os {
namespace statsd {
-StatsCallbackPuller::StatsCallbackPuller(int tagId, const sp<IPullAtomCallback>& callback,
+StatsCallbackPuller::StatsCallbackPuller(int tagId, const shared_ptr<IPullAtomCallback>& callback,
const int64_t coolDownNs, int64_t timeoutNs,
const vector<int> additiveFields)
: StatsPuller(tagId, coolDownNs, timeoutNs, additiveFields), mCallback(callback) {
@@ -57,7 +57,7 @@
shared_ptr<vector<shared_ptr<LogEvent>>> sharedData =
make_shared<vector<shared_ptr<LogEvent>>>();
- sp<PullResultReceiver> resultReceiver = new PullResultReceiver(
+ shared_ptr<PullResultReceiver> resultReceiver = SharedRefBase::make<PullResultReceiver>(
[cv_mutex, cv, pullFinish, pullSuccess, sharedData](
int32_t atomTag, bool success, const vector<StatsEventParcel>& output) {
// This is the result of the pull, executing in a statsd binder thread.
@@ -66,9 +66,11 @@
{
lock_guard<mutex> lk(*cv_mutex);
for (const StatsEventParcel& parcel: output) {
+ uint8_t* buf = reinterpret_cast<uint8_t*>(
+ const_cast<int8_t*>(parcel.buffer.data()));
shared_ptr<LogEvent> event = make_shared<LogEvent>(
- const_cast<uint8_t*>(parcel.buffer.data()), parcel.buffer.size(),
- /*uid=*/-1, /*pid=*/-1, /*useNewSchema=*/true);
+ buf, parcel.buffer.size(), /*uid=*/-1, /*pid=*/-1,
+ /*useNewSchema=*/true);
sharedData->push_back(event);
}
*pullSuccess = success;
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.h b/cmds/statsd/src/external/StatsCallbackPuller.h
index fe6af19..e82e8bb 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.h
+++ b/cmds/statsd/src/external/StatsCallbackPuller.h
@@ -16,23 +16,25 @@
#pragma once
-#include <android/os/IPullAtomCallback.h>
-
+#include <aidl/android/os/IPullAtomCallback.h>
#include "StatsPuller.h"
+using aidl::android::os::IPullAtomCallback;
+using std::shared_ptr;
+
namespace android {
namespace os {
namespace statsd {
class StatsCallbackPuller : public StatsPuller {
public:
- explicit StatsCallbackPuller(int tagId, const sp<IPullAtomCallback>& callback,
+ explicit StatsCallbackPuller(int tagId, const shared_ptr<IPullAtomCallback>& callback,
const int64_t coolDownNs, const int64_t timeoutNs,
const std::vector<int> additiveFields);
private:
- bool PullInternal(vector<std::shared_ptr<LogEvent> >* data) override;
- const sp<IPullAtomCallback> mCallback;
+ bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+ const shared_ptr<IPullAtomCallback> mCallback;
FRIEND_TEST(StatsCallbackPullerTest, PullFail);
FRIEND_TEST(StatsCallbackPullerTest, PullSuccess);
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index 9c21695..fee571c 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -16,7 +16,7 @@
#pragma once
-#include <android/os/IStatsCompanionService.h>
+#include <aidl/android/os/IStatsCompanionService.h>
#include <utils/RefBase.h>
#include <mutex>
#include <vector>
@@ -26,6 +26,9 @@
#include "logd/LogEvent.h"
#include "puller_util.h"
+using aidl::android::os::IStatsCompanionService;
+using std::shared_ptr;
+
namespace android {
namespace os {
namespace statsd {
@@ -58,7 +61,8 @@
static void SetUidMap(const sp<UidMap>& uidMap);
- virtual void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService){};
+ virtual void SetStatsCompanionService(
+ shared_ptr<IStatsCompanionService> statsCompanionService) {};
protected:
const int mTagId;
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 3ceff75..1c38542 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -19,8 +19,6 @@
#include "StatsPullerManager.h"
-#include <android/os/IPullAtomCallback.h>
-#include <android/os/IStatsCompanionService.h>
#include <cutils/log.h>
#include <math.h>
#include <stdint.h>
@@ -87,7 +85,10 @@
return;
}
- sp<IStatsCompanionService> statsCompanionServiceCopy = mStatsCompanionService;
+ // TODO(b/149254662): Why are we creating a copy here? This is different
+ // from the other places where we create a copy because we don't reassign
+ // mStatsCompanionService so a destructor can't implicitly be called...
+ shared_ptr<IStatsCompanionService> statsCompanionServiceCopy = mStatsCompanionService;
if (statsCompanionServiceCopy != nullptr) {
statsCompanionServiceCopy->setPullingAlarm(mNextPullTimeNs / 1000000);
} else {
@@ -97,9 +98,11 @@
}
void StatsPullerManager::SetStatsCompanionService(
- sp<IStatsCompanionService> statsCompanionService) {
+ shared_ptr<IStatsCompanionService> statsCompanionService) {
+ // TODO(b/149254662): Why are we using AutoMutex instead of lock_guard?
+ // Additionally, do we need the temporary shared_ptr to prevent deadlocks?
AutoMutex _l(mLock);
- sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
+ shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService;
mStatsCompanionService = statsCompanionService;
for (const auto& pulledAtom : kAllPullAtomInfo) {
pulledAtom.second->SetStatsCompanionService(statsCompanionService);
@@ -250,10 +253,11 @@
void StatsPullerManager::RegisterPullAtomCallback(const int uid, const int32_t atomTag,
const int64_t coolDownNs, const int64_t timeoutNs,
const vector<int32_t>& additiveFields,
- const sp<IPullAtomCallback>& callback) {
+ const shared_ptr<IPullAtomCallback>& callback) {
AutoMutex _l(mLock);
VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag);
- // TODO: linkToDeath with the callback so that we can remove it and delete the puller.
+ // TODO(b/146439412): linkToDeath with the callback so that we can remove it
+ // and delete the puller.
StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/true);
kAllPullAtomInfo[{.atomTag = atomTag}] =
new StatsCallbackPuller(atomTag, callback, coolDownNs, timeoutNs, additiveFields);
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index aef16dc..e067766 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -16,9 +16,8 @@
#pragma once
-#include <android/os/IPullAtomCallback.h>
-#include <android/os/IStatsCompanionService.h>
-#include <binder/IServiceManager.h>
+#include <aidl/android/os/IPullAtomCallback.h>
+#include <aidl/android/os/IStatsCompanionService.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
@@ -30,11 +29,14 @@
#include "guardrail/StatsdStats.h"
#include "logd/LogEvent.h"
+using aidl::android::os::IPullAtomCallback;
+using aidl::android::os::IStatsCompanionService;
+using std::shared_ptr;
+
namespace android {
namespace os {
namespace statsd {
-
typedef struct PullerKey {
// The uid of the process that registers this puller.
const int uid = -1;
@@ -93,18 +95,18 @@
// Clear pull data cache if it is beyond respective cool down time.
int ClearPullerCacheIfNecessary(int64_t timestampNs);
- void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService);
+ void SetStatsCompanionService(shared_ptr<IStatsCompanionService> statsCompanionService);
void RegisterPullAtomCallback(const int uid, const int32_t atomTag, const int64_t coolDownNs,
const int64_t timeoutNs, const vector<int32_t>& additiveFields,
- const sp<IPullAtomCallback>& callback);
+ const shared_ptr<IPullAtomCallback>& callback);
void UnregisterPullAtomCallback(const int uid, const int32_t atomTag);
std::map<const PullerKey, sp<StatsPuller>> kAllPullAtomInfo;
private:
- sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+ shared_ptr<IStatsCompanionService> mStatsCompanionService = nullptr;
typedef struct {
int64_t nextPullTimeNs;
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 1ef1ab2..9c50846 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -20,7 +20,8 @@
#include "stats_log_util.h"
#include "statslog.h"
-#include <binder/IPCThreadState.h>
+#include <android/binder_ibinder.h>
+#include <android-base/stringprintf.h>
#include <private/android_filesystem_config.h>
namespace android {
@@ -31,6 +32,7 @@
const int FIELD_ID_EXPERIMENT_ID = 1;
using namespace android::util;
+using android::base::StringPrintf;
using android::util::ProtoOutputStream;
using std::string;
using std::vector;
@@ -180,8 +182,8 @@
mLogdTimestampNs = getWallClockNs();
mElapsedTimestampNs = getElapsedRealtimeNs();
mTagId = android::util::BINARY_PUSH_STATE_CHANGED;
- mLogUid = android::IPCThreadState::self()->getCallingUid();
- mLogPid = android::IPCThreadState::self()->getCallingPid();
+ mLogUid = AIBinder_getCallingUid();
+ mLogPid = AIBinder_getCallingPid();
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)), Value(trainName)));
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainVersionCode)));
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index 140ef4e..d79b6a2 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -20,9 +20,9 @@
#include "StatsService.h"
#include "socket/StatsSocketListener.h"
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_process.h>
+#include <android/binder_manager.h>
#include <utils/Looper.h>
#include <stdio.h>
@@ -32,16 +32,11 @@
using namespace android;
using namespace android::os::statsd;
+using ::ndk::SharedRefBase;
+using std::shared_ptr;
+using std::make_shared;
-/**
- * Thread function data.
- */
-struct log_reader_thread_data {
- sp<StatsService> service;
-};
-
-
-sp<StatsService> gStatsService = nullptr;
+shared_ptr<StatsService> gStatsService = nullptr;
void sigHandler(int sig) {
if (gStatsService != nullptr) {
@@ -68,20 +63,18 @@
sp<Looper> looper(Looper::prepare(0 /* opts */));
// Set up the binder
- sp<ProcessState> ps(ProcessState::self());
- ps->setThreadPoolMaxThreadCount(9);
- ps->startThreadPool();
- ps->giveThreadPoolName();
- IPCThreadState::self()->disableBackgroundScheduling(true);
+ ABinderProcess_setThreadPoolMaxThreadCount(9);
+ ABinderProcess_startThreadPool();
std::shared_ptr<LogEventQueue> eventQueue =
std::make_shared<LogEventQueue>(2000 /*buffer limit. Buffer is NOT pre-allocated*/);
// Create the service
- gStatsService = new StatsService(looper, eventQueue);
- if (defaultServiceManager()->addService(String16("stats"), gStatsService, false,
- IServiceManager::DUMP_FLAG_PRIORITY_NORMAL | IServiceManager::DUMP_FLAG_PROTO)
- != 0) {
+ gStatsService = SharedRefBase::make<StatsService>(looper, eventQueue);
+ // TODO(b/149582373): Set DUMP_FLAG_PROTO once libbinder_ndk supports
+ // setting dumpsys priorities.
+ binder_status_t status = AServiceManager_addService(gStatsService->asBinder().get(), "stats");
+ if (status != STATUS_OK) {
ALOGE("Failed to add service as AIDL service");
return -1;
}
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index bfac6e3..4e3c506 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -21,10 +21,11 @@
#include "packages/PackageInfoListener.h"
#include "stats_util.h"
-#include <binder/IShellCallback.h>
#include <gtest/gtest_prod.h>
#include <stdio.h>
#include <utils/RefBase.h>
+#include <utils/String16.h>
+
#include <list>
#include <mutex>
#include <set>
diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp
index d6a0433..a861a3b 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.cpp
+++ b/cmds/statsd/src/shell/ShellSubscriber.cpp
@@ -29,49 +29,88 @@
const static int FIELD_ID_ATOM = 1;
-void ShellSubscriber::startNewSubscription(int in, int out, sp<IResultReceiver> resultReceiver,
- int timeoutSec) {
+void ShellSubscriber::startNewSubscription(int in, int out, int timeoutSec) {
VLOG("start new shell subscription");
+ int64_t subscriberId = getElapsedRealtimeNs();
+
{
std::lock_guard<std::mutex> lock(mMutex);
- if (mResultReceiver != nullptr) {
+ if (mSubscriberId> 0) {
VLOG("Only one shell subscriber is allowed.");
return;
}
+ mSubscriberId = subscriberId;
mInput = in;
mOutput = out;
- mResultReceiver = resultReceiver;
- IInterface::asBinder(mResultReceiver)->linkToDeath(this);
}
- // Note that the following is blocking, and it's intended as we cannot return until the shell
- // cmd exits, otherwise all resources & FDs will be automatically closed.
+ bool success = readConfig();
+ if (!success) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ cleanUpLocked();
+ }
- // Read config forever until EOF is reached. Clients may send multiple configs -- each new
- // config replace the previous one.
- readConfig(in);
- VLOG("timeout : %d", timeoutSec);
-
- // Now we have read an EOF we now wait for the semaphore until the client exits.
- VLOG("Now wait for client to exit");
+ VLOG("Wait for client to exit or timeout (%d sec)", timeoutSec);
std::unique_lock<std::mutex> lk(mMutex);
+ // Note that the following is blocking, and it's intended as we cannot return until the shell
+ // cmd exits or we time out.
if (timeoutSec > 0) {
mShellDied.wait_for(lk, timeoutSec * 1s,
- [this, resultReceiver] { return mResultReceiver != resultReceiver; });
+ [this, subscriberId] { return mSubscriberId != subscriberId; });
} else {
- mShellDied.wait(lk, [this, resultReceiver] { return mResultReceiver != resultReceiver; });
+ mShellDied.wait(lk, [this, subscriberId] { return mSubscriberId != subscriberId; });
+ }
+}
+
+
+// Read configs until EOF is reached. There may be multiple configs in the input
+// -- each new config should replace the previous one.
+//
+// Returns a boolean indicating whether the input was read successfully.
+bool ShellSubscriber::readConfig() {
+ if (mInput < 0) {
+ return false;
+ }
+
+ while (true) {
+ // Read the size of the config.
+ size_t bufferSize = 0;
+ ssize_t bytesRead = read(mInput, &bufferSize, sizeof(bufferSize));
+ if (bytesRead == 0) {
+ VLOG("We have reached the end of the input.");
+ return true;
+ } else if (bytesRead < 0 || (size_t)bytesRead != sizeof(bufferSize)) {
+ ALOGE("Error reading config size");
+ return false;
+ }
+
+ // Read and parse the config.
+ vector<uint8_t> buffer(bufferSize);
+ bytesRead = read(mInput, buffer.data(), bufferSize);
+ if (bytesRead > 0 && (size_t)bytesRead == bufferSize) {
+ ShellSubscription config;
+ if (config.ParseFromArray(buffer.data(), bufferSize)) {
+ updateConfig(config);
+ } else {
+ ALOGE("Error parsing the config");
+ return false;
+ }
+ } else {
+ VLOG("Error reading the config, expected bytes: %zu, actual bytes: %zu", bufferSize,
+ bytesRead);
+ return false;
+ }
}
}
void ShellSubscriber::updateConfig(const ShellSubscription& config) {
- std::lock_guard<std::mutex> lock(mMutex);
mPushedMatchers.clear();
mPulledInfo.clear();
for (const auto& pushed : config.pushed()) {
mPushedMatchers.push_back(pushed);
- VLOG("adding matcher for atom %d", pushed.atom_id());
+ VLOG("adding matcher for pushed atom %d", pushed.atom_id());
}
int64_t token = getElapsedRealtimeNs();
@@ -89,46 +128,20 @@
}
if (mPulledInfo.size() > 0 && minInterval > 0) {
- // This thread is guaranteed to terminate after it detects the token is different or
- // cleaned up.
+ // This thread is guaranteed to terminate after it detects the token is
+ // different.
std::thread puller([token, minInterval, this] { startPull(token, minInterval); });
puller.detach();
}
}
-void ShellSubscriber::writeToOutputLocked(const vector<std::shared_ptr<LogEvent>>& data,
- const SimpleAtomMatcher& matcher) {
- if (mOutput == 0) return;
- int count = 0;
- mProto.clear();
- for (const auto& event : data) {
- VLOG("%s", event->ToString().c_str());
- if (matchesSimple(*mUidMap, matcher, *event)) {
- VLOG("matched");
- count++;
- uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
- util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
- event->ToProto(mProto);
- mProto.end(atomToken);
- }
- }
-
- if (count > 0) {
- // First write the payload size.
- size_t bufferSize = mProto.size();
- write(mOutput, &bufferSize, sizeof(bufferSize));
- VLOG("%d atoms, proto size: %zu", count, bufferSize);
- // Then write the payload.
- mProto.flush(mOutput);
- }
- mProto.clear();
-}
-
void ShellSubscriber::startPull(int64_t token, int64_t intervalMillis) {
- while (1) {
+ while (true) {
int64_t nowMillis = getElapsedRealtimeMillis();
{
std::lock_guard<std::mutex> lock(mMutex);
+ // If the token has changed, the config has changed, so this
+ // puller can now stop.
if (mPulledInfo.size() == 0 || mPullToken != token) {
VLOG("Pulling thread %lld done!", (long long)token);
return;
@@ -152,55 +165,47 @@
}
}
-void ShellSubscriber::readConfig(int in) {
- if (in <= 0) {
+// Must be called with the lock acquired, so that mProto isn't being written to
+// at the same time by multiple threads.
+void ShellSubscriber::writeToOutputLocked(const vector<std::shared_ptr<LogEvent>>& data,
+ const SimpleAtomMatcher& matcher) {
+ if (mOutput < 0) {
return;
}
-
- while (1) {
- size_t bufferSize = 0;
- int result = 0;
- if ((result = read(in, &bufferSize, sizeof(bufferSize))) == 0) {
- VLOG("Done reading");
- break;
- } else if (result < 0 || result != sizeof(bufferSize)) {
- ALOGE("Error reading config size");
- break;
- }
-
- vector<uint8_t> buffer(bufferSize);
- if ((result = read(in, buffer.data(), bufferSize)) > 0 && ((size_t)result) == bufferSize) {
- ShellSubscription config;
- if (config.ParseFromArray(buffer.data(), bufferSize)) {
- updateConfig(config);
- } else {
- ALOGE("error parsing the config");
- break;
- }
- } else {
- VLOG("Error reading the config, returned: %d, expecting %zu", result, bufferSize);
- break;
+ int count = 0;
+ mProto.clear();
+ for (const auto& event : data) {
+ VLOG("%s", event->ToString().c_str());
+ if (matchesSimple(*mUidMap, matcher, *event)) {
+ VLOG("matched");
+ count++;
+ uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
+ util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
+ event->ToProto(mProto);
+ mProto.end(atomToken);
}
}
-}
-void ShellSubscriber::cleanUpLocked() {
- // The file descriptors will be closed by binder.
- mInput = 0;
- mOutput = 0;
- mResultReceiver = nullptr;
- mPushedMatchers.clear();
- mPulledInfo.clear();
- mPullToken = 0;
- VLOG("done clean up");
+ if (count > 0) {
+ // First write the payload size.
+ size_t bufferSize = mProto.size();
+ write(mOutput, &bufferSize, sizeof(bufferSize));
+
+ VLOG("%d atoms, proto size: %zu", count, bufferSize);
+ // Then write the payload.
+ mProto.flush(mOutput);
+ }
}
void ShellSubscriber::onLogEvent(const LogEvent& event) {
+ // Acquire a lock to prevent corruption from multiple threads writing to
+ // mProto.
std::lock_guard<std::mutex> lock(mMutex);
-
- if (mOutput <= 0) {
+ if (mOutput < 0) {
return;
}
+
+ mProto.clear();
for (const auto& matcher : mPushedMatchers) {
if (matchesSimple(*mUidMap, matcher, event)) {
VLOG("%s", event.ToString().c_str());
@@ -208,25 +213,27 @@
util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
event.ToProto(mProto);
mProto.end(atomToken);
+
// First write the payload size.
size_t bufferSize = mProto.size();
write(mOutput, &bufferSize, sizeof(bufferSize));
// Then write the payload.
mProto.flush(mOutput);
- mProto.clear();
- break;
}
}
}
-void ShellSubscriber::binderDied(const wp<IBinder>& who) {
- {
- VLOG("Shell exits");
- std::lock_guard<std::mutex> lock(mMutex);
- cleanUpLocked();
- }
- mShellDied.notify_all();
+void ShellSubscriber::cleanUpLocked() {
+ // The file descriptors will be closed by binder.
+ mInput = -1;
+ mOutput = -1;
+ mSubscriberId = 0;
+ mPushedMatchers.clear();
+ mPulledInfo.clear();
+ // Setting mPullToken == 0 tells pull thread that its work is done.
+ mPullToken = 0;
+ VLOG("done clean up");
}
} // namespace statsd
diff --git a/cmds/statsd/src/shell/ShellSubscriber.h b/cmds/statsd/src/shell/ShellSubscriber.h
index 86d8590..eaf2ad1 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.h
+++ b/cmds/statsd/src/shell/ShellSubscriber.h
@@ -19,7 +19,6 @@
#include "logd/LogEvent.h"
#include <android/util/ProtoOutputStream.h>
-#include <binder/IResultReceiver.h>
#include <condition_variable>
#include <mutex>
#include <thread>
@@ -56,7 +55,7 @@
* Only one shell subscriber allowed at a time, because each shell subscriber blocks one thread
* until it exits.
*/
-class ShellSubscriber : public virtual IBinder::DeathRecipient {
+class ShellSubscriber : public virtual RefBase {
public:
ShellSubscriber(sp<UidMap> uidMap, sp<StatsPullerManager> pullerMgr)
: mUidMap(uidMap), mPullerMgr(pullerMgr){};
@@ -64,10 +63,7 @@
/**
* Start a new subscription.
*/
- void startNewSubscription(int inFd, int outFd, sp<IResultReceiver> resultReceiver,
- int timeoutSec);
-
- void binderDied(const wp<IBinder>& who);
+ void startNewSubscription(int inFd, int outFd, int timeoutSec);
void onLogEvent(const LogEvent& event);
@@ -80,7 +76,7 @@
int64_t mInterval;
int64_t mPrevPullElapsedRealtimeMs;
};
- void readConfig(int in);
+ bool readConfig();
void updateConfig(const ShellSubscription& config);
@@ -101,16 +97,16 @@
std::condition_variable mShellDied; // semaphore for waiting until shell exits.
- int mInput; // The input file descriptor
+ int mInput = -1; // The input file descriptor
- int mOutput; // The output file descriptor
-
- sp<IResultReceiver> mResultReceiver;
+ int mOutput = -1; // The output file descriptor
std::vector<SimpleAtomMatcher> mPushedMatchers;
std::vector<PullInfo> mPulledInfo;
+ int64_t mSubscriberId = 0; // A unique id to identify a subscriber.
+
int64_t mPullToken = 0; // A unique token to identify a puller thread.
};
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 73f640e..3d02ffb 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -17,6 +17,7 @@
#include "hash.h"
#include "stats_log_util.h"
+#include <aidl/android/os/IStatsCompanionService.h>
#include <private/android_filesystem_config.h>
#include <set>
#include <utils/SystemClock.h>
@@ -35,6 +36,10 @@
using android::util::FIELD_TYPE_UINT64;
using android::util::ProtoOutputStream;
+using aidl::android::os::IStatsCompanionService;
+using std::shared_ptr;
+using std::string;
+
namespace android {
namespace os {
namespace statsd {
@@ -587,13 +592,13 @@
}
bool checkPermissionForIds(const char* permission, pid_t pid, uid_t uid) {
- sp<IStatsCompanionService> scs = getStatsCompanionService();
+ shared_ptr<IStatsCompanionService> scs = getStatsCompanionService();
if (scs == nullptr) {
return false;
}
bool success;
- binder::Status status = scs->checkPermission(String16(permission), pid, uid, &success);
+ ::ndk::ScopedAStatus status = scs->checkPermission(string(permission), pid, uid, &success);
if (!status.isOk()) {
return false;
}
diff --git a/cmds/statsd/src/statscompanion_util.cpp b/cmds/statsd/src/statscompanion_util.cpp
index d338827..ce07ec0 100644
--- a/cmds/statsd/src/statscompanion_util.cpp
+++ b/cmds/statsd/src/statscompanion_util.cpp
@@ -18,26 +18,16 @@
#include "Log.h"
#include "statscompanion_util.h"
+#include <android/binder_auto_utils.h>
+#include <android/binder_manager.h>
namespace android {
namespace os {
namespace statsd {
-sp <IStatsCompanionService> getStatsCompanionService() {
- sp<IStatsCompanionService> statsCompanion = nullptr;
- // Get statscompanion service from service manager
- static const sp <IServiceManager> sm(defaultServiceManager());
- if (statsCompanion == nullptr) {
- if (sm != nullptr) {
- const String16 name("statscompanion");
- statsCompanion = interface_cast<IStatsCompanionService>(sm->checkService(name));
- if (statsCompanion == nullptr) {
- ALOGW("statscompanion service unavailable!");
- return nullptr;
- }
- }
- }
- return statsCompanion;
+shared_ptr<IStatsCompanionService> getStatsCompanionService() {
+ ::ndk::SpAIBinder binder(AServiceManager_getService("statscompanion"));
+ return IStatsCompanionService::fromBinder(binder);
}
} // namespace statsd
diff --git a/cmds/statsd/src/statscompanion_util.h b/cmds/statsd/src/statscompanion_util.h
index dc4f283..e20c40b 100644
--- a/cmds/statsd/src/statscompanion_util.h
+++ b/cmds/statsd/src/statscompanion_util.h
@@ -16,14 +16,17 @@
#pragma once
-#include "StatsLogProcessor.h"
+#include <aidl/android/os/IStatsCompanionService.h>
+
+using aidl::android::os::IStatsCompanionService;
+using std::shared_ptr;
namespace android {
namespace os {
namespace statsd {
/** Fetches and returns the StatsCompanionService. */
-sp<IStatsCompanionService> getStatsCompanionService();
+shared_ptr<IStatsCompanionService> getStatsCompanionService();
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
index 8fd6b46..93af5e9 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
@@ -27,38 +27,52 @@
using std::vector;
-class BroadcastSubscriberDeathRecipient : public android::IBinder::DeathRecipient {
- public:
- BroadcastSubscriberDeathRecipient(const ConfigKey& configKey, int64_t subscriberId):
- mConfigKey(configKey),
- mSubscriberId(subscriberId) {}
- ~BroadcastSubscriberDeathRecipient() override = default;
- private:
- ConfigKey mConfigKey;
- int64_t mSubscriberId;
+struct BroadcastSubscriberDeathCookie {
+ BroadcastSubscriberDeathCookie(const ConfigKey& configKey, int64_t subscriberId,
+ const shared_ptr<IPendingIntentRef>& pir):
+ mConfigKey(configKey),
+ mSubscriberId(subscriberId),
+ mPir(pir) {}
- void binderDied(const android::wp<android::IBinder>& who) override {
- if (IInterface::asBinder(SubscriberReporter::getInstance().getBroadcastSubscriber(
- mConfigKey, mSubscriberId)) == who.promote()) {
- SubscriberReporter::getInstance().unsetBroadcastSubscriber(mConfigKey, mSubscriberId);
- }
- }
+ ConfigKey mConfigKey;
+ int64_t mSubscriberId;
+ shared_ptr<IPendingIntentRef> mPir;
};
+static void broadcastSubscriberDied(void* cookie) {
+ BroadcastSubscriberDeathCookie* cookie_ = (BroadcastSubscriberDeathCookie*)cookie;
+ ConfigKey configKey = cookie_->mConfigKey;
+ int64_t subscriberId = cookie_->mSubscriberId;
+ shared_ptr<IPendingIntentRef> pir = cookie_->mPir;
+
+ // TODO(b/149254662): Fix threading. This currently fails if a new pir gets
+ // set between the get and the unset.
+ if (SubscriberReporter::getInstance().getBroadcastSubscriber(configKey, subscriberId) == pir) {
+ SubscriberReporter::getInstance().unsetBroadcastSubscriber(configKey, subscriberId);
+ }
+ // The death recipient corresponding to this specific pir can never be
+ // triggered again, so free up resources.
+ delete cookie_;
+}
+
+static ::ndk::ScopedAIBinder_DeathRecipient sBroadcastSubscriberDeathRecipient(
+ AIBinder_DeathRecipient_new(broadcastSubscriberDied));
+
void SubscriberReporter::setBroadcastSubscriber(const ConfigKey& configKey,
int64_t subscriberId,
- const sp<IPendingIntentRef>& pir) {
+ const shared_ptr<IPendingIntentRef>& pir) {
VLOG("SubscriberReporter::setBroadcastSubscriber called.");
- lock_guard<std::mutex> lock(mLock);
+ lock_guard<mutex> lock(mLock);
mIntentMap[configKey][subscriberId] = pir;
- IInterface::asBinder(pir)->linkToDeath(
- new BroadcastSubscriberDeathRecipient(configKey, subscriberId));
+ // TODO(b/149254662): Is it ok to call linkToDeath while holding a lock?
+ AIBinder_linkToDeath(pir->asBinder().get(), sBroadcastSubscriberDeathRecipient.get(),
+ new BroadcastSubscriberDeathCookie(configKey, subscriberId, pir));
}
void SubscriberReporter::unsetBroadcastSubscriber(const ConfigKey& configKey,
int64_t subscriberId) {
VLOG("SubscriberReporter::unsetBroadcastSubscriber called.");
- lock_guard<std::mutex> lock(mLock);
+ lock_guard<mutex> lock(mLock);
auto subscriberMapIt = mIntentMap.find(configKey);
if (subscriberMapIt != mIntentMap.end()) {
subscriberMapIt->second.erase(subscriberId);
@@ -80,7 +94,7 @@
// config id - the name of this config (for this particular uid)
VLOG("SubscriberReporter::alertBroadcastSubscriber called.");
- lock_guard<std::mutex> lock(mLock);
+ lock_guard<mutex> lock(mLock);
if (!subscription.has_broadcast_subscriber_details()
|| !subscription.broadcast_subscriber_details().has_subscriber_id()) {
@@ -89,10 +103,12 @@
}
int64_t subscriberId = subscription.broadcast_subscriber_details().subscriber_id();
- vector<String16> cookies;
+ // TODO(b/149254662): Is there a way to convert a RepeatedPtrField into a
+ // vector without copying?
+ vector<string> cookies;
cookies.reserve(subscription.broadcast_subscriber_details().cookie_size());
for (auto& cookie : subscription.broadcast_subscriber_details().cookie()) {
- cookies.push_back(String16(cookie.c_str()));
+ cookies.push_back(cookie);
}
auto it1 = mIntentMap.find(configKey);
@@ -109,10 +125,10 @@
sendBroadcastLocked(it2->second, configKey, subscription, cookies, dimKey);
}
-void SubscriberReporter::sendBroadcastLocked(const sp<IPendingIntentRef>& pir,
+void SubscriberReporter::sendBroadcastLocked(const shared_ptr<IPendingIntentRef>& pir,
const ConfigKey& configKey,
const Subscription& subscription,
- const vector<String16>& cookies,
+ const vector<string>& cookies,
const MetricDimensionKey& dimKey) const {
VLOG("SubscriberReporter::sendBroadcastLocked called.");
pir->sendSubscriberBroadcast(
@@ -124,9 +140,9 @@
dimKey.getDimensionKeyInWhat().toStatsDimensionsValueParcel());
}
-sp<IPendingIntentRef> SubscriberReporter::getBroadcastSubscriber(const ConfigKey& configKey,
- int64_t subscriberId) {
- lock_guard<std::mutex> lock(mLock);
+shared_ptr<IPendingIntentRef> SubscriberReporter::getBroadcastSubscriber(const ConfigKey& configKey,
+ int64_t subscriberId) {
+ lock_guard<mutex> lock(mLock);
auto subscriberMapIt = mIntentMap.find(configKey);
if (subscriberMapIt == mIntentMap.end()) {
return nullptr;
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h
index 42599f5..0f97d39 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.h
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.h
@@ -16,9 +16,9 @@
#pragma once
-#include <android/os/IPendingIntentRef.h>
-#include <android/os/IStatsCompanionService.h>
+#include <aidl/android/os/IPendingIntentRef.h>
#include <utils/RefBase.h>
+#include <utils/String16.h>
#include "config/ConfigKey.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // subscription
@@ -28,6 +28,13 @@
#include <unordered_map>
#include <vector>
+using aidl::android::os::IPendingIntentRef;
+using std::mutex;
+using std::shared_ptr;
+using std::string;
+using std::unordered_map;
+using std::vector;
+
namespace android {
namespace os {
namespace statsd {
@@ -51,7 +58,7 @@
*/
void setBroadcastSubscriber(const ConfigKey& configKey,
int64_t subscriberId,
- const sp<IPendingIntentRef>& pir);
+ const shared_ptr<IPendingIntentRef>& pir);
/**
* Erases any intentSender information from the given (configKey, subscriberId) pair.
@@ -67,28 +74,25 @@
const Subscription& subscription,
const MetricDimensionKey& dimKey) const;
- sp<IPendingIntentRef> getBroadcastSubscriber(const ConfigKey& configKey, int64_t subscriberId);
+ shared_ptr<IPendingIntentRef> getBroadcastSubscriber(const ConfigKey& configKey,
+ int64_t subscriberId);
private:
SubscriberReporter() {};
- mutable std::mutex mLock;
-
- /** Binder interface for communicating with StatsCompanionService. */
- sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+ mutable mutex mLock;
/** Maps <ConfigKey, SubscriberId> -> IPendingIntentRef (which represents a PendingIntent). */
- std::unordered_map<ConfigKey,
- std::unordered_map<int64_t, sp<IPendingIntentRef>>> mIntentMap;
+ unordered_map<ConfigKey, unordered_map<int64_t, shared_ptr<IPendingIntentRef>>> mIntentMap;
/**
* Sends a broadcast via the given intentSender (using mStatsCompanionService), along
* with the information in the other parameters.
*/
- void sendBroadcastLocked(const sp<IPendingIntentRef>& pir,
+ void sendBroadcastLocked(const shared_ptr<IPendingIntentRef>& pir,
const ConfigKey& configKey,
const Subscription& subscription,
- const std::vector<String16>& cookies,
+ const vector<string>& cookies,
const MetricDimensionKey& dimKey) const;
};
diff --git a/cmds/statsd/tests/AlarmMonitor_test.cpp b/cmds/statsd/tests/AlarmMonitor_test.cpp
index 1fccb35..edbf8b5 100644
--- a/cmds/statsd/tests/AlarmMonitor_test.cpp
+++ b/cmds/statsd/tests/AlarmMonitor_test.cpp
@@ -17,14 +17,16 @@
#include <gtest/gtest.h>
using namespace android::os::statsd;
+using std::shared_ptr;
#ifdef __ANDROID__
TEST(AlarmMonitor, popSoonerThan) {
std::string emptyMetricId;
std::string emptyDimensionId;
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> set;
- AlarmMonitor am(2, [](const sp<IStatsCompanionService>&, int64_t){},
- [](const sp<IStatsCompanionService>&){});
+ AlarmMonitor am(2,
+ [](const shared_ptr<IStatsCompanionService>&, int64_t){},
+ [](const shared_ptr<IStatsCompanionService>&){});
set = am.popSoonerThan(5);
EXPECT_TRUE(set.empty());
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 69e11ed..3d7e5c9 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -32,6 +32,8 @@
using namespace android;
using namespace testing;
+using ::ndk::SharedRefBase;
+using std::shared_ptr;
namespace android {
namespace os {
@@ -49,10 +51,12 @@
MockMetricsManager()
: MetricsManager(ConfigKey(1, 12345), StatsdConfig(), 1000, 1000, new UidMap(),
new StatsPullerManager(),
- new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t) {},
- [](const sp<IStatsCompanionService>&) {}),
- new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t) {},
- [](const sp<IStatsCompanionService>&) {})) {
+ new AlarmMonitor(10,
+ [](const shared_ptr<IStatsCompanionService>&, int64_t) {},
+ [](const shared_ptr<IStatsCompanionService>&) {}),
+ new AlarmMonitor(10,
+ [](const shared_ptr<IStatsCompanionService>&, int64_t) {},
+ [](const shared_ptr<IStatsCompanionService>&) {})) {
}
MOCK_METHOD0(byteSize, size_t());
@@ -1488,9 +1492,9 @@
metric2ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
// Send the config.
- StatsService service(nullptr, nullptr);
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
string serialized = config1.SerializeAsString();
- service.addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()});
+ service->addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()});
// Make sure the config is stored on disk. Otherwise, we will not reset on system server death.
StatsdConfig tmpConfig;
@@ -1501,7 +1505,7 @@
// Metric 2 is not active.
// Metric 3 is active.
// {{{---------------------------------------------------------------------------
- sp<StatsLogProcessor> processor = service.mProcessor;
+ sp<StatsLogProcessor> processor = service->mProcessor;
EXPECT_EQ(1, processor->mMetricsManagers.size());
auto it = processor->mMetricsManagers.find(cfgKey1);
EXPECT_TRUE(it != processor->mMetricsManagers.end());
@@ -1592,7 +1596,7 @@
EXPECT_TRUE(approximateSystemServerDeath < NS_PER_SEC + configAddedTimeNs);
// System server dies.
- service.binderDied(nullptr);
+ service->statsCompanionServiceDiedImpl();
// We should have a new metrics manager. Lets get it and ensure activation status is restored.
// {{{---------------------------------------------------------------------------
diff --git a/cmds/statsd/tests/StatsService_test.cpp b/cmds/statsd/tests/StatsService_test.cpp
index 7c00531..86f786e 100644
--- a/cmds/statsd/tests/StatsService_test.cpp
+++ b/cmds/statsd/tests/StatsService_test.cpp
@@ -16,6 +16,7 @@
#include "config/ConfigKey.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include <android/binder_interface_utils.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -29,33 +30,34 @@
namespace statsd {
using android::util::ProtoOutputStream;
+using ::ndk::SharedRefBase;
#ifdef __ANDROID__
TEST(StatsServiceTest, TestAddConfig_simple) {
- StatsService service(nullptr, nullptr);
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
StatsdConfig config;
config.set_id(12345);
string serialized = config.SerializeAsString();
EXPECT_TRUE(
- service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
+ service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
}
TEST(StatsServiceTest, TestAddConfig_empty) {
- StatsService service(nullptr, nullptr);
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
string serialized = "";
EXPECT_TRUE(
- service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
+ service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
}
TEST(StatsServiceTest, TestAddConfig_invalid) {
- StatsService service(nullptr, nullptr);
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
string serialized = "Invalid config!";
EXPECT_FALSE(
- service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
+ service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
}
TEST(StatsServiceTest, TestGetUidFromArgs) {
@@ -69,32 +71,32 @@
int32_t uid;
- StatsService service(nullptr, nullptr);
- service.mEngBuild = true;
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
+ service->mEngBuild = true;
// "-1"
- EXPECT_FALSE(service.getUidFromArgs(args, 0, uid));
+ EXPECT_FALSE(service->getUidFromArgs(args, 0, uid));
// "0"
- EXPECT_TRUE(service.getUidFromArgs(args, 1, uid));
+ EXPECT_TRUE(service->getUidFromArgs(args, 1, uid));
EXPECT_EQ(0, uid);
// "1"
- EXPECT_TRUE(service.getUidFromArgs(args, 2, uid));
+ EXPECT_TRUE(service->getUidFromArgs(args, 2, uid));
EXPECT_EQ(1, uid);
// "999999999999999999"
- EXPECT_FALSE(service.getUidFromArgs(args, 3, uid));
+ EXPECT_FALSE(service->getUidFromArgs(args, 3, uid));
// "a1"
- EXPECT_FALSE(service.getUidFromArgs(args, 4, uid));
+ EXPECT_FALSE(service->getUidFromArgs(args, 4, uid));
// ""
- EXPECT_FALSE(service.getUidFromArgs(args, 5, uid));
+ EXPECT_FALSE(service->getUidFromArgs(args, 5, uid));
// For a non-userdebug, uid "1" cannot be impersonated.
- service.mEngBuild = false;
- EXPECT_FALSE(service.getUidFromArgs(args, 2, uid));
+ service->mEngBuild = false;
+ EXPECT_FALSE(service->getUidFromArgs(args, 2, uid));
}
#else
diff --git a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
index 5e6de1c..90ffcd0 100644
--- a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
@@ -15,12 +15,14 @@
#include "src/anomaly/AlarmTracker.h"
#include <gtest/gtest.h>
+#include <log/log_time.h>
#include <stdio.h>
#include <vector>
using namespace testing;
using android::sp;
using std::set;
+using std::shared_ptr;
using std::unordered_map;
using std::vector;
@@ -34,8 +36,9 @@
TEST(AlarmTrackerTest, TestTriggerTimestamp) {
sp<AlarmMonitor> subscriberAlarmMonitor =
- new AlarmMonitor(100, [](const sp<IStatsCompanionService>&, int64_t){},
- [](const sp<IStatsCompanionService>&){});
+ new AlarmMonitor(100,
+ [](const shared_ptr<IStatsCompanionService>&, int64_t){},
+ [](const shared_ptr<IStatsCompanionService>&){});
Alarm alarm;
alarm.set_offset_millis(15 * MS_PER_SEC);
alarm.set_period_millis(60 * 60 * MS_PER_SEC); // 1hr
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index 5e60aba..9127be8 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <android/binder_interface_utils.h>
#include <gtest/gtest.h>
#include <vector>
@@ -20,6 +21,8 @@
#include "src/stats_log_util.h"
#include "tests/statsd_test_util.h"
+using ::ndk::SharedRefBase;
+
namespace android {
namespace os {
namespace statsd {
@@ -71,7 +74,8 @@
ConfigKey cfgKey;
auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
- new FakeSubsystemSleepCallback(), ATOM_TAG);
+ SharedRefBase::make<FakeSubsystemSleepCallback>(),
+ ATOM_TAG);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
@@ -216,7 +220,8 @@
ConfigKey cfgKey;
auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
- new FakeSubsystemSleepCallback(), ATOM_TAG);
+ SharedRefBase::make<FakeSubsystemSleepCallback>(),
+ ATOM_TAG);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
@@ -317,7 +322,8 @@
ConfigKey cfgKey;
auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
- new FakeSubsystemSleepCallback, ATOM_TAG);
+ SharedRefBase::make<FakeSubsystemSleepCallback>(),
+ ATOM_TAG);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
@@ -426,7 +432,8 @@
ConfigKey cfgKey;
auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
- new FakeSubsystemSleepCallback(), ATOM_TAG);
+ SharedRefBase::make<FakeSubsystemSleepCallback>(),
+ ATOM_TAG);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
@@ -544,7 +551,8 @@
ConfigKey cfgKey;
auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
- new FakeSubsystemSleepCallback(), ATOM_TAG);
+ SharedRefBase::make<FakeSubsystemSleepCallback>(),
+ ATOM_TAG);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
index 9d58867..1eecbe5 100644
--- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -12,9 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <android/os/BnPullAtomCallback.h>
-#include <android/os/IPullAtomResultReceiver.h>
-#include <binder/IPCThreadState.h>
+#include <android/binder_ibinder.h>
+#include <android/binder_interface_utils.h>
#include <gtest/gtest.h>
#include <vector>
@@ -24,6 +23,9 @@
#include "src/stats_log_util.h"
#include "tests/statsd_test_util.h"
+using::ndk::SharedRefBase;
+using std::shared_ptr;
+
namespace android {
namespace os {
namespace statsd {
@@ -34,19 +36,17 @@
const int kConfigKey = 789130123; // Randomly chosen to avoid collisions with existing configs.
const int kCallingUid = 0; // Randomly chosen
-void SendConfig(StatsService& service, const StatsdConfig& config) {
+void SendConfig(shared_ptr<StatsService>& service, const StatsdConfig& config) {
string str;
config.SerializeToString(&str);
- std::vector<uint8_t> configAsVec(str.begin(), str.end());
- bool success;
- service.addConfiguration(kConfigKey, configAsVec, kCallingUid);
+ std::vector<int8_t> configAsVec(str.begin(), str.end());
+ service->addConfiguration(kConfigKey, configAsVec, kCallingUid);
}
ConfigMetricsReport GetReports(sp<StatsLogProcessor> processor, int64_t timestamp,
bool include_current = false) {
vector<uint8_t> output;
- IPCThreadState* ipc = IPCThreadState::self();
- ConfigKey configKey(ipc->getCallingUid(), kConfigKey);
+ ConfigKey configKey(AIBinder_getCallingUid(), kConfigKey);
processor->onDumpReport(configKey, timestamp, include_current /* include_current_bucket*/,
true /* erase_data */, ADB_DUMP, NO_TIME_CONSTRAINTS, &output);
ConfigMetricsReportList reports;
@@ -114,56 +114,56 @@
} // anonymous namespace
TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) {
- StatsService service(nullptr, nullptr);
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
SendConfig(service, MakeConfig());
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
- service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
- service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 2).get());
+ service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
+ service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 2).get());
- ConfigMetricsReport report = GetReports(service.mProcessor, start + 3);
+ ConfigMetricsReport report = GetReports(service->mProcessor, start + 3);
// Expect no metrics since the bucket has not finished yet.
EXPECT_EQ(1, report.metrics_size());
EXPECT_EQ(0, report.metrics(0).count_metrics().data_size());
}
TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) {
- StatsService service(nullptr, nullptr);
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
SendConfig(service, MakeConfig());
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
// Force the uidmap to update at timestamp 2.
- service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
+ service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
// This is a new installation, so there shouldn't be a split (should be same as the without
// split case).
- service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
+ service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
String16(""));
// Goes into the second bucket.
- service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
+ service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
- ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
+ ConfigMetricsReport report = GetReports(service->mProcessor, start + 4);
EXPECT_EQ(1, report.metrics_size());
EXPECT_EQ(0, report.metrics(0).count_metrics().data_size());
}
TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) {
- StatsService service(nullptr, nullptr);
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
SendConfig(service, MakeConfig());
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
- service.mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
- {String16("")});
+ service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
+ {String16("")});
// Force the uidmap to update at timestamp 2.
- service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
- service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
+ service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
+ service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
String16(""));
// Goes into the second bucket.
- service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
+ service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
- ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
+ ConfigMetricsReport report = GetReports(service->mProcessor, start + 4);
backfillStartEndTimestamp(&report);
ASSERT_EQ(1, report.metrics_size());
@@ -177,20 +177,20 @@
}
TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) {
- StatsService service(nullptr, nullptr);
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
SendConfig(service, MakeConfig());
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
- service.mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
- {String16("")});
+ service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
+ {String16("")});
// Force the uidmap to update at timestamp 2.
- service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
- service.mUidMap->removeApp(start + 2, String16(kApp1.c_str()), 1);
+ service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
+ service->mUidMap->removeApp(start + 2, String16(kApp1.c_str()), 1);
// Goes into the second bucket.
- service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
+ service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
- ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
+ ConfigMetricsReport report = GetReports(service->mProcessor, start + 4);
backfillStartEndTimestamp(&report);
ASSERT_EQ(1, report.metrics_size());
@@ -204,44 +204,44 @@
}
TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) {
- StatsService service(nullptr, nullptr);
- service.mPullerManager->RegisterPullAtomCallback(
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
+ service->mPullerManager->RegisterPullAtomCallback(
/*uid=*/0, android::util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
- new FakeSubsystemSleepCallback());
+ SharedRefBase::make<FakeSubsystemSleepCallback>());
// Partial buckets don't occur when app is first installed.
- service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
+ service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
SendConfig(service, MakeValueMetricConfig(0));
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
- service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
- service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
- String16("v2"), String16(""));
+ service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
+ service->mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
+ String16("v2"), String16(""));
ConfigMetricsReport report =
- GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
+ GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
EXPECT_EQ(1, report.metrics_size());
EXPECT_EQ(0, report.metrics(0).value_metrics().skipped_size());
}
TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) {
- StatsService service(nullptr, nullptr);
- service.mPullerManager->RegisterPullAtomCallback(
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
+ service->mPullerManager->RegisterPullAtomCallback(
/*uid=*/0, android::util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
- new FakeSubsystemSleepCallback());
+ SharedRefBase::make<FakeSubsystemSleepCallback>());
// Partial buckets don't occur when app is first installed.
- service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
+ service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
SendConfig(service, MakeValueMetricConfig(60 * NS_PER_SEC /* One minute */));
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2;
- service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
- service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
+ service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
+ service->mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
String16(""));
ConfigMetricsReport report =
- GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
+ GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
backfillStartEndTimestamp(&report);
ASSERT_EQ(1, report.metrics_size());
@@ -253,38 +253,38 @@
}
TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) {
- StatsService service(nullptr, nullptr);
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
// Partial buckets don't occur when app is first installed.
- service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
+ service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
SendConfig(service, MakeGaugeMetricConfig(0));
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
- service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
- service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
+ service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
+ service->mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
String16("v2"), String16(""));
ConfigMetricsReport report =
- GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
+ GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
EXPECT_EQ(1, report.metrics_size());
EXPECT_EQ(0, report.metrics(0).gauge_metrics().skipped_size());
}
TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) {
- StatsService service(nullptr, nullptr);
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
// Partial buckets don't occur when app is first installed.
- service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
+ service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
SendConfig(service, MakeGaugeMetricConfig(60 * NS_PER_SEC /* One minute */));
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2;
- service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
- service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
- String16(""));
+ service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
+ service->mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
+ String16(""));
ConfigMetricsReport report =
- GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
+ GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
backfillStartEndTimestamp(&report);
ASSERT_EQ(1, report.metrics_size());
ASSERT_EQ(1, report.metrics(0).gauge_metrics().skipped_size());
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
index a140af8..9d39f9c 100644
--- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <android/binder_interface_utils.h>
#include <gtest/gtest.h>
#include "src/StatsLogProcessor.h"
@@ -20,6 +21,8 @@
#include <vector>
+using ::ndk::SharedRefBase;
+
namespace android {
namespace os {
namespace statsd {
@@ -70,7 +73,7 @@
ConfigKey cfgKey;
auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
- new FakeSubsystemSleepCallback(),
+ SharedRefBase::make<FakeSubsystemSleepCallback>(),
android::util::SUBSYSTEM_SLEEP_STATE);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
@@ -175,7 +178,7 @@
ConfigKey cfgKey;
auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
- new FakeSubsystemSleepCallback(),
+ SharedRefBase::make<FakeSubsystemSleepCallback>(),
android::util::SUBSYSTEM_SLEEP_STATE);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
@@ -288,7 +291,7 @@
ConfigKey cfgKey;
auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
- new FakeSubsystemSleepCallback(),
+ SharedRefBase::make<FakeSubsystemSleepCallback>(),
android::util::SUBSYSTEM_SLEEP_STATE);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
diff --git a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
index a011692e..e416b4c 100644
--- a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
+++ b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
@@ -14,8 +14,10 @@
#include "src/external/StatsCallbackPuller.h"
-#include <android/os/BnPullAtomCallback.h>
-#include <android/os/IPullAtomResultReceiver.h>
+#include <aidl/android/os/BnPullAtomCallback.h>
+#include <aidl/android/os/IPullAtomResultReceiver.h>
+#include <aidl/android/util/StatsEventParcel.h>
+#include <android/binder_interface_utils.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <stdio.h>
@@ -35,6 +37,11 @@
namespace statsd {
using namespace testing;
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::os::BnPullAtomCallback;
+using aidl::android::os::IPullAtomResultReceiver;
+using aidl::android::util::StatsEventParcel;
+using ::ndk::SharedRefBase;
using std::make_shared;
using std::shared_ptr;
using std::vector;
@@ -58,15 +65,15 @@
return event;
}
-void executePull(const sp<IPullAtomResultReceiver>& resultReceiver) {
+void executePull(const shared_ptr<IPullAtomResultReceiver>& resultReceiver) {
// Convert stats_events into StatsEventParcels.
- std::vector<android::util::StatsEventParcel> parcels;
+ vector<StatsEventParcel> parcels;
for (int i = 0; i < values.size(); i++) {
AStatsEvent* event = createSimpleEvent(values[i]);
size_t size;
uint8_t* buffer = AStatsEvent_getBuffer(event, &size);
- android::util::StatsEventParcel p;
+ StatsEventParcel p;
// vector.assign() creates a copy, but this is inevitable unless
// stats_event.h/c uses a vector as opposed to a buffer.
p.buffer.assign(buffer, buffer + size);
@@ -80,11 +87,11 @@
class FakePullAtomCallback : public BnPullAtomCallback {
public:
- binder::Status onPullAtom(int atomTag,
- const sp<IPullAtomResultReceiver>& resultReceiver) override {
+ Status onPullAtom(int atomTag,
+ const shared_ptr<IPullAtomResultReceiver>& resultReceiver) override {
// Force pull to happen in separate thread to simulate binder.
pullThread = std::thread(executePull, resultReceiver);
- return binder::Status::ok();
+ return Status::ok();
}
};
@@ -111,7 +118,7 @@
} // Anonymous namespace.
TEST_F(StatsCallbackPullerTest, PullSuccess) {
- sp<FakePullAtomCallback> cb = new FakePullAtomCallback();
+ shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
int64_t value = 43;
pullSuccess = true;
values.push_back(value);
@@ -132,20 +139,20 @@
}
TEST_F(StatsCallbackPullerTest, PullFail) {
- sp<FakePullAtomCallback> cb = new FakePullAtomCallback();
+ shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
pullSuccess = false;
int64_t value = 1234;
values.push_back(value);
StatsCallbackPuller puller(pullTagId, cb, pullCoolDownNs, pullTimeoutNs, {});
- vector<std::shared_ptr<LogEvent>> dataHolder;
+ vector<shared_ptr<LogEvent>> dataHolder;
EXPECT_FALSE(puller.PullInternal(&dataHolder));
EXPECT_EQ(0, dataHolder.size());
}
TEST_F(StatsCallbackPullerTest, PullTimeout) {
- sp<FakePullAtomCallback> cb = new FakePullAtomCallback();
+ shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
pullSuccess = true;
pullDelayNs = 500000000; // 500ms.
pullTimeoutNs = 10000; // 10 microseconds.
@@ -154,7 +161,7 @@
StatsCallbackPuller puller(pullTagId, cb, pullCoolDownNs, pullTimeoutNs, {});
- vector<std::shared_ptr<LogEvent>> dataHolder;
+ vector<shared_ptr<LogEvent>> dataHolder;
int64_t startTimeNs = getElapsedRealtimeNs();
// Returns true to let StatsPuller code evaluate the timeout.
EXPECT_TRUE(puller.PullInternal(&dataHolder));
@@ -174,7 +181,7 @@
// Register a puller and ensure that the timeout logic works.
TEST_F(StatsCallbackPullerTest, RegisterAndTimeout) {
- sp<FakePullAtomCallback> cb = new FakePullAtomCallback();
+ shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
pullSuccess = true;
pullDelayNs = 500000000; // 500 ms.
pullTimeoutNs = 10000; // 10 microsseconds.
@@ -184,7 +191,7 @@
StatsPullerManager pullerManager;
pullerManager.RegisterPullAtomCallback(/*uid=*/-1, pullTagId, pullCoolDownNs, pullTimeoutNs,
vector<int32_t>(), cb);
- vector<std::shared_ptr<LogEvent>> dataHolder;
+ vector<shared_ptr<LogEvent>> dataHolder;
int64_t startTimeNs = getElapsedRealtimeNs();
// Returns false, since StatsPuller code will evaluate the timeout.
EXPECT_FALSE(pullerManager.Pull(pullTagId, &dataHolder));
diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
index 73d1fd7..dac5f33 100644
--- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
+++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
@@ -34,27 +34,6 @@
#ifdef __ANDROID__
-class MyResultReceiver : public BnResultReceiver {
-public:
- Mutex mMutex;
- Condition mCondition;
- bool mHaveResult = false;
- int32_t mResult = 0;
-
- virtual void send(int32_t resultCode) {
- AutoMutex _l(mMutex);
- mResult = resultCode;
- mHaveResult = true;
- mCondition.signal();
- }
-
- int32_t waitForResult() {
- AutoMutex _l(mMutex);
- mCondition.waitRelative(mMutex, 1000000000);
- return mResult;
- }
-};
-
void runShellTest(ShellSubscription config, sp<MockUidMap> uidMap,
sp<MockStatsPullerManager> pullerManager,
const vector<std::shared_ptr<LogEvent>>& pushedEvents,
@@ -67,10 +46,7 @@
ASSERT_EQ(0, pipe(fds_data));
size_t bufferSize = config.ByteSize();
-
// write the config to pipe, first write size of the config
- vector<uint8_t> size_buffer(sizeof(bufferSize));
- std::memcpy(size_buffer.data(), &bufferSize, sizeof(bufferSize));
write(fds_config[1], &bufferSize, sizeof(bufferSize));
// then write config itself
vector<uint8_t> buffer(bufferSize);
@@ -79,11 +55,10 @@
close(fds_config[1]);
sp<ShellSubscriber> shellClient = new ShellSubscriber(uidMap, pullerManager);
- sp<MyResultReceiver> resultReceiver = new MyResultReceiver();
// mimic a binder thread that a shell subscriber runs on. it would block.
- std::thread reader([&resultReceiver, &fds_config, &fds_data, &shellClient] {
- shellClient->startNewSubscription(fds_config[0], fds_data[1], resultReceiver, -1);
+ std::thread reader([&shellClient, &fds_config, &fds_data] {
+ shellClient->startNewSubscription(fds_config[0], fds_data[1], /*timeoutSec=*/-1);
});
reader.detach();
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index db09ee9..6958218 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -14,6 +14,11 @@
#include "statsd_test_util.h"
+#include <aidl/android/util/StatsEventParcel.h>
+
+using aidl::android::util::StatsEventParcel;
+using std::shared_ptr;
+
namespace android {
namespace os {
namespace statsd {
@@ -581,7 +586,7 @@
sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs,
const StatsdConfig& config, const ConfigKey& key,
- const sp<IPullAtomCallback>& puller,
+ const shared_ptr<IPullAtomCallback>& puller,
const int32_t atomTag) {
sp<UidMap> uidMap = new UidMap();
sp<StatsPullerManager> pullerManager = new StatsPullerManager();
@@ -590,11 +595,13 @@
puller);
}
sp<AlarmMonitor> anomalyAlarmMonitor =
- new AlarmMonitor(1, [](const sp<IStatsCompanionService>&, int64_t){},
- [](const sp<IStatsCompanionService>&){});
+ new AlarmMonitor(1,
+ [](const shared_ptr<IStatsCompanionService>&, int64_t){},
+ [](const shared_ptr<IStatsCompanionService>&){});
sp<AlarmMonitor> periodicAlarmMonitor =
- new AlarmMonitor(1, [](const sp<IStatsCompanionService>&, int64_t){},
- [](const sp<IStatsCompanionService>&){});
+ new AlarmMonitor(1,
+ [](const shared_ptr<IStatsCompanionService>&, int64_t){},
+ [](const shared_ptr<IStatsCompanionService>&){});
sp<StatsLogProcessor> processor =
new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
timeBaseNs, [](const ConfigKey&) { return true; },
@@ -948,10 +955,10 @@
}
}
-binder::Status FakeSubsystemSleepCallback::onPullAtom(
- int atomTag, const sp<IPullAtomResultReceiver>& resultReceiver) {
+Status FakeSubsystemSleepCallback::onPullAtom(int atomTag,
+ const shared_ptr<IPullAtomResultReceiver>& resultReceiver) {
// Convert stats_events into StatsEventParcels.
- std::vector<android::util::StatsEventParcel> parcels;
+ std::vector<StatsEventParcel> parcels;
for (int i = 1; i < 3; i++) {
AStatsEvent* event = AStatsEvent_obtain();
AStatsEvent_setAtomId(event, atomTag);
@@ -965,7 +972,7 @@
size_t size;
uint8_t* buffer = AStatsEvent_getBuffer(event, &size);
- android::util::StatsEventParcel p;
+ StatsEventParcel p;
// vector.assign() creates a copy, but this is inevitable unless
// stats_event.h/c uses a vector as opposed to a buffer.
p.buffer.assign(buffer, buffer + size);
@@ -973,7 +980,7 @@
AStatsEvent_write(event);
}
resultReceiver->pullFinished(atomTag, /*success=*/true, parcels);
- return binder::Status::ok();
+ return Status::ok();
}
} // namespace statsd
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 576a491..c8326ee 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -14,9 +14,9 @@
#pragma once
-#include <android/os/BnPullAtomCallback.h>
-#include <android/os/IPullAtomCallback.h>
-#include <android/os/IPullAtomResultReceiver.h>
+#include <aidl/android/os/BnPullAtomCallback.h>
+#include <aidl/android/os/IPullAtomCallback.h>
+#include <aidl/android/os/IPullAtomResultReceiver.h>
#include <gtest/gtest.h>
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
@@ -31,8 +31,12 @@
namespace os {
namespace statsd {
+using ::aidl::android::os::BnPullAtomCallback;
+using ::aidl::android::os::IPullAtomCallback;
+using ::aidl::android::os::IPullAtomResultReceiver;
using android::util::ProtoReader;
using google::protobuf::RepeatedPtrField;
+using Status = ::ndk::ScopedAStatus;
const int SCREEN_STATE_ATOM_ID = android::util::SCREEN_STATE_CHANGED;
const int UID_PROCESS_STATE_ATOM_ID = android::util::UID_PROCESS_STATE_CHANGED;
@@ -230,7 +234,7 @@
// Create a statsd log event processor upon the start time in seconds, config and key.
sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs,
const StatsdConfig& config, const ConfigKey& key,
- const sp<IPullAtomCallback>& puller = nullptr,
+ const shared_ptr<IPullAtomCallback>& puller = nullptr,
const int32_t atomTag = 0 /*for puller only*/);
// Util function to sort the log events by timestamp.
@@ -285,8 +289,8 @@
class FakeSubsystemSleepCallback : public BnPullAtomCallback {
public:
- binder::Status onPullAtom(int atomTag,
- const sp<IPullAtomResultReceiver>& resultReceiver) override;
+ Status onPullAtom(int atomTag,
+ const shared_ptr<IPullAtomResultReceiver>& resultReceiver) override;
};
template <typename T>
diff --git a/core/java/android/content/pm/parsing/AndroidPackage.java b/core/java/android/content/pm/parsing/AndroidPackage.java
index da17ff3..b93c3a2 100644
--- a/core/java/android/content/pm/parsing/AndroidPackage.java
+++ b/core/java/android/content/pm/parsing/AndroidPackage.java
@@ -397,6 +397,8 @@
boolean hasComponentClassName(String className);
+ boolean hasPreserveLegacyExternalStorage();
+
// App Info
boolean hasRequestedLegacyExternalStorage();
diff --git a/core/java/android/content/pm/parsing/ApkParseUtils.java b/core/java/android/content/pm/parsing/ApkParseUtils.java
index a267113..905794b 100644
--- a/core/java/android/content/pm/parsing/ApkParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkParseUtils.java
@@ -2101,6 +2101,10 @@
parsingPackage.setAllowNativeHeapPointerTagging(sa.getBoolean(
R.styleable.AndroidManifestApplication_allowNativeHeapPointerTagging, true));
+ parsingPackage.setPreserveLegacyExternalStorage(sa.getBoolean(
+ R.styleable.AndroidManifestApplication_preserveLegacyExternalStorage,
+ false));
+
parsingPackage
.setMaxAspectRatio(
sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0))
diff --git a/core/java/android/content/pm/parsing/PackageImpl.java b/core/java/android/content/pm/parsing/PackageImpl.java
index 778d7b8..180714a 100644
--- a/core/java/android/content/pm/parsing/PackageImpl.java
+++ b/core/java/android/content/pm/parsing/PackageImpl.java
@@ -289,6 +289,7 @@
private String[] usesLibraryFiles;
private List<SharedLibraryInfo> usesLibraryInfos;
private String zygotePreloadName;
+ private boolean preserveLegacyExternalStorage;
@VisibleForTesting
public PackageImpl(
@@ -1519,6 +1520,12 @@
}
@Override
+ public PackageImpl setPreserveLegacyExternalStorage(boolean preserveLegacyExternalStorage) {
+ this.preserveLegacyExternalStorage = preserveLegacyExternalStorage;
+ return this;
+ }
+
+ @Override
public PackageImpl setUsesNonSdkApi(boolean usesNonSdkApi) {
this.privateFlags = usesNonSdkApi
? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API
@@ -2999,6 +3006,11 @@
return queriesProviders;
}
+ @Override
+ public boolean hasPreserveLegacyExternalStorage() {
+ return preserveLegacyExternalStorage;
+ }
+
private static void internStringArrayList(List<String> list) {
if (list != null) {
final int N = list.size();
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 954d65c..74a2640 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -192,6 +192,8 @@
ParsingPackage setRequestLegacyExternalStorage(boolean requestLegacyExternalStorage);
ParsingPackage setAllowNativeHeapPointerTagging(boolean allowNativeHeapPointerTagging);
+
+ ParsingPackage setPreserveLegacyExternalStorage(boolean preserveLegacyExternalStorage);
ParsingPackage setRestoreAnyVersion(boolean restoreAnyVersion);
diff --git a/core/java/android/inputmethodservice/InlineSuggestionSession.java b/core/java/android/inputmethodservice/InlineSuggestionSession.java
new file mode 100644
index 0000000..edae06a
--- /dev/null
+++ b/core/java/android/inputmethodservice/InlineSuggestionSession.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2020 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.inputmethodservice;
+
+import static android.inputmethodservice.InputMethodService.DEBUG;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.inputmethod.InlineSuggestionsRequest;
+import android.view.inputmethod.InlineSuggestionsResponse;
+
+import com.android.internal.view.IInlineSuggestionsRequestCallback;
+import com.android.internal.view.IInlineSuggestionsResponseCallback;
+
+import java.lang.ref.WeakReference;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * Maintains an active inline suggestion session.
+ *
+ * <p>
+ * Each session corresponds to one inline suggestion request, but there may be multiple callbacks
+ * with the inline suggestions response.
+ */
+class InlineSuggestionSession {
+
+ private static final String TAG = InlineSuggestionSession.class.getSimpleName();
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
+
+ @NonNull
+ private final ComponentName mComponentName;
+ @NonNull
+ private final IInlineSuggestionsRequestCallback mCallback;
+ @NonNull
+ private final InlineSuggestionsResponseCallbackImpl mResponseCallback;
+ @NonNull
+ private final Supplier<String> mClientPackageNameSupplier;
+ @NonNull
+ private final Supplier<InlineSuggestionsRequest> mRequestSupplier;
+ @NonNull
+ private final Supplier<IBinder> mHostInputTokenSupplier;
+ @NonNull
+ private final Consumer<InlineSuggestionsResponse> mResponseConsumer;
+
+ private volatile boolean mInvalidated = false;
+
+ InlineSuggestionSession(@NonNull ComponentName componentName,
+ @NonNull IInlineSuggestionsRequestCallback callback,
+ @NonNull Supplier<String> clientPackageNameSupplier,
+ @NonNull Supplier<InlineSuggestionsRequest> requestSupplier,
+ @NonNull Supplier<IBinder> hostInputTokenSupplier,
+ @NonNull Consumer<InlineSuggestionsResponse> responseConsumer) {
+ mComponentName = componentName;
+ mCallback = callback;
+ mResponseCallback = new InlineSuggestionsResponseCallbackImpl(this);
+ mClientPackageNameSupplier = clientPackageNameSupplier;
+ mRequestSupplier = requestSupplier;
+ mHostInputTokenSupplier = hostInputTokenSupplier;
+ mResponseConsumer = responseConsumer;
+
+ makeInlineSuggestionsRequest();
+ }
+
+ /**
+ * This needs to be called before creating a new session, such that the later response callbacks
+ * will be discarded.
+ */
+ void invalidateSession() {
+ mInvalidated = true;
+ }
+
+ /**
+ * Sends an {@link InlineSuggestionsRequest} obtained from {@cocde supplier} to the current
+ * Autofill Session through
+ * {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsRequest}.
+ */
+ private void makeInlineSuggestionsRequest() {
+ try {
+ final InlineSuggestionsRequest request = mRequestSupplier.get();
+ if (request == null) {
+ if (DEBUG) {
+ Log.d(TAG, "onCreateInlineSuggestionsRequest() returned null request");
+ }
+ mCallback.onInlineSuggestionsUnsupported();
+ } else {
+ request.setHostInputToken(mHostInputTokenSupplier.get());
+ mCallback.onInlineSuggestionsRequest(request, mResponseCallback);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "makeInlinedSuggestionsRequest() remote exception:" + e);
+ }
+ }
+
+ private void handleOnInlineSuggestionsResponse(@NonNull InlineSuggestionsResponse response) {
+ if (mInvalidated) {
+ if (DEBUG) {
+ Log.d(TAG, "handleOnInlineSuggestionsResponse() called on invalid session");
+ }
+ return;
+ }
+ // TODO(b/149522488): checking the current focused input field to make sure we don't send
+ // inline responses for previous input field
+ if (!mComponentName.getPackageName().equals(mClientPackageNameSupplier.get())) {
+ if (DEBUG) {
+ Log.d(TAG, "handleOnInlineSuggestionsResponse() called on the wrong package name");
+ }
+ return;
+ }
+ mResponseConsumer.accept(response);
+ }
+
+ /**
+ * Internal implementation of {@link IInlineSuggestionsResponseCallback}.
+ */
+ static final class InlineSuggestionsResponseCallbackImpl
+ extends IInlineSuggestionsResponseCallback.Stub {
+ private final WeakReference<InlineSuggestionSession> mInlineSuggestionSession;
+
+ private InlineSuggestionsResponseCallbackImpl(
+ InlineSuggestionSession inlineSuggestionSession) {
+ mInlineSuggestionSession = new WeakReference<>(inlineSuggestionSession);
+ }
+
+ @Override
+ public void onInlineSuggestionsResponse(InlineSuggestionsResponse response)
+ throws RemoteException {
+ final InlineSuggestionSession session = mInlineSuggestionSession.get();
+ if (session != null) {
+ session.mHandler.sendMessage(obtainMessage(
+ InlineSuggestionSession::handleOnInlineSuggestionsResponse, session,
+ response));
+ }
+ }
+ }
+}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index a95938b..b1aa67e 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -22,8 +22,6 @@
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.AnyThread;
@@ -51,7 +49,6 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemClock;
@@ -102,13 +99,11 @@
import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
-import com.android.internal.view.IInlineSuggestionsResponseCallback;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
import java.util.Collections;
/**
@@ -450,7 +445,7 @@
final int[] mTmpLocation = new int[2];
@Nullable
- private InlineSuggestionsRequestInfo mInlineSuggestionsRequestInfo = null;
+ private InlineSuggestionSession mInlineSuggestionSession;
private boolean mAutomotiveHideNavBarForKeyboard;
private boolean mIsAutomotive;
@@ -465,8 +460,6 @@
*/
private IBinder mCurShowInputToken;
- private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
-
final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> {
onComputeInsets(mTmpInsets);
if (isExtractViewShown()) {
@@ -538,7 +531,7 @@
if (DEBUG) {
Log.d(TAG, "InputMethodService received onCreateInlineSuggestionsRequest()");
}
- handleOnCreateInlineSuggestionsRequest(componentName, autofillId, cb);
+ handleOnCreateInlineSuggestionsRequest(componentName, cb);
}
/**
@@ -770,40 +763,9 @@
return false;
}
- /**
- * Sends an {@link InlineSuggestionsRequest} obtained from
- * {@link #onCreateInlineSuggestionsRequest()} to the current Autofill Session through
- * {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsRequest}.
- */
- private void makeInlineSuggestionsRequest() {
- if (mInlineSuggestionsRequestInfo == null) {
- Log.w(TAG, "makeInlineSuggestionsRequest() called with null requestInfo cache");
- return;
- }
-
- final IInlineSuggestionsRequestCallback requestCallback =
- mInlineSuggestionsRequestInfo.mCallback;
- try {
- final InlineSuggestionsRequest request = onCreateInlineSuggestionsRequest();
- if (request == null) {
- Log.w(TAG, "onCreateInlineSuggestionsRequest() returned null request");
- requestCallback.onInlineSuggestionsUnsupported();
- } else {
- request.setHostInputToken(getHostInputToken());
- final IInlineSuggestionsResponseCallback inlineSuggestionsResponseCallback =
- new InlineSuggestionsResponseCallbackImpl(this,
- mInlineSuggestionsRequestInfo.mComponentName,
- mInlineSuggestionsRequestInfo.mFocusedId);
- requestCallback.onInlineSuggestionsRequest(request,
- inlineSuggestionsResponseCallback);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "makeInlinedSuggestionsRequest() remote exception:" + e);
- }
- }
-
+ @MainThread
private void handleOnCreateInlineSuggestionsRequest(@NonNull ComponentName componentName,
- @NonNull AutofillId autofillId, @NonNull IInlineSuggestionsRequestCallback callback) {
+ @NonNull IInlineSuggestionsRequestCallback callback) {
if (!mInputStarted) {
try {
Log.w(TAG, "onStartInput() not called yet");
@@ -814,24 +776,20 @@
return;
}
- mInlineSuggestionsRequestInfo = new InlineSuggestionsRequestInfo(componentName, autofillId,
- callback);
-
- makeInlineSuggestionsRequest();
+ if (mInlineSuggestionSession != null) {
+ mInlineSuggestionSession.invalidateSession();
+ }
+ mInlineSuggestionSession = new InlineSuggestionSession(componentName, callback,
+ this::getEditorInfoPackageName, this::onCreateInlineSuggestionsRequest,
+ this::getHostInputToken, this::onInlineSuggestionsResponse);
}
- private void handleOnInlineSuggestionsResponse(@NonNull ComponentName componentName,
- @NonNull AutofillId autofillId, @NonNull InlineSuggestionsResponse response) {
- if (!mInlineSuggestionsRequestInfo.validate(componentName)) {
- if (DEBUG) {
- Log.d(TAG,
- "Response component=" + componentName + " differs from request component="
- + mInlineSuggestionsRequestInfo.mComponentName
- + ", ignoring response");
- }
- return;
+ @Nullable
+ private String getEditorInfoPackageName() {
+ if (mInputEditorInfo != null) {
+ return mInputEditorInfo.packageName;
}
- onInlineSuggestionsResponse(response);
+ return null;
}
/**
@@ -864,63 +822,6 @@
}
/**
- * Internal implementation of {@link IInlineSuggestionsResponseCallback}.
- */
- private static final class InlineSuggestionsResponseCallbackImpl
- extends IInlineSuggestionsResponseCallback.Stub {
- private final WeakReference<InputMethodService> mInputMethodService;
-
- private final ComponentName mRequestComponentName;
- private final AutofillId mRequestAutofillId;
-
- private InlineSuggestionsResponseCallbackImpl(InputMethodService inputMethodService,
- ComponentName componentName, AutofillId autofillId) {
- mInputMethodService = new WeakReference<>(inputMethodService);
- mRequestComponentName = componentName;
- mRequestAutofillId = autofillId;
- }
-
- @Override
- public void onInlineSuggestionsResponse(InlineSuggestionsResponse response)
- throws RemoteException {
- final InputMethodService service = mInputMethodService.get();
- if (service != null) {
- service.mHandler.sendMessage(obtainMessage(
- InputMethodService::handleOnInlineSuggestionsResponse, service,
- mRequestComponentName, mRequestAutofillId, response));
- }
- }
- }
-
- /**
- * Information about incoming requests from Autofill Frameworks for inline suggestions.
- */
- private static final class InlineSuggestionsRequestInfo {
- final ComponentName mComponentName;
- final AutofillId mFocusedId;
- final IInlineSuggestionsRequestCallback mCallback;
-
- InlineSuggestionsRequestInfo(ComponentName componentName, AutofillId focusedId,
- IInlineSuggestionsRequestCallback callback) {
- this.mComponentName = componentName;
- this.mFocusedId = focusedId;
- this.mCallback = callback;
- }
-
- /**
- * Returns whether the cached {@link ComponentName} matches the passed in activity.
- */
- public boolean validate(ComponentName componentName) {
- final boolean result = componentName.equals(mComponentName);
- if (DEBUG && !result) {
- Log.d(TAG, "Cached request info ComponentName=" + mComponentName
- + " differs from received ComponentName=" + componentName);
- }
- return result;
- }
- }
-
- /**
* Concrete implementation of
* {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
* all of the standard behavior for an input method session.
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index 5cac5f5..e78aef5 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -118,4 +118,10 @@
* @param userId the userId for which to reset storage
*/
public abstract void resetUser(int userId);
+
+ /**
+ * Returns {@code true} if the immediate last installed version of an app with {@code uid} had
+ * legacy storage, {@code false} otherwise.
+ */
+ public abstract boolean hasLegacyExternalStorage(int uid);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a8f88d4..f6072f9 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6462,6 +6462,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS =
"lock_screen_allow_private_notifications";
@@ -7880,6 +7881,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @TestApi
public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
/**
@@ -8082,6 +8084,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS =
"lock_screen_show_notifications";
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 79852d3..672b501 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -89,6 +89,8 @@
private SparseArray<AutofillProxy> mAutofillProxies;
+ private AutofillProxy mAutofillProxyForLastRequest;
+
// Used for metrics / debug only
private ComponentName mServiceComponentName;
@@ -158,6 +160,38 @@
}
/**
+ * The child class of the service can call this method to initiate an Autofill flow.
+ *
+ * <p> The request would be respected only if the previous augmented autofill request was
+ * made for the same {@code activityComponent} and {@code autofillId}, and the field is
+ * currently on focus.
+ *
+ * <p> The request would start a new autofill flow. It doesn't guarantee that the
+ * {@link AutofillManager} will proceed with the request.
+ *
+ * @param activityComponent the client component for which the autofill is requested for
+ * @param autofillId the client field id for which the autofill is requested for
+ * @return true if the request makes the {@link AutofillManager} start a new Autofill flow,
+ * false otherwise.
+ */
+ public final boolean requestAutofill(@NonNull ComponentName activityComponent,
+ @NonNull AutofillId autofillId) {
+ // TODO(b/149531989): revisit this. The request should start a new autofill session
+ // rather than reusing the existing session.
+ final AutofillProxy proxy = mAutofillProxyForLastRequest;
+ if (proxy == null || !proxy.mComponentName.equals(activityComponent)
+ || !proxy.mFocusedId.equals(autofillId)) {
+ return false;
+ }
+ try {
+ return proxy.requestAutofill();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return false;
+ }
+
+ /**
* Asks the service to handle an "augmented" autofill request.
*
* <p>This method is called when the "stantard" autofill service cannot handle a request, which
@@ -241,6 +275,7 @@
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
+ mAutofillProxyForLastRequest = proxy;
onFillRequest(new FillRequest(proxy, inlineSuggestionsRequest), cancellationSignal,
new FillController(proxy), new FillCallback(proxy));
}
@@ -268,6 +303,7 @@
proxy.destroy();
}
mAutofillProxies.clear();
+ mAutofillProxyForLastRequest = null;
}
}
@@ -287,6 +323,7 @@
}
}
mAutofillProxies = null;
+ mAutofillProxyForLastRequest = null;
}
@Override
@@ -481,6 +518,11 @@
mClient.requestHideFillUi(mSessionId, mFocusedId);
}
+
+ private boolean requestAutofill() throws RemoteException {
+ return mClient.requestAutofill(mSessionId, mFocusedId);
+ }
+
private void update(@NonNull AutofillId focusedId, @NonNull AutofillValue focusedValue,
@NonNull IFillCallback callback, @NonNull CancellationSignal cancellationSignal) {
synchronized (mLock) {
diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsCallback.java b/core/java/android/service/quickaccesswallet/GetWalletCardsCallback.java
index 9d210cd..f6a86d9 100644
--- a/core/java/android/service/quickaccesswallet/GetWalletCardsCallback.java
+++ b/core/java/android/service/quickaccesswallet/GetWalletCardsCallback.java
@@ -17,28 +17,11 @@
package android.service.quickaccesswallet;
import android.annotation.NonNull;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.util.Log;
/**
* Handles response from the {@link QuickAccessWalletService} for {@link GetWalletCardsRequest}
*/
-public final class GetWalletCardsCallback {
-
- private static final String TAG = "QAWalletCallback";
-
- private final IQuickAccessWalletServiceCallbacks mCallback;
- private final Handler mHandler;
- private boolean mCalled;
-
- /**
- * @hide
- */
- GetWalletCardsCallback(IQuickAccessWalletServiceCallbacks callback, Handler handler) {
- mCallback = callback;
- mHandler = handler;
- }
+public interface GetWalletCardsCallback {
/**
* Notifies the Android System that an {@link QuickAccessWalletService#onWalletCardsRequested}
@@ -46,11 +29,10 @@
*
* @param response The response contains the list of {@link WalletCard walletCards} to be shown
* to the user as well as the index of the card that should initially be
- * presented as the selected card.
+ * presented as the selected card. The list should not contain more than the
+ * maximum number of cards requested.
*/
- public void onSuccess(@NonNull GetWalletCardsResponse response) {
- mHandler.post(() -> onSuccessInternal(response));
- }
+ void onSuccess(@NonNull GetWalletCardsResponse response);
/**
* Notifies the Android System that an {@link QuickAccessWalletService#onWalletCardsRequested}
@@ -60,33 +42,5 @@
* (Personally Identifiable Information, such as username or email address).
* @throws IllegalStateException if this method or {@link #onSuccess} was already called.
*/
- public void onFailure(@NonNull GetWalletCardsError error) {
- mHandler.post(() -> onFailureInternal(error));
- }
-
- private void onSuccessInternal(GetWalletCardsResponse response) {
- if (mCalled) {
- Log.w(TAG, "already called");
- return;
- }
- mCalled = true;
- try {
- mCallback.onGetWalletCardsSuccess(response);
- } catch (RemoteException e) {
- Log.e(TAG, "Error returning wallet cards", e);
- }
- }
-
- private void onFailureInternal(GetWalletCardsError error) {
- if (mCalled) {
- Log.w(TAG, "already called");
- return;
- }
- mCalled = true;
- try {
- mCallback.onGetWalletCardsFailure(error);
- } catch (RemoteException e) {
- Log.e(TAG, "Error returning failure message", e);
- }
- }
+ void onFailure(@NonNull GetWalletCardsError error);
}
diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java b/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java
new file mode 100644
index 0000000..d2494a5
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2020 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.service.quickaccesswallet;
+
+import android.annotation.NonNull;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Icon;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * Handles response from the {@link QuickAccessWalletService} for {@link GetWalletCardsRequest}
+ *
+ * @hide
+ */
+final class GetWalletCardsCallbackImpl implements GetWalletCardsCallback {
+
+ private static final String TAG = "QAWalletCallback";
+
+ private final IQuickAccessWalletServiceCallbacks mCallback;
+ private final GetWalletCardsRequest mRequest;
+ private final Handler mHandler;
+ private boolean mCalled;
+
+ GetWalletCardsCallbackImpl(GetWalletCardsRequest request,
+ IQuickAccessWalletServiceCallbacks callback, Handler handler) {
+ mRequest = request;
+ mCallback = callback;
+ mHandler = handler;
+ }
+
+ /**
+ * Notifies the Android System that an {@link QuickAccessWalletService#onWalletCardsRequested}
+ * was successfully handled by the service.
+ *
+ * @param response The response contains the list of {@link WalletCard walletCards} to be shown
+ * to the user as well as the index of the card that should initially be
+ * presented as the selected card.
+ */
+ public void onSuccess(@NonNull GetWalletCardsResponse response) {
+ Log.i(TAG, "onSuccess");
+ if (isValidResponse(response)) {
+ mHandler.post(() -> onSuccessInternal(response));
+ } else {
+ Log.w(TAG, "Invalid GetWalletCards response");
+ mHandler.post(() -> onFailureInternal(new GetWalletCardsError(null, null)));
+ }
+ }
+
+ /**
+ * Notifies the Android System that an {@link QuickAccessWalletService#onWalletCardsRequested}
+ * could not be handled by the service.
+ *
+ * @param error The error message. <b>Note: </b> this message should <b>not</b> contain PII
+ * (Personally Identifiable Information, such as username or email address).
+ * @throws IllegalStateException if this method or {@link #onSuccess} was already called.
+ */
+ public void onFailure(@NonNull GetWalletCardsError error) {
+ mHandler.post(() -> onFailureInternal(error));
+ }
+
+ private void onSuccessInternal(GetWalletCardsResponse response) {
+ Log.i(TAG, "onSuccessInternal");
+ if (mCalled) {
+ Log.w(TAG, "already called");
+ return;
+ }
+ mCalled = true;
+ try {
+ mCallback.onGetWalletCardsSuccess(response);
+ Log.i(TAG, "onSuccessInternal: returned response");
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error returning wallet cards", e);
+ }
+ }
+
+ private void onFailureInternal(GetWalletCardsError error) {
+ if (mCalled) {
+ Log.w(TAG, "already called");
+ return;
+ }
+ mCalled = true;
+ try {
+ mCallback.onGetWalletCardsFailure(error);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error returning failure message", e);
+ }
+ }
+
+ private boolean isValidResponse(@NonNull GetWalletCardsResponse response) {
+ return response != null
+ && response.getWalletCards() != null
+ && response.getSelectedIndex() >= 0
+ && (response.getWalletCards().isEmpty() // selectedIndex may be 0 when list is empty
+ || response.getSelectedIndex() < response.getWalletCards().size())
+ && response.getWalletCards().size() < mRequest.getMaxCards()
+ && areValidCards(response.getWalletCards());
+ }
+
+ private boolean areValidCards(List<WalletCard> walletCards) {
+ for (WalletCard walletCard : walletCards) {
+ if (walletCard == null
+ || walletCard.getCardId() == null
+ || walletCard.getCardImage() == null
+ || TextUtils.isEmpty(walletCard.getContentDescription())
+ || walletCard.getPendingIntent() == null) {
+ return false;
+ }
+ Icon cardImage = walletCard.getCardImage();
+ if (cardImage.getType() == Icon.TYPE_BITMAP
+ && walletCard.getCardImage().getBitmap().getConfig()
+ != Bitmap.Config.HARDWARE) {
+ Log.w(TAG, "WalletCard bitmaps should be hardware bitmaps");
+ }
+ }
+ return true;
+ }
+}
diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java b/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java
index 996622a..0551e27 100644
--- a/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java
+++ b/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java
@@ -35,9 +35,13 @@
/**
* Construct a new response.
*
- * @param walletCards The list of wallet cards.
+ * @param walletCards The list of wallet cards. The list may be empty but must NOT be larger
+ * than {@link GetWalletCardsRequest#getMaxCards()}. The list may not
+ * contain null values.
* @param selectedIndex The index of the card that should be presented as the initially
- * 'selected' card
+ * 'selected' card. The index must be greater than or equal to zero and
+ * less than the size of the list of walletCards (unless the list is empty
+ * in which case the value may be 0).
*/
public GetWalletCardsResponse(@NonNull List<WalletCard> walletCards, int selectedIndex) {
this.mWalletCards = walletCards;
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
index cfc6d57..be9ab11 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
@@ -18,16 +18,16 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.content.Context;
import android.content.Intent;
-import java.util.function.Consumer;
-
/**
* Facilitates accessing cards from the {@link QuickAccessWalletService}.
*
* @hide
*/
+@TestApi
public interface QuickAccessWalletClient {
/**
@@ -40,17 +40,65 @@
}
/**
- * @return true if the {@link QuickAccessWalletService} is available.
+ * @return true if the {@link QuickAccessWalletService} is available. This means that the
+ * default NFC payment application has an exported service that can provide cards to the Quick
+ * Access Wallet. However, it does not mean that (1) the call will necessarily be successful,
+ * nor does it mean that cards may be displayed at this time. Addition checks are required:
+ * <ul>
+ * <li>If {@link #isWalletFeatureAvailable()} is false, cards should not be displayed
+ * <li>If the device is locked and {@link #isWalletFeatureAvailableWhenDeviceLocked} is
+ * false, cards should not be displayed while the device remains locked. (A message
+ * prompting the user to unlock to view cards may be appropriate).</li>
+ * </ul>
*/
boolean isWalletServiceAvailable();
/**
+ * Wallet cards should not be displayed if:
+ * <ul>
+ * <li>The wallet service is unavailable</li>
+ * <li>The device is not provisioned, ie user setup is incomplete</li>
+ * <li>If the wallet feature has been disabled by the user</li>
+ * <li>If the phone has been put into lockdown mode</li>
+ * </ul>
+ * <p>
+ * Quick Access Wallet implementers should call this method before calling
+ * {@link #getWalletCards} to ensure that cards may be displayed.
+ */
+ boolean isWalletFeatureAvailable();
+
+ /**
+ * Wallet cards may not be displayed on the lock screen if the user has opted to hide
+ * notifications or sensitive content on the lock screen.
+ * <ul>
+ * <li>The device is not provisioned, ie user setup is incomplete</li>
+ * <li>If the wallet feature has been disabled by the user</li>
+ * <li>If the phone has been put into lockdown mode</li>
+ * </ul>
+ *
+ * <p>
+ * Quick Access Wallet implementers should call this method before calling
+ * {@link #getWalletCards} if the device is currently locked.
+ *
+ * @return true if cards may be displayed on the lock screen.
+ */
+ boolean isWalletFeatureAvailableWhenDeviceLocked();
+
+ /**
* Get wallet cards from the {@link QuickAccessWalletService}.
*/
void getWalletCards(
@NonNull GetWalletCardsRequest request,
- @NonNull Consumer<GetWalletCardsResponse> onSuccessListener,
- @NonNull Consumer<GetWalletCardsError> onFailureListener);
+ @NonNull OnWalletCardsRetrievedCallback callback);
+
+ /**
+ * Callback for getWalletCards
+ */
+ interface OnWalletCardsRetrievedCallback {
+ void onWalletCardsRetrieved(@NonNull GetWalletCardsResponse response);
+
+ void onWalletCardRetrievalError(@NonNull GetWalletCardsError error);
+ }
/**
* Notify the {@link QuickAccessWalletService} service that a wallet card was selected.
@@ -65,12 +113,24 @@
/**
* Unregister event listener.
*/
- void registerWalletServiceEventListener(Consumer<WalletServiceEvent> listener);
+ void addWalletServiceEventListener(@NonNull WalletServiceEventListener listener);
/**
* Unregister event listener
*/
- void unregisterWalletServiceEventListener(Consumer<WalletServiceEvent> listener);
+ void removeWalletServiceEventListener(@NonNull WalletServiceEventListener listener);
+
+ /**
+ * A listener for {@link WalletServiceEvent walletServiceEvents}
+ */
+ interface WalletServiceEventListener {
+ void onWalletServiceEvent(@NonNull WalletServiceEvent event);
+ }
+
+ /**
+ * Unregister all event listeners and disconnect from the service.
+ */
+ void disconnect();
/**
* The manifest entry for the QuickAccessWalletService may also publish information about the
@@ -78,12 +138,12 @@
* application.
*/
@Nullable
- Intent getWalletActivity();
+ Intent createWalletIntent();
/**
* The manifest entry for the {@link QuickAccessWalletService} may publish the activity that
* hosts the settings
*/
@Nullable
- Intent getSettingsActivity();
+ Intent createWalletSettingsIntent();
}
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
index 17c287f..37a8703 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -27,79 +28,225 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
-import android.os.Message;
import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.widget.LockPatternUtils;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.UUID;
-import java.util.function.Consumer;
/**
+ * Implements {@link QuickAccessWalletClient}. The client connects, performs requests, waits for
+ * responses, and disconnects automatically after a short period of time. The client may
* @hide
*/
-@SuppressWarnings("AndroidJdkLibsChecker")
-class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Handler.Callback,
- ServiceConnection {
+public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, ServiceConnection {
private static final String TAG = "QAWalletSClient";
private final Handler mHandler;
private final Context mContext;
private final Queue<ApiCaller> mRequestQueue;
- private final Map<Consumer<WalletServiceEvent>, String> mEventListeners;
+ private final Map<WalletServiceEventListener, String> mEventListeners;
private boolean mIsConnected;
+ /**
+ * Timeout for active service connections (1 minute)
+ */
+ private static final long SERVICE_CONNECTION_TIMEOUT_MS = 60 * 1000;
@Nullable
private IQuickAccessWalletService mService;
-
@Nullable
private final QuickAccessWalletServiceInfo mServiceInfo;
- private static final int MSG_CONNECT = 1;
- private static final int MSG_CONNECTED = 2;
- private static final int MSG_EXECUTE = 3;
- private static final int MSG_DISCONNECT = 4;
+ private static final int MSG_TIMEOUT_SERVICE = 5;
QuickAccessWalletClientImpl(@NonNull Context context) {
mContext = context.getApplicationContext();
mServiceInfo = QuickAccessWalletServiceInfo.tryCreate(context);
- mHandler = new Handler(Looper.getMainLooper(), this);
+ mHandler = new Handler(Looper.getMainLooper());
mRequestQueue = new LinkedList<>();
mEventListeners = new HashMap<>(1);
}
@Override
- public boolean handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_CONNECT:
- connectInternal();
- break;
- case MSG_CONNECTED:
- onConnectedInternal((IQuickAccessWalletService) msg.obj);
- break;
- case MSG_EXECUTE:
- executeInternal((ApiCaller) msg.obj);
- break;
- case MSG_DISCONNECT:
- disconnectInternal();
- break;
- default:
- Log.w(TAG, "Unknown what: " + msg.what);
- return false;
+ public boolean isWalletServiceAvailable() {
+ boolean available = mServiceInfo != null;
+ Log.i(TAG, "isWalletServiceAvailable: " + available);
+ return available;
+ }
+
+ @Override
+ public boolean isWalletFeatureAvailable() {
+ int currentUser = ActivityManager.getCurrentUser();
+ return checkUserSetupComplete()
+ && checkSecureSetting(Settings.Secure.GLOBAL_ACTIONS_PANEL_ENABLED)
+ && !new LockPatternUtils(mContext).isUserInLockdown(currentUser);
+ }
+
+ @Override
+ public boolean isWalletFeatureAvailableWhenDeviceLocked() {
+ return checkSecureSetting(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS)
+ && checkSecureSetting(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+ }
+
+ @Override
+ public void getWalletCards(
+ @NonNull GetWalletCardsRequest request,
+ @NonNull OnWalletCardsRetrievedCallback callback) {
+
+ Log.i(TAG, "getWalletCards");
+
+ if (!isWalletServiceAvailable()) {
+ callback.onWalletCardRetrievalError(new GetWalletCardsError(null, null));
+ return;
}
- return true;
+
+ BaseCallbacks serviceCallback = new BaseCallbacks() {
+ @Override
+ public void onGetWalletCardsSuccess(GetWalletCardsResponse response) {
+ mHandler.post(() -> callback.onWalletCardsRetrieved(response));
+ }
+
+ @Override
+ public void onGetWalletCardsFailure(GetWalletCardsError error) {
+ mHandler.post(() -> callback.onWalletCardRetrievalError(error));
+ }
+ };
+
+ executeApiCall(new ApiCaller("onWalletCardsRequested") {
+ @Override
+ public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
+ service.onWalletCardsRequested(request, serviceCallback);
+ }
+
+ @Override
+ public void onApiError() {
+ serviceCallback.onGetWalletCardsFailure(new GetWalletCardsError(null, null));
+ }
+ });
+ }
+
+ @Override
+ public void selectWalletCard(@NonNull SelectWalletCardRequest request) {
+ Log.i(TAG, "selectWalletCard");
+ if (!isWalletServiceAvailable()) {
+ return;
+ }
+ executeApiCall(new ApiCaller("onWalletCardSelected") {
+ @Override
+ public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
+ service.onWalletCardSelected(request);
+ }
+ });
+ }
+
+ @Override
+ public void notifyWalletDismissed() {
+ if (!isWalletServiceAvailable()) {
+ return;
+ }
+ Log.i(TAG, "notifyWalletDismissed");
+ executeApiCall(new ApiCaller("onWalletDismissed") {
+ @Override
+ public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
+ service.onWalletDismissed();
+ }
+ });
+ }
+
+ @Override
+ public void addWalletServiceEventListener(WalletServiceEventListener listener) {
+ if (!isWalletServiceAvailable()) {
+ return;
+ }
+ Log.i(TAG, "registerWalletServiceEventListener");
+ BaseCallbacks callback = new BaseCallbacks() {
+ @Override
+ public void onWalletServiceEvent(WalletServiceEvent event) {
+ Log.i(TAG, "onWalletServiceEvent");
+ mHandler.post(() -> listener.onWalletServiceEvent(event));
+ }
+ };
+
+ executeApiCall(new ApiCaller("registerListener") {
+ @Override
+ public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
+ String listenerId = UUID.randomUUID().toString();
+ WalletServiceEventListenerRequest request =
+ new WalletServiceEventListenerRequest(listenerId);
+ mEventListeners.put(listener, listenerId);
+ service.registerWalletServiceEventListener(request, callback);
+ }
+ });
+ }
+
+ @Override
+ public void removeWalletServiceEventListener(WalletServiceEventListener listener) {
+ if (!isWalletServiceAvailable()) {
+ return;
+ }
+ Log.i(TAG, "unregisterWalletServiceEventListener");
+ executeApiCall(new ApiCaller("unregisterListener") {
+ @Override
+ public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
+ String listenerId = mEventListeners.remove(listener);
+ if (listenerId == null) {
+ return;
+ }
+ WalletServiceEventListenerRequest request =
+ new WalletServiceEventListenerRequest(listenerId);
+ service.unregisterWalletServiceEventListener(request);
+ }
+ });
+ }
+
+ @Override
+ public void disconnect() {
+ Log.i(TAG, "disconnect");
+ mHandler.post(() -> disconnectInternal(true));
+ }
+
+ @Override
+ @Nullable
+ public Intent createWalletIntent() {
+ if (mServiceInfo == null || TextUtils.isEmpty(mServiceInfo.getWalletActivity())) {
+ return null;
+ }
+ return new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET)
+ .setComponent(
+ new ComponentName(
+ mServiceInfo.getComponentName().getPackageName(),
+ mServiceInfo.getWalletActivity()));
+ }
+
+ @Override
+ @Nullable
+ public Intent createWalletSettingsIntent() {
+ if (mServiceInfo == null || TextUtils.isEmpty(mServiceInfo.getSettingsActivity())) {
+ return null;
+ }
+ return new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET_SETTINGS)
+ .setComponent(
+ new ComponentName(
+ mServiceInfo.getComponentName().getPackageName(),
+ mServiceInfo.getSettingsActivity()));
}
private void connect() {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_CONNECT));
+ Log.i(TAG, "connect");
+ mHandler.post(this::connectInternal);
}
private void connectInternal() {
+ Log.i(TAG, "connectInternal");
if (mServiceInfo == null) {
Log.w(TAG, "Wallet service unavailable");
return;
@@ -113,15 +260,18 @@
intent.setComponent(mServiceInfo.getComponentName());
int flags = Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY;
mContext.bindService(intent, this, flags);
+ resetServiceConnectionTimeout();
}
private void onConnectedInternal(IQuickAccessWalletService service) {
+ Log.i(TAG, "onConnectedInternal");
if (!mIsConnected) {
Log.w(TAG, "onConnectInternal but connection closed");
mService = null;
return;
}
mService = service;
+ Log.i(TAG, "onConnectedInternal success: request queue size " + mRequestQueue.size());
for (ApiCaller apiCaller : new ArrayList<>(mRequestQueue)) {
try {
apiCaller.performApiCall(mService);
@@ -135,15 +285,33 @@
}
}
- private void disconnect() {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_DISCONNECT));
+ /**
+ * Resets the idle timeout for this connection by removing any pending timeout messages and
+ * posting a new delayed message.
+ */
+ private void resetServiceConnectionTimeout() {
+ Log.i(TAG, "resetServiceConnectionTimeout");
+ mHandler.removeMessages(MSG_TIMEOUT_SERVICE);
+ mHandler.postDelayed(
+ () -> disconnectInternal(true),
+ MSG_TIMEOUT_SERVICE,
+ SERVICE_CONNECTION_TIMEOUT_MS);
}
- private void disconnectInternal() {
+ private void disconnectInternal(boolean clearEventListeners) {
+ Log.i(TAG, "disconnectInternal: " + clearEventListeners);
if (!mIsConnected) {
Log.w(TAG, "already disconnected");
return;
}
+ if (clearEventListeners && !mEventListeners.isEmpty()) {
+ Log.i(TAG, "disconnectInternal: clear event listeners");
+ for (WalletServiceEventListener listener : mEventListeners.keySet()) {
+ removeWalletServiceEventListener(listener);
+ }
+ mHandler.post(() -> disconnectInternal(false));
+ return;
+ }
mIsConnected = false;
mContext.unbindService(/*conn=*/this);
mService = null;
@@ -151,181 +319,81 @@
mRequestQueue.clear();
}
- private void execute(ApiCaller apiCaller) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_EXECUTE, apiCaller));
+ private void executeApiCall(ApiCaller apiCaller) {
+ Log.i(TAG, "execute: " + apiCaller.mDesc);
+ mHandler.post(() -> executeInternal(apiCaller));
}
private void executeInternal(ApiCaller apiCall) {
+ Log.i(TAG, "executeInternal: " + apiCall.mDesc);
if (mIsConnected && mService != null) {
try {
apiCall.performApiCall(mService);
+ Log.i(TAG, "executeInternal success: " + apiCall.mDesc);
+ resetServiceConnectionTimeout();
} catch (RemoteException e) {
- Log.w(TAG, "executeInternal error", e);
+ Log.w(TAG, "executeInternal error: " + apiCall.mDesc, e);
apiCall.onApiError();
disconnect();
}
} else {
+ Log.i(TAG, "executeInternal: queued" + apiCall.mDesc);
mRequestQueue.add(apiCall);
connect();
}
}
- public boolean isWalletServiceAvailable() {
- return mServiceInfo != null;
- }
-
private abstract static class ApiCaller {
+ private final String mDesc;
+
+ private ApiCaller(String desc) {
+ this.mDesc = desc;
+ }
+
abstract void performApiCall(IQuickAccessWalletService service) throws RemoteException;
void onApiError() {
- Log.w(TAG, "api error");
+ Log.w(TAG, "api error: " + mDesc);
}
}
- public void getWalletCards(
- @NonNull GetWalletCardsRequest request,
- @NonNull Consumer<GetWalletCardsResponse> onSuccessListener,
- @NonNull Consumer<GetWalletCardsError> onFailureListener) {
-
- BaseCallbacks callback = new BaseCallbacks() {
- @Override
- public void onGetWalletCardsSuccess(GetWalletCardsResponse response) {
- mHandler.post(() -> onSuccessListener.accept(response));
- }
-
- @Override
- public void onGetWalletCardsFailure(GetWalletCardsError error) {
- mHandler.post(() -> onFailureListener.accept(error));
- }
- };
-
- execute(new ApiCaller() {
- @Override
- public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
- service.onWalletCardsRequested(request, callback);
- }
-
- @Override
- public void onApiError() {
- callback.onGetWalletCardsFailure(new GetWalletCardsError(null, null));
- }
- });
- }
-
- public void selectWalletCard(@NonNull SelectWalletCardRequest request) {
- execute(new ApiCaller() {
- @Override
- public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
- service.onWalletCardSelected(request);
- }
- });
- }
-
- public void notifyWalletDismissed() {
- execute(new ApiCaller() {
- @Override
- public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
- service.onWalletDismissed();
- mHandler.sendMessage(mHandler.obtainMessage(MSG_DISCONNECT));
- }
- });
- }
-
- @Override
- public void registerWalletServiceEventListener(Consumer<WalletServiceEvent> listener) {
-
- BaseCallbacks callback = new BaseCallbacks() {
- @Override
- public void onWalletServiceEvent(WalletServiceEvent event) {
- Log.i(TAG, "onWalletServiceEvent");
- mHandler.post(() -> listener.accept(event));
- }
- };
-
- execute(new ApiCaller() {
- @Override
- public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
- String listenerId = UUID.randomUUID().toString();
- WalletServiceEventListenerRequest request =
- new WalletServiceEventListenerRequest(listenerId);
- mEventListeners.put(listener, listenerId);
- service.registerWalletServiceEventListener(request, callback);
- }
- });
- }
-
- @Override
- public void unregisterWalletServiceEventListener(Consumer<WalletServiceEvent> listener) {
- execute(new ApiCaller() {
- @Override
- public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
- String listenerId = mEventListeners.get(listener);
- if (listenerId == null) {
- return;
- }
- WalletServiceEventListenerRequest request =
- new WalletServiceEventListenerRequest(listenerId);
- service.unregisterWalletServiceEventListener(request);
- }
- });
- }
-
- @Override
- @Nullable
- public Intent getWalletActivity() {
- if (mServiceInfo == null || TextUtils.isEmpty(mServiceInfo.getWalletActivity())) {
- return null;
- }
- return new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET)
- .setComponent(
- new ComponentName(
- mServiceInfo.getComponentName().getPackageName(),
- mServiceInfo.getWalletActivity()));
- }
-
- @Override
- @Nullable
- public Intent getSettingsActivity() {
- if (mServiceInfo == null || TextUtils.isEmpty(mServiceInfo.getSettingsActivity())) {
- return null;
- }
- return new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET_SETTINGS)
- .setComponent(
- new ComponentName(
- mServiceInfo.getComponentName().getPackageName(),
- mServiceInfo.getSettingsActivity()));
- }
-
- /**
- * Connection to the {@link QuickAccessWalletService}
- */
-
-
- @Override
+ @Override // ServiceConnection
public void onServiceConnected(ComponentName name, IBinder binder) {
+ Log.i(TAG, "onServiceConnected: " + name);
IQuickAccessWalletService service = IQuickAccessWalletService.Stub.asInterface(binder);
- mHandler.sendMessage(mHandler.obtainMessage(MSG_CONNECTED, service));
+ mHandler.post(() -> onConnectedInternal(service));
}
- @Override
+ @Override // ServiceConnection
public void onServiceDisconnected(ComponentName name) {
// Do not disconnect, as we may later be re-connected
Log.w(TAG, "onServiceDisconnected");
}
- @Override
+ @Override // ServiceConnection
public void onBindingDied(ComponentName name) {
// This is a recoverable error but the client will need to reconnect.
Log.w(TAG, "onBindingDied");
disconnect();
}
- @Override
+ @Override // ServiceConnection
public void onNullBinding(ComponentName name) {
Log.w(TAG, "onNullBinding");
disconnect();
}
+ private boolean checkSecureSetting(String name) {
+ return Settings.Secure.getInt(mContext.getContentResolver(), name, 0) == 1;
+ }
+
+ private boolean checkUserSetupComplete() {
+ return Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 0,
+ UserHandle.USER_CURRENT) == 1;
+ }
+
private static class BaseCallbacks extends IQuickAccessWalletServiceCallbacks.Stub {
public void onGetWalletCardsSuccess(GetWalletCardsResponse response) {
throw new IllegalStateException();
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
index d968405..aba5611 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
@@ -45,8 +45,9 @@
* android.permission.BIND_NFC_SERVICE permission.
* <li>The user explicitly selected the application as the default payment application in
* the Tap & pay settings screen.
- * <li>The application requires the {@code android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE}
- * permission in its manifest.
+ * <li>The QuickAccessWalletService requires that the binding application hold the
+ * {@code android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE} permission, which only the System
+ * Service can hold.
* <li>The user explicitly enables it using Android Settings (the
* {@link Settings#ACTION_QUICK_ACCESS_WALLET_SETTINGS} intent can be used to launch it).
* </ol>
@@ -73,6 +74,11 @@
* a new card is selected, the Android System will notify the service through
* {@link #onWalletCardSelected} and will provide the {@link WalletCard#getCardId() cardId} of the
* card that is now selected.
+ * <li>If the user commences an NFC payment, the service may send a {@link WalletServiceEvent}
+ * to the System indicating that the wallet application now needs to show the activity associated
+ * with making a payment. Sending a {@link WalletServiceEvent} of type
+ * {@link WalletServiceEvent#TYPE_NFC_PAYMENT_STARTED} should cause the quick access wallet UI
+ * to be dismissed.
* <li>When the wallet is dismissed, the Android System will notify the service through
* {@link #onWalletDismissed}.
* </ol>
@@ -110,6 +116,7 @@
* android:permission="android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE">
* <intent-filter>
* <action android:name="android.service.quickaccesswallet.QuickAccessWalletService" />
+ * <category android:name="android.intent.category.DEFAULT"/>
* </intent-filter>
* <meta-data android:name="android.quickaccesswallet"
* android:resource="@xml/quickaccesswallet_configuration" />;
@@ -166,29 +173,6 @@
"android.service.quickaccesswallet.action.VIEW_WALLET_SETTINGS";
/**
- * Broadcast Action: Sent by the wallet application to dismiss the Quick Access Wallet.
- * <p>
- * The Quick Access Wallet may be shown in a system window on top of other Activities. If the
- * user selects a payment card from the Quick Access Wallet and then holds their phone to an NFC
- * terminal, the wallet application will need to show a payment Activity. But if the Quick
- * Access Wallet is still being shown, it may obscure the payment Activity. To avoid this, the
- * wallet application can send a broadcast to the Android System with this action to request
- * that the Quick Access Wallet be dismissed.
- * <p>
- * This broadcast must use the {@code android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE}
- * permission to ensure that it is only delivered to System UI. Furthermore, your application
- * must require the {@code android.permission.DISMISS_QUICK_ACCESS_WALLET}
- * <p>
- * <pre class="prettyprint">
- * context.sendBroadcast(
- * new Intent(ACTION_DISMISS_WALLET), Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE);
- * </pre>
- */
- @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_DISMISS_WALLET =
- "android.service.quickaccesswallet.action.DISMISS_WALLET";
-
- /**
* Name under which a QuickAccessWalletService component publishes information about itself.
* This meta-data should reference an XML resource containing a
* <code><{@link
@@ -202,8 +186,20 @@
public static final String SERVICE_META_DATA = "android.quickaccesswallet";
private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+ /**
+ * The service currently only supports one listener at a time. Multiple connections that
+ * register different listeners will clobber the listener. This field may only be accessed from
+ * the main thread.
+ */
@Nullable
private String mEventListenerId;
+
+ /**
+ * The service currently only supports one listener at a time. Multiple connections that
+ * register different listeners will clobber the listener. This field may only be accessed from
+ * the main thread.
+ */
@Nullable
private IQuickAccessWalletServiceCallbacks mEventListener;
@@ -240,7 +236,8 @@
private void onWalletCardsRequestedInternal(
GetWalletCardsRequest request,
IQuickAccessWalletServiceCallbacks callback) {
- onWalletCardsRequested(request, new GetWalletCardsCallback(callback, mHandler));
+ onWalletCardsRequested(request,
+ new GetWalletCardsCallbackImpl(request, callback, mHandler));
}
@Override
@@ -250,14 +247,14 @@
// Binding to the QuickAccessWalletService is protected by the
// android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE permission, which is defined in
// R. Pre-R devices can have other side-loaded applications that claim this permission.
- // This ensures that the service is only available when properly permission protected.
+ // Ensures that the service is only enabled when properly permission protected.
Log.w(TAG, "Warning: binding on pre-R device");
}
- if (SERVICE_INTERFACE.equals(intent.getAction())) {
- return mInterface.asBinder();
+ if (!SERVICE_INTERFACE.equals(intent.getAction())) {
+ Log.w(TAG, "Wrong action");
+ return null;
}
- Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
- return null;
+ return mInterface.asBinder();
}
/**
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
index 8793f28..23173a8 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
@@ -77,8 +77,9 @@
}
if (!Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE.equals(serviceInfo.permission)) {
- Log.w(TAG, String.format("QuickAccessWalletService from %s does not have permission %s",
- serviceInfo.packageName, Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE));
+ Log.w(TAG, String.format("%s.%s does not require permission %s",
+ serviceInfo.packageName, serviceInfo.name,
+ Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE));
return null;
}
diff --git a/core/java/android/service/quickaccesswallet/WalletCard.java b/core/java/android/service/quickaccesswallet/WalletCard.java
index c3b1a4b..e6ae0ab 100644
--- a/core/java/android/service/quickaccesswallet/WalletCard.java
+++ b/core/java/android/service/quickaccesswallet/WalletCard.java
@@ -174,22 +174,25 @@
* @param cardId The card id must be non-null and unique within the list of
* cards returned. <b>Note:
* </b> this card ID should <b>not</b> contain PII (Personally
- * Identifiable Information, * such as username or email
- * address).
+ * Identifiable Information, such as username or email address).
* @param cardImage The visual representation of the card. If the card image Icon
* is a bitmap, it should have a width of {@link
* GetWalletCardsRequest#getCardWidthPx()} and a height of {@link
* GetWalletCardsRequest#getCardHeightPx()}. If the card image
* does not have these dimensions, it may appear distorted when it
- * is scaled to fit these dimensions on screen.
+ * is scaled to fit these dimensions on screen. Bitmaps should be
+ * of type {@link android.graphics.Bitmap.Config#HARDWARE} for
+ * performance reasons.
* @param contentDescription The content description of the card image. This field is
- * required.
+ * required and may not be null or empty.
* <b>Note: </b> this message should <b>not</b> contain PII
* (Personally Identifiable Information, such as username or email
* address).
* @param pendingIntent If the user performs a click on the card, this PendingIntent
* will be sent. If the device is locked, the wallet will first
- * request device unlock before sending the pending intent.
+ * request device unlock before sending the pending intent. It is
+ * recommended that the pending intent be immutable (use {@link
+ * PendingIntent#FLAG_IMMUTABLE}).
*/
public Builder(@NonNull String cardId,
@NonNull Icon cardImage,
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b971a20..688517d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1705,12 +1705,13 @@
}
Surface getOrCreateBLASTSurface(int width, int height) {
- if (mSurfaceControl == null || !mSurfaceControl.isValid()) {
+ if (mSurfaceControl == null
+ || !mSurfaceControl.isValid()
+ || mBlastSurfaceControl == null
+ || !mBlastSurfaceControl.isValid()) {
return null;
}
- if ((mBlastSurfaceControl != null)
- && (mBlastBufferQueue == null)
- && mBlastSurfaceControl.isValid()) {
+ if (mBlastBufferQueue == null) {
mBlastBufferQueue = new BLASTBufferQueue(
mBlastSurfaceControl, width, height);
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index e9a8a73..54bdb88 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -3366,14 +3366,8 @@
final AutofillManager afm = mAfm.get();
if (afm == null) return null;
- final AutofillClient client = afm.getClient();
- if (client == null) {
- Log.w(TAG, "getViewCoordinates(" + id + "): no autofill client");
- return null;
- }
- final View view = client.autofillClientFindViewByAutofillIdTraversal(id);
+ final View view = getView(afm, id);
if (view == null) {
- Log.w(TAG, "getViewCoordinates(" + id + "): could not find view");
return null;
}
final Rect windowVisibleDisplayFrame = new Rect();
@@ -3414,5 +3408,42 @@
afm.post(() -> afm.requestHideFillUi(id, false));
}
}
+
+ @Override
+ public boolean requestAutofill(int sessionId, AutofillId id) {
+ final AutofillManager afm = mAfm.get();
+ if (afm == null || afm.mSessionId != sessionId) {
+ if (sDebug) {
+ Slog.d(TAG, "Autofill not available or sessionId doesn't match");
+ }
+ return false;
+ }
+ final View view = getView(afm, id);
+ if (view == null || !view.isFocused()) {
+ if (sDebug) {
+ Slog.d(TAG, "View not available or is not on focus");
+ }
+ return false;
+ }
+ if (sVerbose) {
+ Log.v(TAG, "requestAutofill() by AugmentedAutofillService.");
+ }
+ afm.post(() -> afm.requestAutofill(view));
+ return true;
+ }
+
+ @Nullable
+ private View getView(@NonNull AutofillManager afm, @NonNull AutofillId id) {
+ final AutofillClient client = afm.getClient();
+ if (client == null) {
+ Log.w(TAG, "getView(" + id + "): no autofill client");
+ return null;
+ }
+ View view = client.autofillClientFindViewByAutofillIdTraversal(id);
+ if (view == null) {
+ Log.w(TAG, "getView(" + id + "): could not find view");
+ }
+ return view;
+ }
}
}
diff --git a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
index 140507c..03054df 100644
--- a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
@@ -50,4 +50,10 @@
* Requests hiding the fill UI.
*/
void requestHideFillUi(int sessionId, in AutofillId id);
+
+ /**
+ * Requests to start a new autofill flow. Returns true if the autofill request is made to
+ * {@link AutofillManager#requestAutofill(View)}.
+ */
+ boolean requestAutofill(int sessionId, in AutofillId id);
}
diff --git a/core/res/res/layout/autofill_inline_suggestion.xml b/core/res/res/layout/autofill_inline_suggestion.xml
deleted file mode 100644
index 27faea4..0000000
--- a/core/res/res/layout/autofill_inline_suggestion.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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"
- style="?android:attr/autofillInlineSuggestionChip"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:paddingTop="4dp"
- android:paddingBottom="4dp"
- android:orientation="horizontal">
-
- <ImageView
- android:id="@+id/autofill_inline_suggestion_start_icon"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:scaleType="fitCenter"
- android:contentDescription="autofill_inline_suggestion_start_icon" />
-
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:layout_weight="1"
- android:paddingStart="4dp"
- android:paddingEnd="4dp"
- android:orientation="vertical"
- android:gravity="center">
-
- <TextView
- style="?android:attr/autofillInlineSuggestionTitle"
- android:id="@+id/autofill_inline_suggestion_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:maxLines="1"/>
-
- <TextView
- style="?android:attr/autofillInlineSuggestionSubtitle"
- android:id="@+id/autofill_inline_suggestion_subtitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:maxLines="1"/>
- </LinearLayout>
-
- <ImageView
- android:id="@+id/autofill_inline_suggestion_end_icon"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:scaleType="fitCenter"
- android:contentDescription="autofill_inline_suggestion_end_icon" />
-</LinearLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 7fc041c..9cdd3ed 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -9207,12 +9207,4 @@
</declare-styleable>
<attr name="autoSizePresetSizes" />
-
- <declare-styleable name="AutofillInlineSuggestion">
- <!-- @hide @SystemApi -->
- <attr name="isAutofillInlineSuggestionTheme" format="boolean" />
- <attr name="autofillInlineSuggestionChip" format="reference" />
- <attr name="autofillInlineSuggestionTitle" format="reference" />
- <attr name="autofillInlineSuggestionSubtitle" format="reference" />
- </declare-styleable>
</resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index e231c3a..16bed503 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1770,6 +1770,21 @@
-->
<attr name="requestLegacyExternalStorage" format="boolean" />
+ <!-- If {@code true} this app would like to preserve the legacy storage
+ model from a previously installed version. Note that this may not always be
+ respected due to policy or backwards compatibility reasons.
+
+ <p>This has no effect on the first install of an app on a device.
+ For an updating app, setting this to {@code true} will preserve the legacy behaviour
+ configured by the {@code requestLegacyExternalStorage} flag. If on an update, this
+ flag is set to {@code false} then the legacy access is not preserved, such an app can
+ only have legacy access with the {@code requestLegacyExternalStorage} flag.
+ <p>
+
+ The default value is {@code false}.
+ -->
+ <attr name="preserveLegacyExternalStorage" format="boolean" />
+
<!-- If {@code true} this app declares that it should be visible to all other apps on
device, regardless of what they declare via the {@code queries} tags in their
manifest.
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 0edad3b..80e5d1b 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3012,19 +3012,14 @@
<public name="sdkVersion" />
<!-- @hide @SystemApi -->
<public name="minExtensionVersion" />
- <public name="autofillInlineSuggestionChip" />
- <public name="autofillInlineSuggestionTitle" />
- <public name="autofillInlineSuggestionSubtitle" />
- <!-- @hide @SystemApi -->
- <public name="isAutofillInlineSuggestionTheme" />
<public name="allowNativeHeapPointerTagging" />
+ <public name="preserveLegacyExternalStorage" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
</public-group>
<public-group type="style" first-id="0x010302e5">
- <public name="Theme.AutofillInlineSuggestion" />
</public-group>
<public-group type="id" first-id="0x0102004a">
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 751eca0..bcce1f0 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1482,22 +1482,6 @@
<item name="android:windowExitAnimation">@anim/slide_out_down</item>
</style>
- <!-- The style for the Autofill inline suggestion chip. -->
- <!-- @hide -->
- <style name="AutofillInlineSuggestionChip">
- <item name="background">@drawable/autofill_dataset_picker_background</item>
- </style>
-
- <!-- @hide -->
- <style name="AutofillInlineSuggestionTitle">
- <item name="android:textAppearance">@style/TextAppearance</item>
- </style>
-
- <!-- @hide -->
- <style name="AutofillInlineSuggestionSubtitle">
- <item name="android:textAppearance">@style/TextAppearance.Small</item>
- </style>
-
<!-- The style for the container of media actions in a notification. -->
<!-- @hide -->
<style name="NotificationMediaActionContainer">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 500bfe9..02d90a7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3261,7 +3261,6 @@
<java-symbol type="layout" name="autofill_dataset_picker"/>
<java-symbol type="layout" name="autofill_dataset_picker_fullscreen"/>
<java-symbol type="layout" name="autofill_dataset_picker_header_footer"/>
- <java-symbol type="layout" name="autofill_inline_suggestion" />
<java-symbol type="id" name="autofill" />
<java-symbol type="id" name="autofill_dataset_footer"/>
<java-symbol type="id" name="autofill_dataset_header"/>
@@ -3269,10 +3268,6 @@
<java-symbol type="id" name="autofill_dataset_list"/>
<java-symbol type="id" name="autofill_dataset_picker"/>
<java-symbol type="id" name="autofill_dataset_title" />
- <java-symbol type="id" name="autofill_inline_suggestion_end_icon" />
- <java-symbol type="id" name="autofill_inline_suggestion_start_icon" />
- <java-symbol type="id" name="autofill_inline_suggestion_subtitle" />
- <java-symbol type="id" name="autofill_inline_suggestion_title" />
<java-symbol type="id" name="autofill_save_custom_subtitle" />
<java-symbol type="id" name="autofill_save_icon" />
<java-symbol type="id" name="autofill_save_no" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 5e6dd82..5d9cb48 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -892,12 +892,4 @@
<item name="windowContentTransitions">false</item>
<item name="windowActivityTransitions">false</item>
</style>
-
- <!-- Theme for the Autofill inline suggestion on IME -->
- <style name="Theme.AutofillInlineSuggestion" parent="Theme.DeviceDefault">
- <item name="isAutofillInlineSuggestionTheme">true</item>
- <item name="autofillInlineSuggestionChip">@style/AutofillInlineSuggestionChip</item>
- <item name="autofillInlineSuggestionTitle">@style/AutofillInlineSuggestionTitle</item>
- <item name="autofillInlineSuggestionSubtitle">@style/AutofillInlineSuggestionSubtitle</item>
- </style>
</resources>
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
index f54808d..82af658 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -38,6 +38,14 @@
* Invalid stream ID.
*/
public static final int INVALID_STREAM_ID = Constants.Constant.INVALID_STREAM_ID;
+ /**
+ * Invalid filter ID.
+ */
+ public static final int INVALID_FILTER_ID = Constants.Constant.INVALID_FILTER_ID;
+ /**
+ * Invalid AV Sync ID.
+ */
+ public static final int INVALID_AV_SYNC_ID = Constants.Constant.INVALID_AV_SYNC_ID;
/** @hide */
@IntDef(prefix = "SCAN_TYPE_", value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_AUTO, SCAN_TYPE_BLIND})
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 1e39954..cdeb5c3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -38,9 +38,11 @@
import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.io.FileDescriptor;
@@ -50,7 +52,7 @@
/**
* Encapsulates the data and UI elements of a bubble.
*/
-class Bubble {
+class Bubble implements BubbleViewProvider {
private static final String TAG = "Bubble";
private NotificationEntry mEntry;
@@ -148,12 +150,12 @@
}
@Nullable
- BadgedImageView getIconView() {
+ public BadgedImageView getIconView() {
return mIconView;
}
@Nullable
- BubbleExpandedView getExpandedView() {
+ public BubbleExpandedView getExpandedView() {
return mExpandedView;
}
@@ -238,7 +240,7 @@
* Note that this contents visibility doesn't affect visibility at {@link android.view.View},
* and setting {@code false} actually means rendering the expanded view in transparent.
*/
- void setContentVisibility(boolean visibility) {
+ public void setContentVisibility(boolean visibility) {
if (mExpandedView != null) {
mExpandedView.setContentVisibility(visibility);
}
@@ -481,4 +483,36 @@
public int hashCode() {
return Objects.hash(mKey);
}
+
+ public void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index) {
+ if (this.getEntry() == null
+ || this.getEntry().getSbn() == null) {
+ SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
+ null /* package name */,
+ null /* notification channel */,
+ 0 /* notification ID */,
+ 0 /* bubble position */,
+ bubbleCount,
+ action,
+ normalX,
+ normalY,
+ false /* unread bubble */,
+ false /* on-going bubble */,
+ false /* isAppForeground (unused) */);
+ } else {
+ StatusBarNotification notification = this.getEntry().getSbn();
+ SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
+ notification.getPackageName(),
+ notification.getNotification().getChannelId(),
+ notification.getId(),
+ index,
+ bubbleCount,
+ action,
+ normalX,
+ normalY,
+ this.showInShade(),
+ this.isOngoing(),
+ false /* isAppForeground (unused) */);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
new file mode 100644
index 0000000..6cf1086
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles;
+
+import static android.view.View.GONE;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.InsetDrawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+
+/**
+ * Class for showing aged out bubbles.
+ */
+public class BubbleOverflow implements BubbleViewProvider {
+
+ private ImageView mOverflowBtn;
+ private BubbleExpandedView mOverflowExpandedView;
+ private LayoutInflater mInflater;
+ private Context mContext;
+
+ public BubbleOverflow(Context context) {
+ mContext = context;
+ mInflater = LayoutInflater.from(context);
+ }
+
+ public void setUpOverflow(ViewGroup parentViewGroup) {
+ mOverflowExpandedView = (BubbleExpandedView) mInflater.inflate(
+ R.layout.bubble_expanded_view, parentViewGroup /* root */,
+ false /* attachToRoot */);
+ mOverflowExpandedView.setOverflow(true);
+
+ mOverflowBtn = (ImageView) mInflater.inflate(R.layout.bubble_overflow_button,
+ parentViewGroup /* root */,
+ false /* attachToRoot */);
+
+ setOverflowBtnTheme();
+ mOverflowBtn.setVisibility(GONE);
+ }
+
+ ImageView getBtn() {
+ return mOverflowBtn;
+ }
+
+ void setBtnVisible(int visible) {
+ mOverflowBtn.setVisibility(visible);
+ }
+
+ // TODO(b/149146374) Propagate theme change to bubbles in overflow.
+ void setOverflowBtnTheme() {
+ TypedArray ta = mContext.obtainStyledAttributes(
+ new int[]{android.R.attr.colorBackgroundFloating});
+ int bgColor = ta.getColor(0, Color.WHITE /* default */);
+ ta.recycle();
+
+ InsetDrawable fg = new InsetDrawable(mOverflowBtn.getDrawable(), 28);
+ ColorDrawable bg = new ColorDrawable(bgColor);
+ AdaptiveIconDrawable adaptiveIcon = new AdaptiveIconDrawable(bg, fg);
+ mOverflowBtn.setImageDrawable(adaptiveIcon);
+ }
+
+
+ public BubbleExpandedView getExpandedView() {
+ return mOverflowExpandedView;
+ }
+
+ public void setContentVisibility(boolean visible) {
+ mOverflowExpandedView.setContentVisibility(visible);
+ }
+
+ public void logUIEvent(int bubbleCount, int action, float normalX, float normalY,
+ int index) {
+ // TODO(b/149133814) Log overflow UI events.
+ }
+
+ public View getIconView() {
+ return mOverflowBtn;
+ }
+
+ public String getKey() {
+ return BubbleOverflowActivity.KEY;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index f3cfa83..756c598 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -47,6 +47,7 @@
* Must be public to be accessible to androidx...AppComponentFactory
*/
public class BubbleOverflowActivity extends Activity {
+ public static final String KEY = "Overflow";
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleOverflowActivity" : TAG_BUBBLES;
private LinearLayout mEmptyState;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index cff78cf..9a6295a 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -33,8 +33,6 @@
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
@@ -42,13 +40,9 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.InsetDrawable;
import android.os.Bundle;
import android.os.VibrationEffect;
import android.os.Vibrator;
-import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.view.Choreographer;
import android.view.DisplayCutout;
@@ -63,7 +57,6 @@
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
-import android.widget.ImageView;
import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
@@ -199,7 +192,7 @@
private int mPointerHeight;
private int mStatusBarHeight;
private int mImeOffset;
- private Bubble mExpandedBubble;
+ private BubbleViewProvider mExpandedBubble;
private boolean mIsExpanded;
/** Whether the stack is currently on the left side of the screen, or animating there. */
@@ -322,8 +315,8 @@
private Runnable mAfterMagnet;
private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
- private BubbleExpandedView mOverflowExpandedView;
- private ImageView mOverflowBtn;
+
+ private BubbleOverflow mBubbleOverflow;
public BubbleStackView(Context context, BubbleData data,
@Nullable SurfaceSynchronizer synchronizer) {
@@ -333,7 +326,6 @@
mInflater = LayoutInflater.from(context);
mTouchHandler = new BubbleTouchHandler(this, data, context);
setOnTouchListener(mTouchHandler);
- mInflater = LayoutInflater.from(context);
Resources res = getResources();
mMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
@@ -407,12 +399,8 @@
.setStiffness(SpringForce.STIFFNESS_LOW)
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
mExpandedViewYAnim.addEndListener((anim, cancelled, value, velocity) -> {
- if (mIsExpanded) {
- if (mExpandedBubble == null) {
- mOverflowExpandedView.updateView();
- } else {
- mExpandedBubble.getExpandedView().updateView();
- }
+ if (mIsExpanded && mExpandedBubble != null) {
+ mExpandedBubble.getExpandedView().updateView();
}
});
@@ -420,8 +408,12 @@
setFocusable(true);
mBubbleContainer.bringToFront();
+ mBubbleOverflow = new BubbleOverflow(mContext);
if (BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
- setUpOverflow();
+ mBubbleOverflow.setUpOverflow(this);
+ mBubbleContainer.addView(mBubbleOverflow.getBtn(), 0,
+ new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+
}
setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
@@ -432,9 +424,7 @@
// Update the insets after we're done translating otherwise position
// calculation for them won't be correct.
() -> {
- if (mExpandedBubble == null) {
- mOverflowExpandedView.updateInsets(insets);
- } else {
+ if (mExpandedBubble != null) {
mExpandedBubble.getExpandedView().updateInsets(insets);
}
});
@@ -449,9 +439,7 @@
// Reposition & adjust the height for new orientation
if (mIsExpanded) {
mExpandedViewContainer.setTranslationY(getExpandedViewY());
- if (mExpandedBubble == null) {
- mOverflowExpandedView.updateView();
- } else {
+ if (mExpandedBubble != null) {
mExpandedBubble.getExpandedView().updateView();
}
}
@@ -516,40 +504,8 @@
});
}
- private void setUpOverflow() {
- mOverflowExpandedView = (BubbleExpandedView) mInflater.inflate(
- R.layout.bubble_expanded_view, this /* root */, false /* attachToRoot */);
- mOverflowExpandedView.setOverflow(true);
-
- mOverflowBtn = (ImageView) mInflater.inflate(R.layout.bubble_overflow_button,
- this /* root */,
- false /* attachToRoot */);
-
- mBubbleContainer.addView(mOverflowBtn, 0,
- new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
-
- setOverflowBtnTheme();
- mOverflowBtn.setVisibility(GONE);
- }
-
- // TODO(b/149146374) Propagate theme change to bubbles in overflow.
- private void setOverflowBtnTheme() {
- TypedArray ta = mContext.obtainStyledAttributes(
- new int[]{android.R.attr.colorBackgroundFloating});
- int bgColor = ta.getColor(0, Color.WHITE /* default */);
- ta.recycle();
-
- InsetDrawable fg = new InsetDrawable(mOverflowBtn.getDrawable(), 28);
- ColorDrawable bg = new ColorDrawable(bgColor);
- AdaptiveIconDrawable adaptiveIcon = new AdaptiveIconDrawable(bg, fg);
- mOverflowBtn.setImageDrawable(adaptiveIcon);
- }
-
void showExpandedViewContents(int displayId) {
- if (mOverflowExpandedView != null
- && mOverflowExpandedView.getVirtualDisplayId() == displayId) {
- mOverflowExpandedView.setContentVisibility(true);
- } else if (mExpandedBubble != null
+ if (mExpandedBubble != null
&& mExpandedBubble.getExpandedView().getVirtualDisplayId() == displayId) {
mExpandedBubble.setContentVisibility(true);
}
@@ -573,7 +529,7 @@
public void onThemeChanged() {
setUpFlyout();
if (BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
- setOverflowBtnTheme();
+ mBubbleOverflow.setOverflowBtnTheme();
}
}
@@ -756,15 +712,22 @@
/**
* The {@link BadgedImageView} that is expanded, null if one does not exist.
*/
- BadgedImageView getExpandedBubbleView() {
+ View getExpandedBubbleView() {
return mExpandedBubble != null ? mExpandedBubble.getIconView() : null;
}
/**
* The {@link Bubble} that is expanded, null if one does not exist.
*/
+ @Nullable
Bubble getExpandedBubble() {
- return mExpandedBubble;
+ if (mExpandedBubble == null
+ || (BubbleExperimentConfig.allowBubbleOverflow(mContext)
+ && mExpandedBubble.getIconView() == mBubbleOverflow.getBtn()
+ && mExpandedBubble.getKey() == BubbleOverflowActivity.KEY)) {
+ return null;
+ }
+ return (Bubble) mExpandedBubble;
}
// via BubbleData.Listener
@@ -818,7 +781,7 @@
if (DEBUG_BUBBLE_STACK_VIEW) {
Log.d(TAG, "Show overflow button.");
}
- mOverflowBtn.setVisibility(VISIBLE);
+ mBubbleOverflow.setBtnVisible(VISIBLE);
if (apply) {
mExpandedAnimationController.expandFromStack(() -> {
updatePointerPosition();
@@ -828,7 +791,7 @@
if (DEBUG_BUBBLE_STACK_VIEW) {
Log.d(TAG, "Collapsed. Hide overflow button.");
}
- mOverflowBtn.setVisibility(GONE);
+ mBubbleOverflow.setBtnVisible(GONE);
}
}
@@ -849,7 +812,7 @@
}
void showOverflow() {
- setSelectedBubble(null);
+ setSelectedBubble(mBubbleOverflow);
}
/**
@@ -858,14 +821,14 @@
* position of any bubble.
*/
// via BubbleData.Listener
- public void setSelectedBubble(@Nullable Bubble bubbleToSelect) {
+ public void setSelectedBubble(@Nullable BubbleViewProvider bubbleToSelect) {
if (DEBUG_BUBBLE_STACK_VIEW) {
Log.d(TAG, "setSelectedBubble: " + bubbleToSelect);
}
if (mExpandedBubble != null && mExpandedBubble.equals(bubbleToSelect)) {
return;
}
- final Bubble previouslySelected = mExpandedBubble;
+ final BubbleViewProvider previouslySelected = mExpandedBubble;
mExpandedBubble = bubbleToSelect;
if (mIsExpanded) {
@@ -874,14 +837,11 @@
// expanded view becomes visible on the screen. See b/126856255
mExpandedViewContainer.setAlpha(0.0f);
mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
- if (previouslySelected == null) {
- mOverflowExpandedView.setContentVisibility(false);
- } else {
- previouslySelected.setContentVisibility(false);
- }
+ previouslySelected.setContentVisibility(false);
updateExpandedBubble();
updatePointerPosition();
requestUpdate();
+
logBubbleEvent(previouslySelected,
SysUiStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
logBubbleEvent(bubbleToSelect, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
@@ -941,8 +901,8 @@
if (mIsExpanded) {
if (isIntersecting(mBubbleContainer, x, y)) {
if (BubbleExperimentConfig.allowBubbleOverflow(mContext)
- && isIntersecting(mOverflowBtn, x, y)) {
- return mOverflowBtn;
+ && isIntersecting(mBubbleOverflow.getBtn(), x, y)) {
+ return mBubbleOverflow.getBtn();
}
// Could be tapping or dragging a bubble while expanded
for (int i = 0; i < getBubbleCount(); i++) {
@@ -1030,7 +990,7 @@
private void animateCollapse() {
mIsExpanded = false;
- final Bubble previouslySelected = mExpandedBubble;
+ final BubbleViewProvider previouslySelected = mExpandedBubble;
beforeExpandedViewAnimation();
if (DEBUG_BUBBLE_STACK_VIEW) {
@@ -1046,11 +1006,7 @@
() -> {
mBubbleContainer.setActiveController(mStackAnimationController);
afterExpandedViewAnimation();
- if (previouslySelected == null) {
- mOverflowExpandedView.setContentVisibility(false);
- } else {
- previouslySelected.setContentVisibility(false);
- }
+ previouslySelected.setContentVisibility(false);
});
mExpandedViewXAnim.animateToFinalPosition(getCollapsedX());
@@ -1093,7 +1049,7 @@
mExpandedAnimateYDistance);
}
- private void notifyExpansionChanged(Bubble bubble, boolean expanded) {
+ private void notifyExpansionChanged(BubbleViewProvider bubble, boolean expanded) {
if (mExpandListener != null && bubble != null) {
mExpandListener.onBubbleExpandChanged(expanded, bubble.getKey());
}
@@ -1613,11 +1569,8 @@
Log.d(TAG, "updateExpandedBubble()");
}
mExpandedViewContainer.removeAllViews();
- if (mIsExpanded) {
- BubbleExpandedView bev = mOverflowExpandedView;
- if (mExpandedBubble != null) {
- bev = mExpandedBubble.getExpandedView();
- }
+ if (mIsExpanded && mExpandedBubble != null) {
+ BubbleExpandedView bev = mExpandedBubble.getExpandedView();
mExpandedViewContainer.addView(bev);
bev.populateExpandedView();
mExpandedViewContainer.setVisibility(VISIBLE);
@@ -1636,9 +1589,7 @@
if (!mExpandedViewYAnim.isRunning()) {
// We're not animating so set the value
mExpandedViewContainer.setTranslationY(y);
- if (mExpandedBubble == null) {
- mOverflowExpandedView.updateView();
- } else {
+ if (mExpandedBubble != null) {
mExpandedBubble.getExpandedView().updateView();
}
} else {
@@ -1693,15 +1644,16 @@
/**
* Finds the bubble index within the stack.
*
- * @param bubble the bubble to look up.
+ * @param provider the bubble view provider with the bubble to look up.
* @return the index of the bubble view within the bubble stack. The range of the position
* is between 0 and the bubble count minus 1.
*/
- int getBubbleIndex(@Nullable Bubble bubble) {
- if (bubble == null) {
+ int getBubbleIndex(@Nullable BubbleViewProvider provider) {
+ if (provider == null || provider.getKey() == BubbleOverflowActivity.KEY) {
return 0;
}
- return mBubbleContainer.indexOfChild(bubble.getIconView());
+ Bubble b = (Bubble) provider;
+ return mBubbleContainer.indexOfChild(b.getIconView());
}
/**
@@ -1733,36 +1685,12 @@
* the user interaction is not specific to one bubble.
* @param action the user interaction enum.
*/
- private void logBubbleEvent(@Nullable Bubble bubble, int action) {
- if (bubble == null || bubble.getEntry() == null
- || bubble.getEntry().getSbn() == null) {
- SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
- null /* package name */,
- null /* notification channel */,
- 0 /* notification ID */,
- 0 /* bubble position */,
- getBubbleCount(),
- action,
- getNormalizedXPosition(),
- getNormalizedYPosition(),
- false /* unread bubble */,
- false /* on-going bubble */,
- false /* isAppForeground (unused) */);
- } else {
- StatusBarNotification notification = bubble.getEntry().getSbn();
- SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
- notification.getPackageName(),
- notification.getNotification().getChannelId(),
- notification.getId(),
- getBubbleIndex(bubble),
- getBubbleCount(),
- action,
- getNormalizedXPosition(),
- getNormalizedYPosition(),
- bubble.showInShade(),
- bubble.isOngoing(),
- false /* isAppForeground (unused) */);
+ private void logBubbleEvent(@Nullable BubbleViewProvider bubble, int action) {
+ if (bubble == null) {
+ return;
}
+ bubble.logUIEvent(getBubbleCount(), action, getNormalizedXPosition(),
+ getNormalizedYPosition(), getBubbleIndex(bubble));
}
/**
@@ -1770,14 +1698,10 @@
* a back key down/up event pair is forwarded to the bubble Activity.
*/
boolean performBackPressIfNeeded() {
- if (!isExpanded()) {
+ if (!isExpanded() || mExpandedBubble == null) {
return false;
}
- if (mExpandedBubble == null) {
- return mOverflowExpandedView.performBackPressIfNeeded();
- } else {
- return mExpandedBubble.getExpandedView().performBackPressIfNeeded();
- }
+ return mExpandedBubble.getExpandedView().performBackPressIfNeeded();
}
/** For debugging only */
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
new file mode 100644
index 0000000..59fc435
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles;
+
+import android.view.View;
+
+/**
+ * Interface to represent actual Bubbles and UI elements that act like bubbles, like BubbleOverflow.
+ */
+interface BubbleViewProvider {
+ BubbleExpandedView getExpandedView();
+ void setContentVisibility(boolean visible);
+ View getIconView();
+ void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index);
+ String getKey();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
index e09d20b..9ec71c7 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -86,7 +86,7 @@
private const val MSG_ACTION = 2
private const val MSG_UNBIND = 3
private const val BIND_RETRY_DELAY = 1000L // ms
- private const val LOAD_TIMEOUT = 5000L // ms
+ private const val LOAD_TIMEOUT_SECONDS = 30L // seconds
private const val MAX_BIND_RETRIES = 5
private const val MAX_CONTROLS_REQUEST = 100000L
private const val DEBUG = true
@@ -231,7 +231,7 @@
// Don't accept load callbacks after this
lastLoadCallback = null
unbindService()
- }, LOAD_TIMEOUT, TimeUnit.MILLISECONDS)
+ }, LOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS)
invokeOrQueue(::load, Message.Load)
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt
index 44f3fbc..275c778 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt
@@ -16,6 +16,20 @@
package com.android.systemui.controls.ui
+/**
+ * All controls need to respond to changes in state and handle user-generated events.
+ * Implementations of this interface provide these different means by adding their own
+ * event handlers, and will update the control ui as they see fit.
+ */
interface Behavior {
- fun apply(cvh: ControlViewHolder, cws: ControlWithState)
+
+ /**
+ * Only called once per instance
+ */
+ fun initialize(cvh: ControlViewHolder)
+
+ /**
+ * Will be invoked on every update provided to the Control
+ */
+ fun bind(cws: ControlWithState)
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index aec20751..feaea7c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -37,6 +37,8 @@
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.R
+import kotlin.reflect.KClass
+
private const val UPDATE_DELAY_IN_MILLIS = 3000L
class ControlViewHolder(
@@ -54,6 +56,7 @@
val clipLayer: ClipDrawable
lateinit var cws: ControlWithState
var cancelUpdate: Runnable? = null
+ var behavior: Behavior? = null
init {
val ld = layout.getBackground() as LayerDrawable
@@ -83,7 +86,14 @@
})
}
- findBehavior(status, template).apply(this, cws)
+ val clazz = findBehavior(status, template)
+ if (behavior == null || behavior!!::class != clazz) {
+ // Behavior changes can signal a change in template from the app or
+ // first time setup
+ behavior = clazz.java.newInstance()
+ behavior?.initialize(this)
+ }
+ behavior?.bind(cws)
}
fun actionResponse(@ControlAction.ResponseResult response: Int) {
@@ -115,21 +125,14 @@
controlsController.action(cws.ci, action)
}
- private fun findBehavior(status: Int, template: ControlTemplate): Behavior {
+ private fun findBehavior(status: Int, template: ControlTemplate): KClass<out Behavior> {
return when {
- status == Control.STATUS_UNKNOWN -> UnknownBehavior()
- template is ToggleTemplate -> ToggleBehavior()
- template is ToggleRangeTemplate -> ToggleRangeBehavior()
- template is TemperatureControlTemplate -> TemperatureControlBehavior()
- template is ThumbnailTemplate -> StaticBehavior(uiExecutor, bgExecutor)
- else -> {
- object : Behavior {
- override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
- cvh.status.setText(cws.control?.getStatusText())
- cvh.applyRenderInfo(RenderInfo.lookup(cws.ci.deviceType, false))
- }
- }
- }
+ status == Control.STATUS_UNKNOWN -> UnknownBehavior::class
+ template is ToggleTemplate -> ToggleBehavior::class
+ template is ToggleRangeTemplate -> ToggleRangeBehavior::class
+ template is TemperatureControlTemplate -> TemperatureControlBehavior::class
+ template is ThumbnailTemplate -> StaticBehavior::class
+ else -> DefaultBehavior::class
}
}
@@ -142,9 +145,12 @@
icon.setImageIcon(Icon.createWithResource(context, ri.iconResourceId))
icon.setImageTintList(fg)
- clipLayer.getDrawable().setTintBlendMode(BlendMode.HUE)
- clipLayer.getDrawable().setTintList(bg)
+ clipLayer.getDrawable().apply {
+ setTintBlendMode(BlendMode.HUE)
+ setTintList(bg)
+ }
}
+
fun setEnabled(enabled: Boolean) {
status.setEnabled(enabled)
icon.setEnabled(enabled)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt
new file mode 100644
index 0000000..1747929
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 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.controls.ui
+
+class DefaultBehavior : Behavior {
+ lateinit var cvh: ControlViewHolder
+
+ override fun initialize(cvh: ControlViewHolder) {
+ this.cvh = cvh
+ }
+
+ override fun bind(cws: ControlWithState) {
+ cvh.status.setText(cws.control?.getStatusText() ?: "")
+ cvh.setEnabled(false)
+ cvh.applyRenderInfo(RenderInfo.lookup(cws.ci.deviceType, false))
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/StaticBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/StaticBehavior.kt
index 340cdc0..c006d6f 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/StaticBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/StaticBehavior.kt
@@ -24,20 +24,20 @@
import com.android.systemui.R
import com.android.systemui.controls.ui.ControlActionCoordinator.MAX_LEVEL
-import java.util.concurrent.Executor
-
/**
* Used for controls that cannot be interacted with. Information is presented to the user
* but no actions can be taken. If using a ThumbnailTemplate, the background image will
* be changed.
*/
-class StaticBehavior(
- val uiExecutor: Executor,
- val bgExecutor: Executor
-) : Behavior {
+class StaticBehavior() : Behavior {
lateinit var control: Control
+ lateinit var cvh: ControlViewHolder
- override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+ override fun initialize(cvh: ControlViewHolder) {
+ this.cvh = cvh
+ }
+
+ override fun bind(cws: ControlWithState) {
this.control = cws.control!!
cvh.status.setText(control.getStatusText())
@@ -51,12 +51,12 @@
val template = control.getControlTemplate()
if (template is ThumbnailTemplate) {
- bgExecutor.execute {
+ cvh.bgExecutor.execute {
// clear the default tinting in favor of only using alpha
val drawable = template.getThumbnail().loadDrawable(cvh.context)
drawable.setTintList(null)
drawable.setAlpha((0.45 * 255).toInt())
- uiExecutor.execute {
+ cvh.uiExecutor.execute {
val radius = cvh.context.getResources()
.getDimensionPixelSize(R.dimen.control_corner_radius).toFloat()
clipLayer.setDrawable(CornerDrawable(drawable, radius))
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
index 7661c6f..239d2e5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
@@ -16,12 +16,10 @@
package com.android.systemui.controls.ui
-import android.content.Context
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
import android.service.controls.Control
import android.service.controls.templates.TemperatureControlTemplate
-import android.widget.TextView
import com.android.systemui.R
import com.android.systemui.controls.ui.ControlActionCoordinator.MIN_LEVEL
@@ -32,15 +30,15 @@
lateinit var control: Control
lateinit var cvh: ControlViewHolder
lateinit var template: TemperatureControlTemplate
- lateinit var status: TextView
- lateinit var context: Context
- override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
- this.control = cws.control!!
+ override fun initialize(cvh: ControlViewHolder) {
this.cvh = cvh
- status = cvh.status
+ }
- status.setText(control.getStatusText())
+ override fun bind(cws: ControlWithState) {
+ this.control = cws.control!!
+
+ cvh.status.setText(control.getStatusText())
val ld = cvh.layout.getBackground() as LayerDrawable
clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
index 4c35d26..d306d7c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
@@ -16,11 +16,9 @@
package com.android.systemui.controls.ui
-import android.content.Context
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
import android.view.View
-import android.widget.TextView
import android.service.controls.Control
import android.service.controls.templates.ToggleTemplate
@@ -33,21 +31,21 @@
lateinit var template: ToggleTemplate
lateinit var control: Control
lateinit var cvh: ControlViewHolder
- lateinit var context: Context
- lateinit var status: TextView
- override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
- this.control = cws.control!!
+ override fun initialize(cvh: ControlViewHolder) {
this.cvh = cvh
- status = cvh.status
-
- status.setText(control.getStatusText())
-
- template = control.getControlTemplate() as ToggleTemplate
+ cvh.setEnabled(false)
cvh.layout.setOnClickListener(View.OnClickListener() {
ControlActionCoordinator.toggle(cvh, template.getTemplateId(), template.isChecked())
})
+ }
+
+ override fun bind(cws: ControlWithState) {
+ this.control = cws.control!!
+
+ cvh.status.setText(control.getStatusText())
+ template = control.getControlTemplate() as ToggleTemplate
val ld = cvh.layout.getBackground() as LayerDrawable
clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
index 8d65ca3..45d7397 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
@@ -51,17 +51,13 @@
private const val DEFAULT_FORMAT = "%.1f"
}
- override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
- this.control = cws.control!!
+ override fun initialize(cvh: ControlViewHolder) {
this.cvh = cvh
-
- statusExtra = cvh.statusExtra
status = cvh.status
-
- status.setText(control.getStatusText())
-
context = status.getContext()
+ cvh.setEnabled(false)
+
val gestureListener = ToggleRangeGestureListener(cvh.layout)
val gestureDetector = GestureDetector(context, gestureListener)
cvh.layout.setOnTouchListener({ v: View, e: MotionEvent ->
@@ -77,6 +73,13 @@
return@setOnTouchListener false
})
+ }
+
+ override fun bind(cws: ControlWithState) {
+ this.control = cws.control!!
+
+ statusExtra = cvh.statusExtra
+ status.setText(control.getStatusText())
val ld = cvh.layout.getBackground() as LayerDrawable
clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
index 27f1b33..1f33b85 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
@@ -17,7 +17,13 @@
package com.android.systemui.controls.ui
class UnknownBehavior : Behavior {
- override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+ lateinit var cvh: ControlViewHolder
+
+ override fun initialize(cvh: ControlViewHolder) {
+ this.cvh = cvh
+ }
+
+ override fun bind(cws: ControlWithState) {
cvh.status.setText(cvh.context.getString(com.android.internal.R.string.loading))
cvh.setEnabled(false)
cvh.applyRenderInfo(RenderInfo.lookup(cws.ci.deviceType, false))
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 8a095af..033f63b 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -28,6 +28,10 @@
import static android.app.AppOpsManager.OP_READ_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.OP_REQUEST_INSTALL_PACKAGES;
import static android.app.AppOpsManager.OP_WRITE_EXTERNAL_STORAGE;
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.storage.OnObbStateChangeListener.ERROR_ALREADY_MOUNTED;
@@ -136,6 +140,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.content.PackageMonitor;
import com.android.internal.os.AppFuseMount;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.FuseUnavailableMountException;
@@ -149,6 +154,7 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils;
+import com.android.server.SystemService.TargetUser;
import com.android.server.pm.Installer;
import com.android.server.storage.AppFuseBridge;
import com.android.server.storage.StorageSessionController;
@@ -184,6 +190,7 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -270,6 +277,11 @@
public void onStopUser(int userHandle) {
mStorageManagerService.onStopUser(userHandle);
}
+
+ @Override
+ public void onStartUser(TargetUser user) {
+ mStorageManagerService.snapshotAndMonitorLegacyStorageAppOp(user.getUserHandle());
+ }
}
private static final boolean DEBUG_EVENTS = false;
@@ -578,6 +590,12 @@
private final boolean mIsFuseEnabled;
+ @GuardedBy("mLock")
+ private final Set<Integer> mUidsWithLegacyExternalStorage = new ArraySet<>();
+ // Not guarded by lock, always used on the ActivityManager thread
+ private final Map<Integer, PackageMonitor> mPackageMonitorsForUser = new ArrayMap<>();
+
+
class ObbState implements IBinder.DeathRecipient {
public ObbState(String rawPath, String canonicalPath, int callingUid,
IObbActionListener token, int nonce, String volId) {
@@ -1145,6 +1163,10 @@
} catch (Exception e) {
Slog.wtf(TAG, e);
}
+ PackageMonitor monitor = mPackageMonitorsForUser.remove(userId);
+ if (monitor != null) {
+ monitor.unregister();
+ }
}
private boolean supportsBlockCheckpoint() throws RemoteException {
@@ -1836,6 +1858,49 @@
}
}
+ private void updateLegacyStorageApps(String packageName, int uid, boolean hasLegacy) {
+ synchronized (mLock) {
+ if (hasLegacy) {
+ Slog.v(TAG, "Package " + packageName + " has legacy storage");
+ mUidsWithLegacyExternalStorage.add(uid);
+ } else {
+ // TODO(b/149391976): Handle shared user id. Check if there's any other
+ // installed app with legacy external storage before removing
+ Slog.v(TAG, "Package " + packageName + " does not have legacy storage");
+ mUidsWithLegacyExternalStorage.remove(uid);
+ }
+ }
+ }
+
+ private void snapshotAndMonitorLegacyStorageAppOp(UserHandle user) {
+ int userId = user.getIdentifier();
+
+ // TODO(b/149391976): Use mIAppOpsService.getPackagesForOps instead of iterating below
+ // It should improve performance but the AppOps method doesn't return any app here :(
+ // This operation currently takes about ~20ms on a freshly flashed device
+ for (ApplicationInfo ai : mPmInternal.getInstalledApplications(MATCH_DIRECT_BOOT_AWARE
+ | MATCH_DIRECT_BOOT_UNAWARE | MATCH_UNINSTALLED_PACKAGES | MATCH_ANY_USER,
+ userId, Process.myUid())) {
+ try {
+ boolean hasLegacy = mIAppOpsService.checkOperation(OP_LEGACY_STORAGE, ai.uid,
+ ai.packageName) == MODE_ALLOWED;
+ updateLegacyStorageApps(ai.packageName, ai.uid, hasLegacy);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to check legacy op for package " + ai.packageName, e);
+ }
+ }
+
+ PackageMonitor monitor = new PackageMonitor() {
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ updateLegacyStorageApps(packageName, uid, false);
+ }
+ };
+ // TODO(b/149391976): Use different handler?
+ monitor.register(mContext, user, true, mHandler);
+ mPackageMonitorsForUser.put(userId, monitor);
+ }
+
private static long getLastAccessTime(AppOpsManager manager,
int uid, String packageName, int[] ops) {
long maxTime = 0;
@@ -4337,6 +4402,13 @@
mHandler.obtainMessage(H_RESET).sendToTarget();
}
+ @Override
+ public boolean hasLegacyExternalStorage(int uid) {
+ synchronized (mLock) {
+ return mUidsWithLegacyExternalStorage.contains(uid);
+ }
+ }
+
public boolean hasExternalStorage(int uid, String packageName) {
// No need to check for system uid. This avoids a deadlock between
// PackageManagerService and AppOpsService.
@@ -4382,8 +4454,11 @@
// volumes, USB OTGs that are rarely mounted. The app will get the
// external_storage gid on next organic restart.
killAppForOpChange(code, uid, packageName);
- return;
}
+ return;
+ case OP_LEGACY_STORAGE:
+ updateLegacyStorageApps(packageName, uid, mode == MODE_ALLOWED);
+ return;
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d6d24e7..1da07ef 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7156,7 +7156,7 @@
} finally {
Binder.restoreCallingIdentity(ident);
}
- } else if (dyingProc == cpr.proc) {
+ } else if (dyingProc == cpr.proc && dyingProc != null) {
// The old stable connection's client should be killed during proc cleaning up,
// so do not re-use the old ContentProviderRecord, otherwise the new clients
// could get killed unexpectedly.
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 6474303..8379614 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -29,7 +29,6 @@
import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.MediaRoute2Info;
-import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -49,6 +48,8 @@
@SuppressWarnings("WeakerAccess") /* synthetic access */
final Map<String, BluetoothRouteInfo> mBluetoothRoutes = new HashMap<>();
@SuppressWarnings("WeakerAccess") /* synthetic access */
+ BluetoothRouteInfo mSelectedRoute = null;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
BluetoothA2dp mA2dpProfile;
@SuppressWarnings("WeakerAccess") /* synthetic access */
BluetoothHearingAid mHearingAidProfile;
@@ -62,8 +63,6 @@
private final BroadcastReceiver mBroadcastReceiver = new BluetoothBroadcastReceiver();
private final BluetoothProfileListener mProfileListener = new BluetoothProfileListener();
- private BluetoothDevice mActiveDevice = null;
-
static synchronized BluetoothRouteProvider getInstance(@NonNull Context context,
@NonNull BluetoothRoutesUpdatedListener listener) {
Objects.requireNonNull(context);
@@ -108,27 +107,21 @@
}
/**
- * Clears the active device for all known profiles.
+ * Transfers to a given bluetooth route.
+ * The dedicated BT device with the route would be activated.
+ *
+ * @param routeId the id of the Bluetooth device. {@code null} denotes to clear the use of
+ * BT routes.
*/
- public void clearActiveDevices() {
- BluetoothA2dp a2dpProfile = mA2dpProfile;
- BluetoothHearingAid hearingAidProfile = mHearingAidProfile;
- if (a2dpProfile != null) {
- a2dpProfile.setActiveDevice(null);
+ public void transferTo(@Nullable String routeId) {
+ if (routeId == null) {
+ clearActiveDevices();
+ return;
}
- if (hearingAidProfile != null) {
- hearingAidProfile.setActiveDevice(null);
- }
- }
- /**
- * Sets the active device.
- * @param deviceId the id of the Bluetooth device
- */
- public void setActiveDevice(@NonNull String deviceId) {
- BluetoothRouteInfo btRouteInfo = mBluetoothRoutes.get(deviceId);
+ BluetoothRouteInfo btRouteInfo = mBluetoothRoutes.get(routeId);
if (btRouteInfo == null) {
- Slog.w(TAG, "setActiveDevice: unknown device id=" + deviceId);
+ Slog.w(TAG, "setActiveDevice: unknown route id=" + routeId);
return;
}
BluetoothA2dp a2dpProfile = mA2dpProfile;
@@ -144,6 +137,20 @@
}
}
+ /**
+ * Clears the active device for all known profiles.
+ */
+ private void clearActiveDevices() {
+ BluetoothA2dp a2dpProfile = mA2dpProfile;
+ BluetoothHearingAid hearingAidProfile = mHearingAidProfile;
+ if (a2dpProfile != null) {
+ a2dpProfile.setActiveDevice(null);
+ }
+ if (hearingAidProfile != null) {
+ hearingAidProfile.setActiveDevice(null);
+ }
+ }
+
private void addEventReceiver(String action, BluetoothEventReceiver eventReceiver) {
mEventReceiverMap.put(action, eventReceiver);
mIntentFilter.addAction(action);
@@ -159,7 +166,8 @@
}
}
- @NonNull List<MediaRoute2Info> getBluetoothRoutes() {
+ @NonNull
+ List<MediaRoute2Info> getAllBluetoothRoutes() {
ArrayList<MediaRoute2Info> routes = new ArrayList<>();
for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) {
routes.add(btRoute.route);
@@ -167,17 +175,14 @@
return routes;
}
- @Nullable String getActiveDeviceAddress() {
- BluetoothDevice device = mActiveDevice;
- if (device == null) {
- return null;
- }
- return device.getAddress();
+ @Nullable
+ String getSelectedRouteId() {
+ return mSelectedRoute == null ? null : mSelectedRoute.route.getId();
}
private void notifyBluetoothRoutesUpdated() {
if (mListener != null) {
- mListener.onBluetoothRoutesUpdated(getBluetoothRoutes());
+ mListener.onBluetoothRoutesUpdated(getAllBluetoothRoutes());
}
}
@@ -198,15 +203,10 @@
return newBtRoute;
}
- private void setRouteConnectionStateForDevice(BluetoothDevice device,
+ private void setRouteConnectionState(@NonNull BluetoothRouteInfo btRoute,
@MediaRoute2Info.ConnectionState int state) {
- if (device == null) {
- Slog.w(TAG, "setRouteConnectionStateForDevice: device shouldn't be null");
- return;
- }
- BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress());
if (btRoute == null) {
- Slog.w(TAG, "setRouteConnectionStateForDevice: route shouldn't be null");
+ Slog.w(TAG, "setRouteConnectionState: route shouldn't be null");
return;
}
if (btRoute.route.getConnectionState() == state) {
@@ -260,8 +260,8 @@
mBluetoothRoutes.put(device.getAddress(), btRoute);
}
if (activeDevices.contains(device)) {
- mActiveDevice = device;
- setRouteConnectionStateForDevice(device,
+ mSelectedRoute = btRoute;
+ setRouteConnectionState(mSelectedRoute,
MediaRoute2Info.CONNECTION_STATE_CONNECTED);
}
@@ -344,20 +344,18 @@
public void onReceive(Context context, Intent intent, BluetoothDevice device) {
switch (intent.getAction()) {
case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED:
- String prevActiveDeviceAddress =
- (mActiveDevice == null) ? null : mActiveDevice.getAddress();
- String curActiveDeviceAddress =
- (device == null) ? null : device.getAddress();
- if (!TextUtils.equals(prevActiveDeviceAddress, curActiveDeviceAddress)) {
- if (mActiveDevice != null) {
- setRouteConnectionStateForDevice(mActiveDevice,
+ if (mSelectedRoute == null
+ || !mSelectedRoute.btDevice.equals(device)) {
+ if (mSelectedRoute != null) {
+ setRouteConnectionState(mSelectedRoute,
MediaRoute2Info.CONNECTION_STATE_DISCONNECTED);
}
- if (device != null) {
- setRouteConnectionStateForDevice(device,
+ mSelectedRoute = (device == null) ? null
+ : mBluetoothRoutes.get(device.getAddress());
+ if (mSelectedRoute != null) {
+ setRouteConnectionState(mSelectedRoute,
MediaRoute2Info.CONNECTION_STATE_CONNECTED);
}
- mActiveDevice = device;
notifyBluetoothRoutesUpdated();
}
break;
@@ -386,10 +384,8 @@
btRoute.connectedProfiles.delete(profile);
if (btRoute.connectedProfiles.size() == 0) {
mBluetoothRoutes.remove(device.getAddress());
- if (mActiveDevice != null
- && TextUtils.equals(mActiveDevice.getAddress(),
- device.getAddress())) {
- mActiveDevice = null;
+ if (mSelectedRoute != null && mSelectedRoute.btDevice.equals(device)) {
+ mSelectedRoute = null;
}
notifyBluetoothRoutesUpdated();
}
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index ae11c70..7f7a663 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -145,9 +145,9 @@
@Override
public void transferToRoute(String sessionId, String routeId) {
if (TextUtils.equals(routeId, mDefaultRoute.getId())) {
- mBtRouteProvider.clearActiveDevices();
+ mBtRouteProvider.transferTo(null);
} else {
- mBtRouteProvider.setActiveDevice(routeId);
+ mBtRouteProvider.transferTo(routeId);
}
}
@@ -196,7 +196,7 @@
}
private void initializeSessionInfo() {
- mBluetoothRoutes = mBtRouteProvider.getBluetoothRoutes();
+ mBluetoothRoutes = mBtRouteProvider.getAllBluetoothRoutes();
MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
builder.addRoute(mDefaultRoute);
@@ -251,7 +251,7 @@
RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
SYSTEM_SESSION_ID, "" /* clientPackageName */)
.setSystemSession(true);
- String activeBtDeviceAddress = mBtRouteProvider.getActiveDeviceAddress();
+ String activeBtDeviceAddress = mBtRouteProvider.getSelectedRouteId();
mSelectedRouteId = TextUtils.isEmpty(activeBtDeviceAddress) ? mDefaultRoute.getId()
: activeBtDeviceAddress;
builder.addSelectedRoute(mSelectedRouteId);
@@ -311,7 +311,7 @@
AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0);
if (newVolume != oldVolume) {
- String activeBtDeviceAddress = mBtRouteProvider.getActiveDeviceAddress();
+ String activeBtDeviceAddress = mBtRouteProvider.getSelectedRouteId();
if (!TextUtils.isEmpty(activeBtDeviceAddress)) {
for (int i = mBluetoothRoutes.size() - 1; i >= 0; i--) {
MediaRoute2Info route = mBluetoothRoutes.get(i);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a45a996..19e6062 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -45,7 +45,6 @@
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -2058,11 +2057,8 @@
/** {@inheritDoc} */
@Override
- public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp) {
- final int type = attrs.type;
- final boolean isRoundedCornerOverlay =
- (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
-
+ public int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
+ int[] outAppOp) {
if (isRoundedCornerOverlay && mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
!= PERMISSION_GRANTED) {
return ADD_PERMISSION_DENIED;
@@ -2119,7 +2115,7 @@
ApplicationInfo appInfo;
try {
appInfo = mPackageManager.getApplicationInfoAsUser(
- attrs.packageName,
+ packageName,
0 /* flags */,
UserHandle.getUserId(callingUid));
} catch (PackageManager.NameNotFoundException e) {
@@ -2139,7 +2135,7 @@
// check if user has enabled this operation. SecurityException will be thrown if this app
// has not been allowed by the user
- final int mode = mAppOpsManager.noteOpNoThrow(outAppOp[0], callingUid, attrs.packageName);
+ final int mode = mAppOpsManager.noteOpNoThrow(outAppOp[0], callingUid, packageName);
switch (mode) {
case AppOpsManager.MODE_ALLOWED:
case AppOpsManager.MODE_IGNORED:
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
index a83c58d..740472e 100644
--- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -35,14 +35,17 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.os.storage.StorageManagerInternal;
import android.util.Log;
import com.android.internal.compat.IPlatformCompat;
+import com.android.server.LocalServices;
/**
* The behavior of soft restricted permissions is different for each permission. This class collects
@@ -107,11 +110,16 @@
final boolean isWhiteListed;
boolean shouldApplyRestriction;
final boolean hasRequestedLegacyExternalStorage;
+ final boolean shouldPreserveLegacyExternalStorage;
final boolean hasWriteMediaStorageGrantedForUid;
final boolean isScopedStorageEnabled;
if (appInfo != null) {
PackageManager pm = context.getPackageManager();
+ PackageManagerInternal pmInternal =
+ LocalServices.getService(PackageManagerInternal.class);
+ StorageManagerInternal smInternal =
+ LocalServices.getService(StorageManagerInternal.class);
int flags = pm.getPermissionFlags(permission, appInfo.packageName, user);
isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
hasRequestedLegacyExternalStorage = hasUidRequestedLegacyExternalStorage(
@@ -123,12 +131,16 @@
isScopedStorageEnabled =
isChangeEnabledForUid(context, appInfo, user, ENABLE_SCOPED_STORAGE)
|| isScopedStorageRequired;
+ shouldPreserveLegacyExternalStorage = pmInternal.getPackage(
+ appInfo.packageName).hasPreserveLegacyExternalStorage()
+ && smInternal.hasLegacyExternalStorage(appInfo.uid);
shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0
- || isScopedStorageRequired;
+ || (isScopedStorageRequired && !shouldPreserveLegacyExternalStorage);
} else {
isWhiteListed = false;
shouldApplyRestriction = false;
hasRequestedLegacyExternalStorage = false;
+ shouldPreserveLegacyExternalStorage = false;
hasWriteMediaStorageGrantedForUid = false;
isScopedStorageEnabled = false;
}
@@ -150,7 +162,8 @@
public boolean mayAllowExtraAppOp() {
return !shouldApplyRestriction
&& (hasRequestedLegacyExternalStorage
- || hasWriteMediaStorageGrantedForUid);
+ || hasWriteMediaStorageGrantedForUid
+ || shouldPreserveLegacyExternalStorage);
}
@Override
public boolean mayDenyExtraAppOpIfGranted() {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 39093ae..9d417c9 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -686,17 +686,25 @@
WindowManagerFuncs windowManagerFuncs);
/**
- * Check permissions when adding a window.
+ * Check permissions when adding a window or a window token from
+ * {@link android.app.WindowContext}.
*
- * @param attrs The window's LayoutParams.
+ * @param type The window type
+ * @param isRoundedCornerOverlay {@code true} to indicate the adding window is
+ * round corner overlay.
+ * @param packageName package name
* @param outAppOp First element will be filled with the app op corresponding to
* this window, or OP_NONE.
*
* @return {@link WindowManagerGlobal#ADD_OKAY} if the add can proceed;
* else an error code, usually
* {@link WindowManagerGlobal#ADD_PERMISSION_DENIED}, to abort the add.
+ *
+ * @see IWindowManager#addWindowTokenWithOptions(IBinder, int, int, Bundle, String)
+ * @see WindowManager.LayoutParams#PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY
*/
- public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp);
+ int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
+ int[] outAppOp);
/**
* After the window manager has computed the current configuration based
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index f72020e..3f4e791 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -713,7 +713,11 @@
final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
final boolean showWallpaper = wallpaperTarget != null
- && (wallpaperTarget.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
+ && ((wallpaperTarget.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0
+ // Update task open transition to wallpaper transition when wallpaper is visible.
+ // (i.e.launching app info activity from recent tasks)
+ || ((transit == TRANSIT_TASK_OPEN || transit == TRANSIT_TASK_TO_FRONT)
+ && mWallpaperControllerLocked.isWallpaperVisible()));
// If wallpaper is animating or wallpaperTarget doesn't have SHOW_WALLPAPER flag set,
// don't consider upgrading to wallpaper transition.
final WindowState oldWallpaper =
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index be68c5d..634d7e9 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -742,8 +742,10 @@
}
boolean isWallpaperVisible(WindowState w) {
- return w != null && w.mAttrs.type == TYPE_BASE_APPLICATION && w.mActivityRecord != null
- && mTargetActivityRecord == w.mActivityRecord && isTargetOverWallpaper();
+ return w != null && w.mAttrs.type == TYPE_BASE_APPLICATION &&
+ ((w.mActivityRecord != null && mTargetActivityRecord == w.mActivityRecord)
+ || isAnimatingTask(w.getTask()))
+ && isTargetOverWallpaper();
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1b0d177..e85261d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1336,7 +1336,10 @@
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState) {
int[] appOp = new int[1];
- int res = mPolicy.checkAddPermission(attrs, appOp);
+ final boolean isRoundedCornerOverlay = (attrs.privateFlags
+ & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
+ int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
+ appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
@@ -1410,8 +1413,6 @@
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
- final boolean isRoundedCornerOverlay =
- (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
} else if (rootType >= FIRST_APPLICATION_WINDOW
@@ -2560,10 +2561,8 @@
final boolean callerCanManageAppTokens =
checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()");
if (!callerCanManageAppTokens) {
- // TODO(window-context): refactor checkAddPermission to not take attrs.
- LayoutParams attrs = new LayoutParams(type);
- attrs.packageName = packageName;
- final int res = mPolicy.checkAddPermission(attrs, new int[1]);
+ final int res = mPolicy.checkAddPermission(type, false /* isRoundedCornerOverlay */,
+ packageName, new int[1]);
if (res != ADD_OKAY) {
return res;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 3a68924..7a075a2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -75,7 +75,8 @@
}
@Override
- public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp) {
+ public int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
+ int[] outAppOp) {
return 0;
}