669 lines
24 KiB
Java
669 lines
24 KiB
Java
/*
|
|
* Copyright (C) 2019 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.server.stats;
|
|
|
|
import static com.android.server.stats.StatsCompanion.PendingIntentRef;
|
|
|
|
import android.Manifest;
|
|
import android.annotation.Nullable;
|
|
import android.app.AppOpsManager;
|
|
import android.app.PendingIntent;
|
|
import android.content.Context;
|
|
import android.os.Binder;
|
|
import android.os.IPullAtomCallback;
|
|
import android.os.IStatsManagerService;
|
|
import android.os.IStatsd;
|
|
import android.os.PowerManager;
|
|
import android.os.Process;
|
|
import android.os.RemoteException;
|
|
import android.util.ArrayMap;
|
|
import android.util.Log;
|
|
|
|
import com.android.internal.annotations.GuardedBy;
|
|
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
|
|
/**
|
|
* Service for {@link android.app.StatsManager}.
|
|
*
|
|
* @hide
|
|
*/
|
|
public class StatsManagerService extends IStatsManagerService.Stub {
|
|
|
|
private static final String TAG = "StatsManagerService";
|
|
private static final boolean DEBUG = false;
|
|
|
|
private static final int STATSD_TIMEOUT_MILLIS = 5000;
|
|
|
|
private static final String USAGE_STATS_PERMISSION_OPS = "android:get_usage_stats";
|
|
|
|
@GuardedBy("mLock")
|
|
private IStatsd mStatsd;
|
|
private final Object mLock = new Object();
|
|
|
|
private StatsCompanionService mStatsCompanionService;
|
|
private Context mContext;
|
|
|
|
@GuardedBy("mLock")
|
|
private ArrayMap<ConfigKey, PendingIntentRef> mDataFetchPirMap = new ArrayMap<>();
|
|
@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();
|
|
mContext = context;
|
|
}
|
|
|
|
private static class ConfigKey {
|
|
private final int mUid;
|
|
private final long mConfigId;
|
|
|
|
ConfigKey(int uid, long configId) {
|
|
mUid = uid;
|
|
mConfigId = configId;
|
|
}
|
|
|
|
public int getUid() {
|
|
return mUid;
|
|
}
|
|
|
|
public long getConfigId() {
|
|
return mConfigId;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return Objects.hash(mUid, mConfigId);
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object obj) {
|
|
if (obj instanceof ConfigKey) {
|
|
ConfigKey other = (ConfigKey) obj;
|
|
return this.mUid == other.getUid() && this.mConfigId == other.getConfigId();
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static class PullerKey {
|
|
private final int mUid;
|
|
private final int mAtomTag;
|
|
|
|
PullerKey(int uid, int atom) {
|
|
mUid = uid;
|
|
mAtomTag = atom;
|
|
}
|
|
|
|
public int getUid() {
|
|
return mUid;
|
|
}
|
|
|
|
public int getAtom() {
|
|
return mAtomTag;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return Objects.hash(mUid, mAtomTag);
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object obj) {
|
|
if (obj instanceof PullerKey) {
|
|
PullerKey other = (PullerKey) obj;
|
|
return this.mUid == other.getUid() && this.mAtomTag == other.getAtom();
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static class PullerValue {
|
|
private final long mCoolDownMillis;
|
|
private final long mTimeoutMillis;
|
|
private final int[] mAdditiveFields;
|
|
private final IPullAtomCallback mCallback;
|
|
|
|
PullerValue(long coolDownMillis, long timeoutMillis, int[] additiveFields,
|
|
IPullAtomCallback callback) {
|
|
mCoolDownMillis = coolDownMillis;
|
|
mTimeoutMillis = timeoutMillis;
|
|
mAdditiveFields = additiveFields;
|
|
mCallback = callback;
|
|
}
|
|
|
|
public long getCoolDownMillis() {
|
|
return mCoolDownMillis;
|
|
}
|
|
|
|
public long getTimeoutMillis() {
|
|
return mTimeoutMillis;
|
|
}
|
|
|
|
public int[] getAdditiveFields() {
|
|
return mAdditiveFields;
|
|
}
|
|
|
|
public IPullAtomCallback getCallback() {
|
|
return mCallback;
|
|
}
|
|
}
|
|
|
|
private final ArrayMap<PullerKey, PullerValue> mPullers = new ArrayMap<>();
|
|
|
|
@Override
|
|
public void registerPullAtomCallback(int atomTag, long coolDownMillis, long timeoutMillis,
|
|
int[] additiveFields, IPullAtomCallback pullerCallback) {
|
|
enforceRegisterStatsPullAtomPermission();
|
|
if (pullerCallback == null) {
|
|
Log.w(TAG, "Puller callback is null for atom " + atomTag);
|
|
return;
|
|
}
|
|
int callingUid = Binder.getCallingUid();
|
|
PullerKey key = new PullerKey(callingUid, atomTag);
|
|
PullerValue val =
|
|
new PullerValue(coolDownMillis, timeoutMillis, additiveFields, pullerCallback);
|
|
|
|
// Always cache the puller in StatsManagerService. If statsd is down, we will register the
|
|
// puller when statsd comes back up.
|
|
synchronized (mLock) {
|
|
mPullers.put(key, val);
|
|
}
|
|
|
|
IStatsd statsd = getStatsdNonblocking();
|
|
if (statsd == null) {
|
|
return;
|
|
}
|
|
|
|
final long token = Binder.clearCallingIdentity();
|
|
try {
|
|
statsd.registerPullAtomCallback(callingUid, atomTag, coolDownMillis, timeoutMillis,
|
|
additiveFields, pullerCallback);
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to access statsd to register puller for atom " + atomTag);
|
|
} finally {
|
|
Binder.restoreCallingIdentity(token);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void unregisterPullAtomCallback(int atomTag) {
|
|
enforceRegisterStatsPullAtomPermission();
|
|
int callingUid = Binder.getCallingUid();
|
|
PullerKey key = new PullerKey(callingUid, atomTag);
|
|
|
|
// Always remove the puller from StatsManagerService even if statsd is down. When statsd
|
|
// comes back up, we will not re-register the removed puller.
|
|
synchronized (mLock) {
|
|
mPullers.remove(key);
|
|
}
|
|
|
|
IStatsd statsd = getStatsdNonblocking();
|
|
if (statsd == null) {
|
|
return;
|
|
}
|
|
|
|
final long token = Binder.clearCallingIdentity();
|
|
try {
|
|
statsd.unregisterPullAtomCallback(callingUid, atomTag);
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to access statsd to unregister puller for atom " + atomTag);
|
|
} finally {
|
|
Binder.restoreCallingIdentity(token);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setDataFetchOperation(long configId, PendingIntent pendingIntent,
|
|
String packageName) {
|
|
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) {
|
|
mDataFetchPirMap.put(key, pir);
|
|
}
|
|
try {
|
|
IStatsd statsd = getStatsdNonblocking();
|
|
if (statsd != null) {
|
|
statsd.setDataFetchOperation(configId, pir, callingUid);
|
|
}
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to setDataFetchOperation with statsd");
|
|
} finally {
|
|
Binder.restoreCallingIdentity(token);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void removeDataFetchOperation(long configId, String packageName) {
|
|
enforceDumpAndUsageStatsPermission(packageName);
|
|
int callingUid = Binder.getCallingUid();
|
|
final long token = Binder.clearCallingIdentity();
|
|
ConfigKey key = new ConfigKey(callingUid, configId);
|
|
synchronized (mLock) {
|
|
mDataFetchPirMap.remove(key);
|
|
}
|
|
try {
|
|
IStatsd statsd = getStatsdNonblocking();
|
|
if (statsd != null) {
|
|
statsd.removeDataFetchOperation(configId, callingUid);
|
|
}
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to removeDataFetchOperation with statsd");
|
|
} finally {
|
|
Binder.restoreCallingIdentity(token);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public long[] setActiveConfigsChangedOperation(PendingIntent pendingIntent,
|
|
String packageName) {
|
|
enforceDumpAndUsageStatsPermission(packageName);
|
|
int callingUid = Binder.getCallingUid();
|
|
final long token = Binder.clearCallingIdentity();
|
|
PendingIntentRef pir = new PendingIntentRef(pendingIntent, mContext);
|
|
// We add the PIR to a map so we can reregister if statsd is unavailable.
|
|
synchronized (mLock) {
|
|
mActiveConfigsPirMap.put(callingUid, pir);
|
|
}
|
|
try {
|
|
IStatsd statsd = getStatsdNonblocking();
|
|
if (statsd != null) {
|
|
return statsd.setActiveConfigsChangedOperation(pir, callingUid);
|
|
}
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to setActiveConfigsChangedOperation with statsd");
|
|
} finally {
|
|
Binder.restoreCallingIdentity(token);
|
|
}
|
|
return new long[] {};
|
|
}
|
|
|
|
@Override
|
|
public void removeActiveConfigsChangedOperation(String packageName) {
|
|
enforceDumpAndUsageStatsPermission(packageName);
|
|
int callingUid = Binder.getCallingUid();
|
|
final long token = Binder.clearCallingIdentity();
|
|
synchronized (mLock) {
|
|
mActiveConfigsPirMap.remove(callingUid);
|
|
}
|
|
try {
|
|
IStatsd statsd = getStatsdNonblocking();
|
|
if (statsd != null) {
|
|
statsd.removeActiveConfigsChangedOperation(callingUid);
|
|
}
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to removeActiveConfigsChangedOperation with statsd");
|
|
} finally {
|
|
Binder.restoreCallingIdentity(token);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setBroadcastSubscriber(long configId, long subscriberId,
|
|
PendingIntent pendingIntent, String packageName) {
|
|
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) {
|
|
Log.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) {
|
|
Log.e(TAG, "Failed to unsetBroadcastSubscriber with statsd");
|
|
} finally {
|
|
Binder.restoreCallingIdentity(token);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public long[] getRegisteredExperimentIds() throws IllegalStateException {
|
|
enforceDumpAndUsageStatsPermission(null);
|
|
final long token = Binder.clearCallingIdentity();
|
|
try {
|
|
IStatsd statsd = waitForStatsd();
|
|
if (statsd != null) {
|
|
return statsd.getRegisteredExperimentIds();
|
|
}
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to getRegisteredExperimentIds with statsd");
|
|
throw new IllegalStateException(e.getMessage(), e);
|
|
} finally {
|
|
Binder.restoreCallingIdentity(token);
|
|
}
|
|
throw new IllegalStateException("Failed to connect to statsd to registerExperimentIds");
|
|
}
|
|
|
|
@Override
|
|
public byte[] getMetadata(String packageName) throws IllegalStateException {
|
|
enforceDumpAndUsageStatsPermission(packageName);
|
|
final long token = Binder.clearCallingIdentity();
|
|
try {
|
|
IStatsd statsd = waitForStatsd();
|
|
if (statsd != null) {
|
|
return statsd.getMetadata();
|
|
}
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to getMetadata with statsd");
|
|
throw new IllegalStateException(e.getMessage(), e);
|
|
} finally {
|
|
Binder.restoreCallingIdentity(token);
|
|
}
|
|
throw new IllegalStateException("Failed to connect to statsd to getMetadata");
|
|
}
|
|
|
|
@Override
|
|
public byte[] getData(long key, String packageName) throws IllegalStateException {
|
|
enforceDumpAndUsageStatsPermission(packageName);
|
|
PowerManager powerManager = (PowerManager)
|
|
mContext.getSystemService(Context.POWER_SERVICE);
|
|
PowerManager.WakeLock wl = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
|
|
/*tag=*/ StatsManagerService.class.getCanonicalName());
|
|
int callingUid = Binder.getCallingUid();
|
|
final long token = Binder.clearCallingIdentity();
|
|
wl.acquire();
|
|
try {
|
|
IStatsd statsd = waitForStatsd();
|
|
if (statsd != null) {
|
|
return statsd.getData(key, callingUid);
|
|
}
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to getData with statsd");
|
|
throw new IllegalStateException(e.getMessage(), e);
|
|
} finally {
|
|
wl.release();
|
|
Binder.restoreCallingIdentity(token);
|
|
}
|
|
throw new IllegalStateException("Failed to connect to statsd to getData");
|
|
}
|
|
|
|
@Override
|
|
public void addConfiguration(long configId, byte[] config, String packageName)
|
|
throws IllegalStateException {
|
|
enforceDumpAndUsageStatsPermission(packageName);
|
|
int callingUid = Binder.getCallingUid();
|
|
final long token = Binder.clearCallingIdentity();
|
|
try {
|
|
IStatsd statsd = waitForStatsd();
|
|
if (statsd != null) {
|
|
statsd.addConfiguration(configId, config, callingUid);
|
|
return;
|
|
}
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to addConfiguration with statsd");
|
|
throw new IllegalStateException(e.getMessage(), e);
|
|
} finally {
|
|
Binder.restoreCallingIdentity(token);
|
|
}
|
|
throw new IllegalStateException("Failed to connect to statsd to addConfig");
|
|
}
|
|
|
|
@Override
|
|
public void removeConfiguration(long configId, String packageName)
|
|
throws IllegalStateException {
|
|
enforceDumpAndUsageStatsPermission(packageName);
|
|
int callingUid = Binder.getCallingUid();
|
|
final long token = Binder.clearCallingIdentity();
|
|
try {
|
|
IStatsd statsd = waitForStatsd();
|
|
if (statsd != null) {
|
|
statsd.removeConfiguration(configId, callingUid);
|
|
return;
|
|
}
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to removeConfiguration with statsd");
|
|
throw new IllegalStateException(e.getMessage(), e);
|
|
} finally {
|
|
Binder.restoreCallingIdentity(token);
|
|
}
|
|
throw new IllegalStateException("Failed to connect to statsd to removeConfig");
|
|
}
|
|
|
|
void setStatsCompanionService(StatsCompanionService statsCompanionService) {
|
|
mStatsCompanionService = statsCompanionService;
|
|
}
|
|
|
|
/**
|
|
* Checks that the caller has both DUMP and PACKAGE_USAGE_STATS permissions. Also checks that
|
|
* the caller has USAGE_STATS_PERMISSION_OPS for the specified packageName if it is not null.
|
|
*
|
|
* @param packageName The packageName to check USAGE_STATS_PERMISSION_OPS.
|
|
*/
|
|
private void enforceDumpAndUsageStatsPermission(@Nullable String packageName) {
|
|
int callingUid = Binder.getCallingUid();
|
|
int callingPid = Binder.getCallingPid();
|
|
|
|
if (callingPid == Process.myPid()) {
|
|
return;
|
|
}
|
|
|
|
mContext.enforceCallingPermission(Manifest.permission.DUMP, null);
|
|
mContext.enforceCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS, null);
|
|
|
|
if (packageName == null) {
|
|
return;
|
|
}
|
|
AppOpsManager appOpsManager = (AppOpsManager) mContext
|
|
.getSystemService(Context.APP_OPS_SERVICE);
|
|
switch (appOpsManager.noteOp(USAGE_STATS_PERMISSION_OPS,
|
|
Binder.getCallingUid(), packageName, null, null)) {
|
|
case AppOpsManager.MODE_ALLOWED:
|
|
case AppOpsManager.MODE_DEFAULT:
|
|
break;
|
|
default:
|
|
throw new SecurityException(
|
|
String.format("UID %d / PID %d lacks app-op %s",
|
|
callingUid, callingPid, USAGE_STATS_PERMISSION_OPS)
|
|
);
|
|
}
|
|
}
|
|
|
|
private void enforceRegisterStatsPullAtomPermission() {
|
|
mContext.enforceCallingOrSelfPermission(
|
|
android.Manifest.permission.REGISTER_STATS_PULL_ATOM,
|
|
"Need REGISTER_STATS_PULL_ATOM permission.");
|
|
}
|
|
|
|
|
|
/**
|
|
* Clients should call this if blocking until statsd to be ready is desired
|
|
*
|
|
* @return IStatsd object if statsd becomes ready within the timeout, null otherwise.
|
|
*/
|
|
private IStatsd waitForStatsd() {
|
|
synchronized (mLock) {
|
|
if (mStatsd == null) {
|
|
try {
|
|
mLock.wait(STATSD_TIMEOUT_MILLIS);
|
|
} catch (InterruptedException e) {
|
|
Log.e(TAG, "wait for statsd interrupted");
|
|
}
|
|
}
|
|
return mStatsd;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clients should call this to receive a reference to statsd.
|
|
*
|
|
* @return IStatsd object if statsd is ready, null otherwise.
|
|
*/
|
|
private IStatsd getStatsdNonblocking() {
|
|
synchronized (mLock) {
|
|
return mStatsd;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called from {@link StatsCompanionService}.
|
|
*
|
|
* Tells StatsManagerService that Statsd is ready and updates
|
|
* Statsd with the contents of our local cache.
|
|
*/
|
|
void statsdReady(IStatsd statsd) {
|
|
synchronized (mLock) {
|
|
mStatsd = statsd;
|
|
mLock.notify();
|
|
}
|
|
sayHiToStatsd(statsd);
|
|
}
|
|
|
|
/**
|
|
* Called from {@link StatsCompanionService}.
|
|
*
|
|
* Tells StatsManagerService that Statsd is no longer ready
|
|
* and we should no longer make binder calls with statsd.
|
|
*/
|
|
void statsdNotReady() {
|
|
synchronized (mLock) {
|
|
mStatsd = null;
|
|
}
|
|
}
|
|
|
|
private void sayHiToStatsd(IStatsd statsd) {
|
|
if (statsd == null) {
|
|
return;
|
|
}
|
|
|
|
final long token = Binder.clearCallingIdentity();
|
|
try {
|
|
registerAllPullers(statsd);
|
|
registerAllDataFetchOperations(statsd);
|
|
registerAllActiveConfigsChangedOperations(statsd);
|
|
registerAllBroadcastSubscribers(statsd);
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "StatsManager failed to (re-)register data with statsd");
|
|
} finally {
|
|
Binder.restoreCallingIdentity(token);
|
|
}
|
|
}
|
|
|
|
// Pre-condition: the Binder calling identity has already been cleared
|
|
private void registerAllPullers(IStatsd statsd) throws RemoteException {
|
|
// Since we do not want to make an IPC with the lock held, we first create a copy of the
|
|
// data with the lock held before iterating through the map.
|
|
ArrayMap<PullerKey, PullerValue> pullersCopy;
|
|
synchronized (mLock) {
|
|
pullersCopy = new ArrayMap<>(mPullers);
|
|
}
|
|
|
|
for (Map.Entry<PullerKey, PullerValue> entry : pullersCopy.entrySet()) {
|
|
PullerKey key = entry.getKey();
|
|
PullerValue value = entry.getValue();
|
|
statsd.registerPullAtomCallback(key.getUid(), key.getAtom(), value.getCoolDownMillis(),
|
|
value.getTimeoutMillis(), value.getAdditiveFields(), value.getCallback());
|
|
}
|
|
statsd.allPullersFromBootRegistered();
|
|
}
|
|
|
|
// Pre-condition: the Binder calling identity has already been cleared
|
|
private void registerAllDataFetchOperations(IStatsd statsd) throws RemoteException {
|
|
// Since we do not want to make an IPC with the lock held, we first create a copy of the
|
|
// data with the lock held before iterating through the map.
|
|
ArrayMap<ConfigKey, PendingIntentRef> dataFetchCopy;
|
|
synchronized (mLock) {
|
|
dataFetchCopy = new ArrayMap<>(mDataFetchPirMap);
|
|
}
|
|
|
|
for (Map.Entry<ConfigKey, PendingIntentRef> entry : dataFetchCopy.entrySet()) {
|
|
ConfigKey key = entry.getKey();
|
|
statsd.setDataFetchOperation(key.getConfigId(), entry.getValue(), key.getUid());
|
|
}
|
|
}
|
|
|
|
// Pre-condition: the Binder calling identity has already been cleared
|
|
private void registerAllActiveConfigsChangedOperations(IStatsd statsd) throws RemoteException {
|
|
// Since we do not want to make an IPC with the lock held, we first create a copy of the
|
|
// data with the lock held before iterating through the map.
|
|
ArrayMap<Integer, PendingIntentRef> activeConfigsChangedCopy;
|
|
synchronized (mLock) {
|
|
activeConfigsChangedCopy = new ArrayMap<>(mActiveConfigsPirMap);
|
|
}
|
|
|
|
for (Map.Entry<Integer, PendingIntentRef> entry : activeConfigsChangedCopy.entrySet()) {
|
|
statsd.setActiveConfigsChangedOperation(entry.getValue(), entry.getKey());
|
|
}
|
|
}
|
|
|
|
// Pre-condition: the Binder calling identity has already been cleared
|
|
private void registerAllBroadcastSubscribers(IStatsd statsd) throws RemoteException {
|
|
// Since we do not want to make an IPC with the lock held, we first create a deep copy of
|
|
// the data with the lock held before iterating through the map.
|
|
ArrayMap<ConfigKey, ArrayMap<Long, PendingIntentRef>> broadcastSubscriberCopy =
|
|
new ArrayMap<>();
|
|
synchronized (mLock) {
|
|
for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry :
|
|
mBroadcastSubscriberPirMap.entrySet()) {
|
|
broadcastSubscriberCopy.put(entry.getKey(), new ArrayMap(entry.getValue()));
|
|
}
|
|
}
|
|
|
|
for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry :
|
|
mBroadcastSubscriberPirMap.entrySet()) {
|
|
ConfigKey configKey = entry.getKey();
|
|
for (Map.Entry<Long, PendingIntentRef> subscriberEntry : entry.getValue().entrySet()) {
|
|
statsd.setBroadcastSubscriber(configKey.getConfigId(), subscriberEntry.getKey(),
|
|
subscriberEntry.getValue(), configKey.getUid());
|
|
}
|
|
}
|
|
}
|
|
}
|