Merge "Cleanup threading in StatsCompanionService" into rvc-dev

This commit is contained in:
Jeffrey Huang
2020-03-10 17:59:27 +00:00
committed by Android (Google) Code Review

View File

@@ -54,6 +54,7 @@ import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -102,9 +103,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
private final OnAlarmListener mAnomalyAlarmListener = new AnomalyAlarmListener();
private final OnAlarmListener mPullingAlarmListener = new PullingAlarmListener();
private final OnAlarmListener mPeriodicAlarmListener = new PeriodicAlarmListener();
private final BroadcastReceiver mAppUpdateReceiver;
private final BroadcastReceiver mUserUpdateReceiver;
private final ShutdownEventReceiver mShutdownEventReceiver;
private StatsManagerService mStatsManagerService;
@@ -118,27 +116,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
super();
mContext = context;
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
mAppUpdateReceiver = new AppUpdateReceiver();
mUserUpdateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (sStatsdLock) {
if (sStatsd == null) {
Log.w(TAG, "Could not access statsd for UserUpdateReceiver");
return;
}
try {
// Pull the latest state of UID->app name, version mapping.
// Needed since the new user basically has a version of every app.
informAllUidsLocked(context);
} catch (RemoteException e) {
Log.e(TAG, "Failed to inform statsd latest update of all apps", e);
forgetEverythingLocked();
}
}
}
};
mShutdownEventReceiver = new ShutdownEventReceiver();
if (DEBUG) Log.d(TAG, "Registered receiver for ACTION_PACKAGE_REPLACED and ADDED.");
HandlerThread handlerThread = new HandlerThread(TAG);
handlerThread.start();
@@ -162,9 +139,18 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
return ret;
}
// Assumes that sStatsdLock is held.
@GuardedBy("sStatsdLock")
private void informAllUidsLocked(Context context) throws RemoteException {
/**
* Non-blocking call to retrieve a reference to statsd
*
* @return IStatsd object if statsd is ready, null otherwise.
*/
private static IStatsd getStatsdNonblocking() {
synchronized (sStatsdLock) {
return sStatsd;
}
}
private static void informAllUidsLocked(Context context) throws RemoteException {
UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
PackageManager pm = context.getPackageManager();
final List<UserHandle> users = um.getUserHandles(true);
@@ -273,7 +259,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
if (!replacing) {
// Don't bother sending an update if we're right about to get another
// intent for the new version that's added.
PackageManager pm = context.getPackageManager();
String app = intent.getData().getSchemeSpecificPart();
sStatsd.informOnePackageRemoved(app, uid);
}
@@ -303,23 +288,43 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
}
public final static class AnomalyAlarmListener implements OnAlarmListener {
private static final class UserUpdateReceiver extends BroadcastReceiver {
@Override
public void onAlarm() {
Log.i(TAG, "StatsCompanionService believes an anomaly has occurred at time "
+ System.currentTimeMillis() + "ms.");
public void onReceive(Context context, Intent intent) {
synchronized (sStatsdLock) {
if (sStatsd == null) {
Log.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
Log.w(TAG, "Could not access statsd for UserUpdateReceiver");
return;
}
try {
// Two-way call to statsd to retain AlarmManager wakelock
sStatsd.informAnomalyAlarmFired();
// Pull the latest state of UID->app name, version mapping.
// Needed since the new user basically has a version of every app.
informAllUidsLocked(context);
} catch (RemoteException e) {
Log.w(TAG, "Failed to inform statsd of anomaly alarm firing", e);
Log.e(TAG, "Failed to inform statsd latest update of all apps", e);
}
}
}
}
public static final class AnomalyAlarmListener implements OnAlarmListener {
@Override
public void onAlarm() {
if (DEBUG) {
Log.i(TAG, "StatsCompanionService believes an anomaly has occurred at time "
+ System.currentTimeMillis() + "ms.");
}
IStatsd statsd = getStatsdNonblocking();
if (statsd == null) {
Log.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
return;
}
try {
// Two-way call to statsd to retain AlarmManager wakelock
statsd.informAnomalyAlarmFired();
} catch (RemoteException e) {
Log.w(TAG, "Failed to inform statsd of anomaly alarm firing", e);
}
// AlarmManager releases its own wakelock here.
}
}
@@ -330,17 +335,16 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
if (DEBUG) {
Log.d(TAG, "Time to poll something.");
}
synchronized (sStatsdLock) {
if (sStatsd == null) {
Log.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
return;
}
try {
// Two-way call to statsd to retain AlarmManager wakelock
sStatsd.informPollAlarmFired();
} catch (RemoteException e) {
Log.w(TAG, "Failed to inform statsd of pulling alarm firing.", e);
}
IStatsd statsd = getStatsdNonblocking();
if (statsd == null) {
Log.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
return;
}
try {
// Two-way call to statsd to retain AlarmManager wakelock
statsd.informPollAlarmFired();
} catch (RemoteException e) {
Log.w(TAG, "Failed to inform statsd of pulling alarm firing.", e);
}
}
}
@@ -351,17 +355,16 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
if (DEBUG) {
Log.d(TAG, "Time to trigger periodic alarm.");
}
synchronized (sStatsdLock) {
if (sStatsd == null) {
Log.w(TAG, "Could not access statsd to inform it of periodic alarm firing.");
return;
}
try {
// Two-way call to statsd to retain AlarmManager wakelock
sStatsd.informAlarmForSubscriberTriggeringFired();
} catch (RemoteException e) {
Log.w(TAG, "Failed to inform statsd of periodic alarm firing.", e);
}
IStatsd statsd = getStatsdNonblocking();
if (statsd == null) {
Log.w(TAG, "Could not access statsd to inform it of periodic alarm firing.");
return;
}
try {
// Two-way call to statsd to retain AlarmManager wakelock
statsd.informAlarmForSubscriberTriggeringFired();
} catch (RemoteException e) {
Log.w(TAG, "Failed to inform statsd of periodic alarm firing.", e);
}
// AlarmManager releases its own wakelock here.
}
@@ -379,17 +382,19 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
return;
}
Log.i(TAG, "StatsCompanionService noticed a shutdown.");
synchronized (sStatsdLock) {
if (sStatsd == null) {
Log.w(TAG, "Could not access statsd to inform it of a shutdown event.");
return;
}
try {
sStatsd.informDeviceShutdown();
} catch (Exception e) {
Log.w(TAG, "Failed to inform statsd of a shutdown event.", e);
}
if (DEBUG) {
Log.i(TAG, "StatsCompanionService noticed a shutdown.");
}
IStatsd statsd = getStatsdNonblocking();
if (statsd == null) {
Log.w(TAG, "Could not access statsd to inform it of a shutdown event.");
return;
}
try {
// two way binder call
statsd.informDeviceShutdown();
} catch (Exception e) {
Log.w(TAG, "Failed to inform statsd of a shutdown event.", e);
}
}
}
@@ -515,7 +520,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
}
@Override
@Override // Binder call
public void triggerUidSnapshot() {
StatsCompanion.enforceStatsdCallingUid();
synchronized (sStatsdLock) {
@@ -525,7 +530,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
} catch (RemoteException e) {
Log.e(TAG, "Failed to trigger uid snapshot.", e);
} finally {
restoreCallingIdentity(token);
Binder.restoreCallingIdentity(token);
}
}
}
@@ -539,15 +544,28 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
// Statsd related code
/**
* Fetches the statsd IBinder service.
* Note: This should only be called from sayHiToStatsd. All other clients should use the cached
* sStatsd with a null check.
* Fetches the statsd IBinder service. This is a blocking call.
* Note: This should only be called from {@link #sayHiToStatsd()}. All other clients should use
* the cached sStatsd via {@link #getStatsdNonblocking()}.
*/
private static IStatsd fetchStatsdService() {
return IStatsd.Stub.asInterface(StatsFrameworkInitializer
.getStatsServiceManager()
.getStatsdServiceRegisterer()
.get());
private IStatsd fetchStatsdService(StatsdDeathRecipient deathRecipient) {
synchronized (sStatsdLock) {
if (sStatsd == null) {
sStatsd = IStatsd.Stub.asInterface(StatsFrameworkInitializer
.getStatsServiceManager()
.getStatsdServiceRegisterer()
.get());
if (sStatsd != null) {
try {
sStatsd.asBinder().linkToDeath(deathRecipient, /* flags */ 0);
} catch (RemoteException e) {
Log.e(TAG, "linkToDeath(StatsdDeathRecipient) failed");
statsdNotReadyLocked();
}
}
}
return sStatsd;
}
}
/**
@@ -567,67 +585,84 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
* statsd.
*/
private void sayHiToStatsd() {
synchronized (sStatsdLock) {
if (sStatsd != null) {
Log.e(TAG, "Trying to fetch statsd, but it was already fetched",
new IllegalStateException(
"sStatsd is not null when being fetched"));
return;
}
sStatsd = fetchStatsdService();
if (sStatsd == null) {
Log.i(TAG,
"Could not yet find statsd to tell it that StatsCompanion is "
+ "alive.");
return;
}
mStatsManagerService.statsdReady(sStatsd);
if (DEBUG) Log.d(TAG, "Saying hi to statsd");
if (getStatsdNonblocking() != null) {
Log.e(TAG, "Trying to fetch statsd, but it was already fetched",
new IllegalStateException(
"sStatsd is not null when being fetched"));
return;
}
StatsdDeathRecipient deathRecipient = new StatsdDeathRecipient();
IStatsd statsd = fetchStatsdService(deathRecipient);
if (statsd == null) {
Log.i(TAG,
"Could not yet find statsd to tell it that StatsCompanion is "
+ "alive.");
return;
}
mStatsManagerService.statsdReady(statsd);
if (DEBUG) Log.d(TAG, "Saying hi to statsd");
try {
statsd.statsCompanionReady();
cancelAnomalyAlarm();
cancelPullingAlarm();
BroadcastReceiver appUpdateReceiver = new AppUpdateReceiver();
BroadcastReceiver userUpdateReceiver = new UserUpdateReceiver();
BroadcastReceiver shutdownEventReceiver = new ShutdownEventReceiver();
// Setup broadcast receiver for updates.
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED);
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
mContext.registerReceiverForAllUsers(appUpdateReceiver, filter, null, null);
// Setup receiver for user initialize (which happens once for a new user)
// and
// if a user is removed.
filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
filter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiverForAllUsers(userUpdateReceiver, filter, null, null);
// Setup receiver for device reboots or shutdowns.
filter = new IntentFilter(Intent.ACTION_REBOOT);
filter.addAction(Intent.ACTION_SHUTDOWN);
mContext.registerReceiverForAllUsers(
shutdownEventReceiver, filter, null, null);
// Only add the receivers if the registration is successful.
deathRecipient.addRegisteredBroadcastReceivers(
List.of(appUpdateReceiver, userUpdateReceiver, shutdownEventReceiver));
final long token = Binder.clearCallingIdentity();
try {
sStatsd.statsCompanionReady();
// If the statsCompanionReady two-way binder call returns, link to statsd.
try {
sStatsd.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
} catch (RemoteException e) {
Log.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e);
forgetEverythingLocked();
}
// Setup broadcast receiver for updates.
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED);
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
mContext.registerReceiverForAllUsers(mAppUpdateReceiver, filter, null, null);
// Setup receiver for user initialize (which happens once for a new user)
// and
// if a user is removed.
filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
filter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiverForAllUsers(mUserUpdateReceiver, filter, null, null);
// Setup receiver for device reboots or shutdowns.
filter = new IntentFilter(Intent.ACTION_REBOOT);
filter.addAction(Intent.ACTION_SHUTDOWN);
mContext.registerReceiverForAllUsers(
mShutdownEventReceiver, filter, null, null);
final long token = Binder.clearCallingIdentity();
try {
// Pull the latest state of UID->app name, version mapping when
// statsd starts.
informAllUidsLocked(mContext);
} finally {
restoreCallingIdentity(token);
}
Log.i(TAG, "Told statsd that StatsCompanionService is alive.");
} catch (RemoteException e) {
Log.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
forgetEverythingLocked();
// Pull the latest state of UID->app name, version mapping when
// statsd starts.
informAllUidsLocked(mContext);
} finally {
Binder.restoreCallingIdentity(token);
}
Log.i(TAG, "Told statsd that StatsCompanionService is alive.");
} catch (RemoteException e) {
Log.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
}
}
private class StatsdDeathRecipient implements IBinder.DeathRecipient {
private List<BroadcastReceiver> mReceiversToUnregister;
StatsdDeathRecipient() {
mReceiversToUnregister = new ArrayList<>();
}
public void addRegisteredBroadcastReceivers(List<BroadcastReceiver> receivers) {
synchronized (sStatsdLock) {
mReceiversToUnregister.addAll(receivers);
}
}
@Override
public void binderDied() {
Log.i(TAG, "Statsd is dead - erase all my knowledge, except pullers");
@@ -656,20 +691,18 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
}
}
forgetEverythingLocked();
// We only unregister in binder death becaseu receivers can only be unregistered
// once, or an IllegalArgumentException is thrown.
for (BroadcastReceiver receiver: mReceiversToUnregister) {
mContext.unregisterReceiver(receiver);
}
statsdNotReadyLocked();
}
}
}
@GuardedBy("StatsCompanionService.sStatsdLock")
private void forgetEverythingLocked() {
private void statsdNotReadyLocked() {
sStatsd = null;
mContext.unregisterReceiver(mAppUpdateReceiver);
mContext.unregisterReceiver(mUserUpdateReceiver);
mContext.unregisterReceiver(mShutdownEventReceiver);
cancelAnomalyAlarm();
cancelPullingAlarm();
mStatsManagerService.statsdNotReady();
}