Update setBroadcastSubscriber

change #setBroadcastSubscriber and #unsetBroadcastSubscriber
to avoid using intentsender

Bug: 146074295
Test: Ran GTS Tests
Change-Id: I1510e44bcdf49b579fd49f51081c6a40618039fa
This commit is contained in:
Jeffrey Huang
2020-01-06 16:24:45 -08:00
parent 47537a1c58
commit 4f2e6bd68d
11 changed files with 186 additions and 119 deletions

View File

@@ -17,7 +17,6 @@
package android.os;
import android.os.IPullAtomCallback;
import android.os.StatsDimensionsValue;
import android.os.StatsLogEventWrapper;
/**
@@ -66,15 +65,6 @@ interface IStatsCompanionService {
/** Pull the specified data. Results will be sent to statsd when complete. */
StatsLogEventWrapper[] pullData(int pullCode);
/**
* Requests StatsCompanionService to send a broadcast using the given intentSender
* (which should cast to an IIntentSender), along with the other information specified.
*/
oneway void sendSubscriberBroadcast(in IBinder intentSender, long configUid, long configId,
long subscriptionId, long subscriptionRuleId,
in String[] cookies,
in StatsDimensionsValue dimensionsValue);
/** Tells StatsCompaionService to grab the uid map snapshot and send it to statsd. */
oneway void triggerUidSnapshot();

View File

@@ -72,8 +72,17 @@ interface IStatsManagerService {
* This function can only be called by the owner (uid) of the config. It must be called each
* time statsd starts. Later calls overwrite previous calls; only one PendingIntent is stored.
*
* Requires Manifest.permission.DUMP.
* Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
*/
void setBroadcastSubscriber(long configKey, long subscriberId, in PendingIntent pendingIntent,
in String packageName);
/**
* Undoes setBroadcastSubscriber() for the (configKey, subscriberId) pair.
* Any broadcasts associated with subscriberId will henceforth not be sent.
* No-op if this (configKey, subscriberId) pair was not associated with an PendingIntent.
*
* Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
*/
void unsetBroadcastSubscriber(long configKey, long subscriberId, in String packageName);
}

View File

@@ -151,34 +151,31 @@ interface IStatsd {
void removeConfiguration(in long configKey, in String packageName);
/**
* Set the IIntentSender (i.e. PendingIntent) to be used when broadcasting subscriber
* Set the PendingIntentRef to be used when broadcasting subscriber
* information to the given subscriberId within the given config.
*
* Suppose that the calling uid has added a config with key configKey, and that in this config
* Suppose that the calling uid has added a config with key configId, and that in this config
* it is specified that when a particular anomaly is detected, a broadcast should be sent to
* a BroadcastSubscriber with id subscriberId. This function links the given intentSender with
* that subscriberId (for that config), so that this intentSender is used to send the broadcast
* a BroadcastSubscriber with id subscriberId. This function links the given pendingIntent with
* that subscriberId (for that config), so that this pendingIntent is used to send the broadcast
* when the anomaly is detected.
*
* This function can only be called by the owner (uid) of the config. It must be called each
* time statsd starts. Later calls overwrite previous calls; only one intentSender is stored.
*
* intentSender must be convertible into an IntentSender using IntentSender(IBinder)
* and cannot be null.
* time statsd starts. Later calls overwrite previous calls; only one pendingIntent is stored.
*
* Requires Manifest.permission.DUMP.
*/
void setBroadcastSubscriber(long configKey, long subscriberId, in IBinder intentSender,
in String packageName);
void setBroadcastSubscriber(long configId, long subscriberId, in IPendingIntentRef pir,
int callingUid);
/**
* Undoes setBroadcastSubscriber() for the (configKey, subscriberId) pair.
* Undoes setBroadcastSubscriber() for the (configId, subscriberId) pair.
* Any broadcasts associated with subscriberId will henceforth not be sent.
* No-op if this (configKey, subsriberId) pair was not associated with an IntentSender.
* No-op if this (configKey, subscriberId) pair was not associated with an PendingIntentRef.
*
* Requires Manifest.permission.DUMP.
*/
void unsetBroadcastSubscriber(long configKey, long subscriberId, in String packageName);
void unsetBroadcastSubscriber(long configId, long subscriberId, int callingUid);
/**
* Apps can send an atom via this application breadcrumb with the specified label and state for

View File

@@ -28,6 +28,7 @@ import android.util.Slog;
import com.android.server.SystemService;
import java.util.ArrayList;
import java.util.Arrays;
/**
@@ -101,7 +102,7 @@ public class StatsCompanion {
private static final String EXTRA_LAST_REPORT_TIME = "android.app.extra.LAST_REPORT_TIME";
private static final int CODE_DATA_BROADCAST = 1;
private static final int CODE_ACTIVE_CONFIGS_BROADCAST = 1;
private static final int CODE_SUBSCRIBER_BROADCAST = 1;
private final PendingIntent mPendingIntent;
private final Context mContext;
@@ -141,7 +142,36 @@ public class StatsCompanion {
@Override
public void sendSubscriberBroadcast(long configUid, long configId, long subscriptionId,
long subscriptionRuleId, String[] cookies, StatsDimensionsValue dimensionsValue) {
// no-op
enforceStatsCompanionPermission(mContext);
Intent intent =
new Intent()
.putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
.putExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, configId)
.putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId)
.putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID,
subscriptionRuleId)
.putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue);
ArrayList<String> cookieList = new ArrayList<>(cookies.length);
cookieList.addAll(Arrays.asList(cookies));
intent.putStringArrayListExtra(
StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES, cookieList);
if (DEBUG) {
Slog.d(TAG,
String.format(
"Statsd sendSubscriberBroadcast with params {%d %d %d %d %s %s}",
configUid, configId, subscriptionId, subscriptionRuleId,
Arrays.toString(cookies),
dimensionsValue));
}
try {
mPendingIntent.send(mContext, CODE_SUBSCRIBER_BROADCAST, intent, null, null);
} catch (PendingIntent.CanceledException e) {
Slog.w(TAG,
"Unable to send using PendingIntent from uid " + configUid
+ "; presumably it had been cancelled.");
}
}
}
}

View File

@@ -50,7 +50,6 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -88,7 +87,6 @@ import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StatFs;
import android.os.StatsDimensionsValue;
import android.os.StatsLogEventWrapper;
import android.os.SynchronousResultReceiver;
import android.os.SystemClock;
@@ -202,7 +200,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
private static final int PACKAGE_NAME_FIELD_ID = 4;
private static final int INSTALLER_FIELD_ID = 5;
public static final int CODE_SUBSCRIBER_BROADCAST = 1;
public static final int DEATH_THRESHOLD = 10;
/**
* Which native processes to snapshot memory for.
@@ -442,43 +439,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
KernelCpuThreadReaderSettingsObserver.getSettingsModifiedReader(mContext);
}
@Override
public void sendSubscriberBroadcast(IBinder intentSenderBinder, long configUid, long configKey,
long subscriptionId, long subscriptionRuleId, String[] cookies,
StatsDimensionsValue dimensionsValue) {
StatsCompanion.enforceStatsCompanionPermission(mContext);
IntentSender intentSender = new IntentSender(intentSenderBinder);
Intent intent =
new Intent()
.putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
.putExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, configKey)
.putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId)
.putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, subscriptionRuleId)
.putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue);
ArrayList<String> cookieList = new ArrayList<>(cookies.length);
for (String cookie : cookies) {
cookieList.add(cookie);
}
intent.putStringArrayListExtra(
StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES, cookieList);
if (DEBUG) {
Slog.d(TAG,
String.format("Statsd sendSubscriberBroadcast with params {%d %d %d %d %s %s}",
configUid, configKey, subscriptionId, subscriptionRuleId,
Arrays.toString(cookies),
dimensionsValue));
}
try {
intentSender.sendIntent(mContext, CODE_SUBSCRIBER_BROADCAST, intent, null, null);
} catch (IntentSender.SendIntentException e) {
Slog.w(TAG,
"Unable to send using IntentSender from uid " + configUid
+ "; presumably it had been cancelled.");
}
}
private final static int[] toIntArray(List<Integer> list) {
int[] ret = new int[list.size()];
for (int i = 0; i < ret.length; i++) {

View File

@@ -61,6 +61,9 @@ public class StatsManagerService extends IStatsManagerService.Stub {
@GuardedBy("mLock")
private ArrayMap<Integer, PendingIntentRef> mActiveConfigsPirMap =
new ArrayMap<>();
@GuardedBy("mLock")
private ArrayMap<ConfigKey, ArrayMap<Long, PendingIntentRef>> mBroadcastSubscriberPirMap =
new ArrayMap<>();
public StatsManagerService(Context context) {
super();
@@ -189,11 +192,56 @@ public class StatsManagerService extends IStatsManagerService.Stub {
}
@Override
public void setBroadcastSubscriber(long configKey, long subscriberId,
public void setBroadcastSubscriber(long configId, long subscriberId,
PendingIntent pendingIntent, String packageName) {
//no-op
if (DEBUG) {
Slog.d(TAG, "setBroadcastSubscriber");
enforceDumpAndUsageStatsPermission(packageName);
int callingUid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
PendingIntentRef pir = new PendingIntentRef(pendingIntent, mContext);
ConfigKey key = new ConfigKey(callingUid, configId);
// We add the PIR to a map so we can reregister if statsd is unavailable.
synchronized (mLock) {
ArrayMap<Long, PendingIntentRef> innerMap = mBroadcastSubscriberPirMap
.getOrDefault(key, new ArrayMap<>());
innerMap.put(subscriberId, pir);
mBroadcastSubscriberPirMap.put(key, innerMap);
}
try {
IStatsd statsd = getStatsdNonblocking();
if (statsd != null) {
statsd.setBroadcastSubscriber(
configId, subscriberId, pir, callingUid);
}
} catch (RemoteException e) {
Slog.e(TAG, "Failed to setBroadcastSubscriber with statsd");
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void unsetBroadcastSubscriber(long configId, long subscriberId, String packageName) {
enforceDumpAndUsageStatsPermission(packageName);
int callingUid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
ConfigKey key = new ConfigKey(callingUid, configId);
synchronized (mLock) {
ArrayMap<Long, PendingIntentRef> innerMap = mBroadcastSubscriberPirMap
.getOrDefault(key, new ArrayMap<>());
innerMap.remove(subscriberId);
if (innerMap.isEmpty()) {
mBroadcastSubscriberPirMap.remove(key);
}
}
try {
IStatsd statsd = getStatsdNonblocking();
if (statsd != null) {
statsd.unsetBroadcastSubscriber(configId, subscriberId, callingUid);
}
} catch (RemoteException e) {
Slog.e(TAG, "Failed to unsetBroadcastSubscriber with statsd");
} finally {
Binder.restoreCallingIdentity(token);
}
}
@@ -285,11 +333,19 @@ public class StatsManagerService extends IStatsManagerService.Stub {
if (statsd == null) {
return;
}
// Since we do not want to make an IPC with the a lock held, we first create local deep
// copies of the data with the lock held before iterating through the maps.
ArrayMap<ConfigKey, PendingIntentRef> dataFetchCopy;
ArrayMap<Integer, PendingIntentRef> activeConfigsChangedCopy;
ArrayMap<ConfigKey, ArrayMap<Long, PendingIntentRef>> broadcastSubscriberCopy;
synchronized (mLock) {
dataFetchCopy = new ArrayMap<>(mDataFetchPirMap);
activeConfigsChangedCopy = new ArrayMap<>(mActiveConfigsPirMap);
broadcastSubscriberCopy = new ArrayMap<>();
for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry
: mBroadcastSubscriberPirMap.entrySet()) {
broadcastSubscriberCopy.put(entry.getKey(), new ArrayMap<>(entry.getValue()));
}
}
for (Map.Entry<ConfigKey, PendingIntentRef> entry : dataFetchCopy.entrySet()) {
ConfigKey key = entry.getKey();
@@ -307,5 +363,17 @@ public class StatsManagerService extends IStatsManagerService.Stub {
Slog.e(TAG, "Failed to setActiveConfigsChangedOperation from pirMap");
}
}
for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry
: broadcastSubscriberCopy.entrySet()) {
for (Map.Entry<Long, PendingIntentRef> subscriberEntry : entry.getValue().entrySet()) {
ConfigKey configKey = entry.getKey();
try {
statsd.setBroadcastSubscriber(configKey.getConfigId(), subscriberEntry.getKey(),
subscriberEntry.getValue(), configKey.getUid());
} catch (RemoteException e) {
Slog.e(TAG, "Failed to setBroadcastSubscriber from pirMap");
}
}
}
}
}

View File

@@ -1111,7 +1111,6 @@ Status StatsService::statsCompanionReady() {
mPullerManager->SetStatsCompanionService(statsCompanion);
mAnomalyAlarmMonitor->setStatsCompanionService(statsCompanion);
mPeriodicAlarmMonitor->setStatsCompanionService(statsCompanion);
SubscriberReporter::getInstance().setStatsCompanionService(statsCompanion);
return Status::ok();
}
@@ -1241,26 +1240,24 @@ Status StatsService::removeConfiguration(int64_t key, const String16& packageNam
Status StatsService::setBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
const sp<android::IBinder>& intentSender,
const String16& packageName) {
ENFORCE_DUMP_AND_USAGE_STATS(packageName);
const sp<IPendingIntentRef>& pir,
const int32_t callingUid) {
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::setBroadcastSubscriber called.");
IPCThreadState* ipc = IPCThreadState::self();
ConfigKey configKey(ipc->getCallingUid(), configId);
ConfigKey configKey(callingUid, configId);
SubscriberReporter::getInstance()
.setBroadcastSubscriber(configKey, subscriberId, intentSender);
.setBroadcastSubscriber(configKey, subscriberId, pir);
return Status::ok();
}
Status StatsService::unsetBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
const String16& packageName) {
ENFORCE_DUMP_AND_USAGE_STATS(packageName);
const int32_t callingUid) {
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::unsetBroadcastSubscriber called.");
IPCThreadState* ipc = IPCThreadState::self();
ConfigKey configKey(ipc->getCallingUid(), configId);
ConfigKey configKey(callingUid, configId);
SubscriberReporter::getInstance()
.unsetBroadcastSubscriber(configKey, subscriberId);
return Status::ok();
@@ -1626,7 +1623,6 @@ void StatsService::binderDied(const wp <IBinder>& who) {
}
mAnomalyAlarmMonitor->setStatsCompanionService(nullptr);
mPeriodicAlarmMonitor->setStatsCompanionService(nullptr);
SubscriberReporter::getInstance().setStatsCompanionService(nullptr);
mPullerManager->SetStatsCompanionService(nullptr);
}

View File

@@ -149,20 +149,19 @@ public:
const String16& packageName) override;
/**
* Binder call to associate the given config's subscriberId with the given intentSender.
* intentSender must be convertible into an IntentSender (in Java) using IntentSender(IBinder).
* Binder call to associate the given config's subscriberId with the given pendingIntentRef.
*/
virtual Status setBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
const sp<android::IBinder>& intentSender,
const String16& packageName) override;
const sp<IPendingIntentRef>& pir,
const int32_t callingUid) override;
/**
* Binder call to unassociate the given config's subscriberId with any intentSender.
* Binder call to unassociate the given config's subscriberId with any pendingIntentRef.
*/
virtual Status unsetBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
const String16& packageName) override;
const int32_t callingUid) override;
/** Inform statsCompanion that statsd is ready. */
virtual void sayHiToStatsCompanion();

View File

@@ -19,7 +19,6 @@
#include "SubscriberReporter.h"
using android::IBinder;
using std::lock_guard;
using std::unordered_map;
@@ -29,12 +28,32 @@ namespace statsd {
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;
void binderDied(const android::wp<android::IBinder>& who) override {
if (IInterface::asBinder(SubscriberReporter::getInstance().getBroadcastSubscriber(
mConfigKey, mSubscriberId)) == who.promote()) {
SubscriberReporter::getInstance().unsetBroadcastSubscriber(mConfigKey, mSubscriberId);
}
}
};
void SubscriberReporter::setBroadcastSubscriber(const ConfigKey& configKey,
int64_t subscriberId,
const sp<IBinder>& intentSender) {
const sp<IPendingIntentRef>& pir) {
VLOG("SubscriberReporter::setBroadcastSubscriber called.");
lock_guard<std::mutex> lock(mLock);
mIntentMap[configKey][subscriberId] = intentSender;
mIntentMap[configKey][subscriberId] = pir;
IInterface::asBinder(pir)->linkToDeath(
new BroadcastSubscriberDeathRecipient(configKey, subscriberId));
}
void SubscriberReporter::unsetBroadcastSubscriber(const ConfigKey& configKey,
@@ -97,18 +116,13 @@ void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey,
sendBroadcastLocked(it2->second, configKey, subscription, cookies, dimKey);
}
void SubscriberReporter::sendBroadcastLocked(const sp<IBinder>& intentSender,
void SubscriberReporter::sendBroadcastLocked(const sp<IPendingIntentRef>& pir,
const ConfigKey& configKey,
const Subscription& subscription,
const vector<String16>& cookies,
const MetricDimensionKey& dimKey) const {
VLOG("SubscriberReporter::sendBroadcastLocked called.");
if (mStatsCompanionService == nullptr) {
ALOGW("Failed to send subscriber broadcast: could not access StatsCompanionService.");
return;
}
mStatsCompanionService->sendSubscriberBroadcast(
intentSender,
pir->sendSubscriberBroadcast(
configKey.GetUid(),
configKey.GetId(),
subscription.id(),
@@ -117,6 +131,20 @@ void SubscriberReporter::sendBroadcastLocked(const sp<IBinder>& intentSender,
getStatsDimensionsValue(dimKey.getDimensionKeyInWhat()));
}
sp<IPendingIntentRef> SubscriberReporter::getBroadcastSubscriber(const ConfigKey& configKey,
int64_t subscriberId) {
lock_guard<std::mutex> lock(mLock);
auto subscriberMapIt = mIntentMap.find(configKey);
if (subscriberMapIt == mIntentMap.end()) {
return nullptr;
}
auto pirMapIt = subscriberMapIt->second.find(subscriberId);
if (pirMapIt == subscriberMapIt->second.end()) {
return nullptr;
}
return pirMapIt->second;
}
void getStatsDimensionsValueHelper(const vector<FieldValue>& dims, size_t* index, int depth,
int prefix, vector<StatsDimensionsValue>* output) {
size_t count = dims.size();

View File

@@ -16,6 +16,7 @@
#pragma once
#include <android/os/IPendingIntentRef.h>
#include <android/os/IStatsCompanionService.h>
#include <utils/RefBase.h>
@@ -46,24 +47,12 @@ public:
SubscriberReporter(SubscriberReporter const&) = delete;
void operator=(SubscriberReporter const&) = delete;
/**
* Tells SubscriberReporter what IStatsCompanionService to use.
* May be nullptr, but SubscriberReporter will not send broadcasts for any calls
* to alertBroadcastSubscriber that occur while nullptr.
*/
void setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) {
std::lock_guard<std::mutex> lock(mLock);
sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
mStatsCompanionService = statsCompanionService;
}
/**
* Stores the given intentSender, associating it with the given (configKey, subscriberId) pair.
* intentSender must be convertible into an IntentSender (in Java) using IntentSender(IBinder).
*/
void setBroadcastSubscriber(const ConfigKey& configKey,
int64_t subscriberId,
const sp<android::IBinder>& intentSender);
const sp<IPendingIntentRef>& pir);
/**
* Erases any intentSender information from the given (configKey, subscriberId) pair.
@@ -82,6 +71,8 @@ public:
const Subscription& subscription,
const MetricDimensionKey& dimKey) const;
sp<IPendingIntentRef> getBroadcastSubscriber(const ConfigKey& configKey, int64_t subscriberId);
static StatsDimensionsValue getStatsDimensionsValue(const HashableDimensionKey& dim);
private:
@@ -92,15 +83,15 @@ private:
/** Binder interface for communicating with StatsCompanionService. */
sp<IStatsCompanionService> mStatsCompanionService = nullptr;
/** Maps <ConfigKey, SubscriberId> -> IBinder (which represents an IIntentSender). */
/** Maps <ConfigKey, SubscriberId> -> IPendingIntentRef (which represents a PendingIntent). */
std::unordered_map<ConfigKey,
std::unordered_map<int64_t, sp<android::IBinder>>> mIntentMap;
std::unordered_map<int64_t, sp<IPendingIntentRef>>> mIntentMap;
/**
* Sends a broadcast via the given intentSender (using mStatsCompanionService), along
* with the information in the other parameters.
*/
void sendBroadcastLocked(const sp<android::IBinder>& intentSender,
void sendBroadcastLocked(const sp<IPendingIntentRef>& pir,
const ConfigKey& configKey,
const Subscription& subscription,
const std::vector<String16>& cookies,

View File

@@ -255,18 +255,17 @@ public final class StatsManager {
throws StatsUnavailableException {
synchronized (sLock) {
try {
IStatsd service = getIStatsdLocked();
IStatsManagerService service = getIStatsManagerServiceLocked();
if (pendingIntent != null) {
// Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
IBinder intentSender = pendingIntent.getTarget().asBinder();
service.setBroadcastSubscriber(configKey, subscriberId, intentSender,
service.setBroadcastSubscriber(configKey, subscriberId, pendingIntent,
mContext.getOpPackageName());
} else {
service.unsetBroadcastSubscriber(configKey, subscriberId,
mContext.getOpPackageName());
}
} catch (RemoteException e) {
Slog.e(TAG, "Failed to connect to statsd when adding broadcast subscriber", e);
Slog.e(TAG, "Failed to connect to statsmanager when adding broadcast subscriber",
e);
throw new StatsUnavailableException("could not connect", e);
} catch (SecurityException e) {
throw new StatsUnavailableException(e.getMessage(), e);