diff --git a/api/system-current.txt b/api/system-current.txt index 31e6c6c04386a..e5ae9a7002b45 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -508,11 +508,13 @@ package android.app { method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getStatsMetadata() throws android.app.StatsManager.StatsUnavailableException; method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void removeConfig(long) throws android.app.StatsManager.StatsUnavailableException; method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean removeConfiguration(long); + method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void setActiveConfigsChangedOperation(@Nullable android.app.PendingIntent) throws android.app.StatsManager.StatsUnavailableException; method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void setBroadcastSubscriber(android.app.PendingIntent, long, long) throws android.app.StatsManager.StatsUnavailableException; method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setBroadcastSubscriber(long, long, android.app.PendingIntent); method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setDataFetchOperation(long, android.app.PendingIntent); method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void setFetchReportsOperation(android.app.PendingIntent, long) throws android.app.StatsManager.StatsUnavailableException; field public static final String ACTION_STATSD_STARTED = "android.app.action.STATSD_STARTED"; + field public static final String EXTRA_STATS_ACTIVE_CONFIG_KEYS = "android.app.extra.STATS_ACTIVE_CONFIG_KEYS"; field public static final String EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES = "android.app.extra.STATS_BROADCAST_SUBSCRIBER_COOKIES"; field public static final String EXTRA_STATS_CONFIG_KEY = "android.app.extra.STATS_CONFIG_KEY"; field public static final String EXTRA_STATS_CONFIG_UID = "android.app.extra.STATS_CONFIG_UID"; diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 9c320d3e2b037..b26c713877dbe 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -989,6 +989,25 @@ Status StatsService::setDataFetchOperation(int64_t key, return Status::ok(); } +Status StatsService::setActiveConfigsChangedOperation(const sp& intentSender, + const String16& packageName, + vector* output) { + ENFORCE_DUMP_AND_USAGE_STATS(packageName); + + IPCThreadState* ipc = IPCThreadState::self(); + mConfigManager->SetActiveConfigsChangedReceiver(ipc->getCallingUid(), intentSender); + //TODO: Return the list of configs that are already active + return Status::ok(); +} + +Status StatsService::removeActiveConfigsChangedOperation(const String16& packageName) { + ENFORCE_DUMP_AND_USAGE_STATS(packageName); + + IPCThreadState* ipc = IPCThreadState::self(); + mConfigManager->RemoveActiveConfigsChangedReceiver(ipc->getCallingUid()); + return Status::ok(); +} + Status StatsService::removeConfiguration(int64_t key, const String16& packageName) { ENFORCE_DUMP_AND_USAGE_STATS(packageName); diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index fe0504fc034f0..cdff50fcb62e8 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -131,6 +131,17 @@ public: virtual Status removeDataFetchOperation(int64_t key, const String16& packageName) override; + /** + * Binder call to let clients register the active configs changed operation. + */ + virtual Status setActiveConfigsChangedOperation(const sp& intentSender, + const String16& packageName, + vector* output) override; + + /** + * Binder call to remove the active configs changed operation for the specified package.. + */ + virtual Status removeActiveConfigsChangedOperation(const String16& packageName) override; /** * Binder call to allow clients to remove the specified configuration. */ diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp index 5fea90b61c80b..aa22333ab26c4 100644 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -128,6 +128,17 @@ void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) { mConfigReceivers.erase(key); } +void ConfigManager::SetActiveConfigsChangedReceiver(const int uid, + const sp& intentSender) { + lock_guard lock(mMutex); + mActiveConfigsChangedReceivers[uid] = intentSender; +} + +void ConfigManager::RemoveActiveConfigsChangedReceiver(const int uid) { + lock_guard lock(mMutex); + mActiveConfigsChangedReceivers.erase(uid); +} + void ConfigManager::RemoveConfig(const ConfigKey& key) { vector> broadcastList; { @@ -181,6 +192,11 @@ void ConfigManager::RemoveConfigs(int uid) { mConfigReceivers.erase(*it); } + auto itActiveConfigsChangedReceiver = mActiveConfigsChangedReceivers.find(uid); + if (itActiveConfigsChangedReceiver != mActiveConfigsChangedReceivers.end()) { + mActiveConfigsChangedReceivers.erase(itActiveConfigsChangedReceiver); + } + mConfigs.erase(uidIt); for (const sp& listener : mListeners) { @@ -213,6 +229,7 @@ void ConfigManager::RemoveAllConfigs() { } mConfigReceivers.clear(); + mActiveConfigsChangedReceivers.clear(); for (const sp& listener : mListeners) { broadcastList.push_back(listener); } @@ -250,6 +267,17 @@ const sp ConfigManager::GetConfigReceiver(const ConfigKey& key } } +const sp ConfigManager::GetActiveConfigsChangedReceiver(const int uid) const { + lock_guard lock(mMutex); + + auto it = mActiveConfigsChangedReceivers.find(uid); + if (it == mActiveConfigsChangedReceivers.end()) { + return nullptr; + } else { + return it->second; + } +} + void ConfigManager::Dump(FILE* out) { lock_guard lock(mMutex); diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h index 122e669057b02..c064a519f597b 100644 --- a/cmds/statsd/src/config/ConfigManager.h +++ b/cmds/statsd/src/config/ConfigManager.h @@ -81,6 +81,23 @@ public: */ void RemoveConfigReceiver(const ConfigKey& key); + /** + * Sets the broadcast receiver that is notified whenever the list of active configs + * changes for this uid. + */ + void SetActiveConfigsChangedReceiver(const int uid, const sp& intentSender); + + /** + * Returns the broadcast receiver for active configs changed for this uid. + */ + + const sp GetActiveConfigsChangedReceiver(const int uid) const; + + /** + * Erase any active configs changed broadcast receiver associated with this uid. + */ + void RemoveActiveConfigsChangedReceiver(const int uid); + /** * A configuration was removed. * @@ -129,6 +146,12 @@ private: */ std::map> 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 IBinder from PendingIntent. + */ + std::map> mActiveConfigsChangedReceivers; + /** * The ConfigListeners that will be told about changes. */ diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java index 9dcd1228522b5..9b66c928abc0b 100644 --- a/core/java/android/app/StatsManager.java +++ b/core/java/android/app/StatsManager.java @@ -73,6 +73,11 @@ public final class StatsManager { */ public static final String EXTRA_STATS_DIMENSIONS_VALUE = "android.app.extra.STATS_DIMENSIONS_VALUE"; + /** + * Long array extra of the active configs for the uid that added those configs. + */ + public static final String EXTRA_STATS_ACTIVE_CONFIG_KEYS = + "android.app.extra.STATS_ACTIVE_CONFIG_KEYS"; /** * Broadcast Action: Statsd has started. @@ -274,6 +279,43 @@ public final class StatsManager { } } + /** + * Registers the operation that is called whenever there is a change in which configs are + * active. This must be called each time statsd starts. This operation allows + * statsd to inform clients that they should pull data of the configs that are currently + * active. The activeConfigsChangedOperation should set periodic alarms to pull data of configs + * that are active and stop pulling data of configs that are no longer active. + * + * @param pendingIntent the PendingIntent to use when broadcasting info to the subscriber + * associated with the given subscriberId. May be null, in which case + * it removes any associated pending intent for this client. + * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service + */ + @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) + public void setActiveConfigsChangedOperation(@Nullable PendingIntent pendingIntent) + throws StatsUnavailableException { + synchronized (this) { + try { + IStatsManager service = getIStatsManagerLocked(); + if (pendingIntent == null) { + service.removeActiveConfigsChangedOperation(mContext.getOpPackageName()); + } else { + // Extracts IIntentSender from the PendingIntent and turns it into an IBinder. + IBinder intentSender = pendingIntent.getTarget().asBinder(); + service.setActiveConfigsChangedOperation(intentSender, + mContext.getOpPackageName()); + } + + } catch (RemoteException e) { + Slog.e(TAG, + "Failed to connect to statsd when registering active configs listener."); + throw new StatsUnavailableException("could not connect", e); + } catch (SecurityException e) { + throw new StatsUnavailableException(e.getMessage(), e); + } + } + } + // TODO: Temporary for backwards compatibility. Remove. /** * @deprecated Use {@link #setFetchReportsOperation(PendingIntent, long)} diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl index 74d434c517814..93d6f4c12128f 100644 --- a/core/java/android/os/IStatsManager.aidl +++ b/core/java/android/os/IStatsManager.aidl @@ -118,6 +118,23 @@ interface IStatsManager { */ void removeDataFetchOperation(long configKey, in String packageName); + /** + * Registers the given pending intent for this packagename. This intent is invoked when the + * active status of any of the configs sent by this package changes and will contain a list of + * config ids that are currently active. It also returns the list of configs that are currently + * active. There can be at most one active configs changed listener per package. + * + * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS. + */ + long[] setActiveConfigsChangedOperation(in IBinder intentSender, in String packageName); + + /** + * Removes the active configs changed operation for the specified package name. + * + * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS. + */ + void removeActiveConfigsChangedOperation(in String packageName); + /** * Removes the configuration with the matching config key. No-op if this config key does not * exist.