Merge "Add usage to CarAudoFocus dumpsys and logs" into qt-qpr1-dev
diff --git a/car_product/init/init.bootstat.rc b/car_product/init/init.bootstat.rc
index 5c5e796..4122ea4 100644
--- a/car_product/init/init.bootstat.rc
+++ b/car_product/init/init.bootstat.rc
@@ -4,4 +4,4 @@
# This is a common source of Android security bugs.
#
on property:boot.car_service_created=1
- exec - root root -- /system/bin/bootstat -r car_service_created
+ exec - system log -- /system/bin/bootstat -r car_service_created
diff --git a/car_product/overlay/frameworks/base/core/res/res/values/config.xml b/car_product/overlay/frameworks/base/core/res/res/values/config.xml
index 6b5ddac..1d5fd14 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values/config.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values/config.xml
@@ -93,4 +93,8 @@
<string name="config_dataUsageSummaryComponent">com.android.car.settings/com.android.car.settings.datausage.DataWarningAndLimitActivity</string>
<bool name="config_automotiveHideNavBarForKeyboard">true</bool>
+
+ <!-- Turn off Wallpaper service -->
+ <bool name="config_enableWallpaperService">false</bool>
+
</resources>
diff --git a/car_product/sepolicy/private/carservice_app.te b/car_product/sepolicy/private/carservice_app.te
index bc1d74c..6e65a8f 100644
--- a/car_product/sepolicy/private/carservice_app.te
+++ b/car_product/sepolicy/private/carservice_app.te
@@ -12,6 +12,9 @@
# Allow Car Service to register/access itself with ServiceManager
add_service(carservice_app, carservice_service)
+# Allow Car Service to register its stats service with ServiceManager
+add_service(carservice_app, carstats_service)
+
allow carservice_app wifi_service:service_manager find;
# Allow Car Service to access certain system services.
diff --git a/car_product/sepolicy/private/service_contexts b/car_product/sepolicy/private/service_contexts
index 7ac544c..38d994c 100644
--- a/car_product/sepolicy/private/service_contexts
+++ b/car_product/sepolicy/private/service_contexts
@@ -1,2 +1,3 @@
car_service u:object_r:carservice_service:s0
+car_stats u:object_r:carstats_service:s0
com.android.car.procfsinspector u:object_r:procfsinspector_service:s0
diff --git a/car_product/sepolicy/private/statsd.te b/car_product/sepolicy/private/statsd.te
new file mode 100644
index 0000000..1a17418
--- /dev/null
+++ b/car_product/sepolicy/private/statsd.te
@@ -0,0 +1,2 @@
+# Allow statsd to pull atoms from car_stats service
+allow statsd carstats_service:service_manager find;
diff --git a/car_product/sepolicy/public/service.te b/car_product/sepolicy/public/service.te
index 87426f4..c6a2e30 100644
--- a/car_product/sepolicy/public/service.te
+++ b/car_product/sepolicy/public/service.te
@@ -1,2 +1,3 @@
type carservice_service, app_api_service, service_manager_type;
+type carstats_service, service_manager_type;
type procfsinspector_service, service_manager_type;
diff --git a/car_product/sepolicy/public/te_macros b/car_product/sepolicy/public/te_macros
new file mode 100644
index 0000000..963afdc
--- /dev/null
+++ b/car_product/sepolicy/public/te_macros
@@ -0,0 +1,7 @@
+# Define a macro to allow extra HAL dump
+define(`dump_extra_hal', `
+ hal_client_domain(dumpstate, $1);
+ allow $1_server dumpstate:fifo_file write;
+ allow $1_server dumpstate:fd use;
+ allow dumpstate $1:process signal;
+')
diff --git a/service/res/values/config.xml b/service/res/values/config.xml
index 81e0620..c756510 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -241,4 +241,8 @@
<integer name="config_mediaSourceChangedAutoplay">2</integer>
<!-- Configuration to enable media center to autoplay on boot -->
<integer name="config_mediaBootAutoplay">2</integer>
+
+ <!-- Disable switching the user while the system is resuming from Suspend to RAM.
+ This default says to prevent changing the user during Resume. -->
+ <bool name="config_disableUserSwitchDuringResume" translatable="false">true</bool>
</resources>
diff --git a/service/src/com/android/car/CarMediaService.java b/service/src/com/android/car/CarMediaService.java
index a2f5567..665643a 100644
--- a/service/src/com/android/car/CarMediaService.java
+++ b/service/src/com/android/car/CarMediaService.java
@@ -191,6 +191,9 @@
}
private void maybeInitUser() {
+ if (mCurrentUser == 0) {
+ return;
+ }
if (mUserManager.isUserUnlocked(mCurrentUser)) {
initUser();
} else {
diff --git a/service/src/com/android/car/CarPowerManagementService.java b/service/src/com/android/car/CarPowerManagementService.java
index c1341da..83c5051 100644
--- a/service/src/com/android/car/CarPowerManagementService.java
+++ b/service/src/com/android/car/CarPowerManagementService.java
@@ -54,6 +54,10 @@
*/
public class CarPowerManagementService extends ICarPower.Stub implements
CarServiceBase, PowerHalService.PowerEventListener {
+
+ private final Object mLock = new Object();
+ private final Object mSimulationWaitObject = new Object();
+
private final Context mContext;
private final PowerHalService mHal;
private final SystemInterface mSystemInterface;
@@ -62,32 +66,38 @@
// The listeners that must indicate asynchronous completion by calling finished().
private final PowerManagerCallbackList mPowerManagerListenersWithCompletion =
new PowerManagerCallbackList();
- private final Set<IBinder> mListenersWeAreWaitingFor = new HashSet<>();
- private final Object mSimulationSleepObject = new Object();
- @GuardedBy("this")
+ @GuardedBy("mSimulationWaitObject")
+ private boolean mWakeFromSimulatedSleep;
+ @GuardedBy("mSimulationWaitObject")
+ private boolean mInSimulatedDeepSleepMode;
+
+ @GuardedBy("mLock")
+ private final Set<IBinder> mListenersWeAreWaitingFor = new HashSet<>();
+ @GuardedBy("mLock")
private CpmsState mCurrentState;
- @GuardedBy("this")
+ @GuardedBy("mLock")
private Timer mTimer;
- @GuardedBy("this")
+ @GuardedBy("mLock")
private long mProcessingStartTime;
- @GuardedBy("this")
+ @GuardedBy("mLock")
private long mLastSleepEntryTime;
- @GuardedBy("this")
+ @GuardedBy("mLock")
private final LinkedList<CpmsState> mPendingPowerStates = new LinkedList<>();
- @GuardedBy("this")
+ @GuardedBy("mLock")
private HandlerThread mHandlerThread;
- @GuardedBy("this")
+ @GuardedBy("mLock")
private PowerHandler mHandler;
- @GuardedBy("this")
+ @GuardedBy("mLock")
private boolean mTimerActive;
- @GuardedBy("mSimulationSleepObject")
- private boolean mInSimulatedDeepSleepMode = false;
- @GuardedBy("mSimulationSleepObject")
- private boolean mWakeFromSimulatedSleep = false;
- private int mNextWakeupSec = 0;
- private boolean mShutdownOnFinish = false;
+ @GuardedBy("mLock")
+ private int mNextWakeupSec;
+ @GuardedBy("mLock")
+ private boolean mShutdownOnFinish;
+ @GuardedBy("mLock")
private boolean mIsBooting = true;
+ @GuardedBy("mLock")
+ private boolean mIsResuming;
private final CarUserManagerHelper mCarUserManagerHelper;
@@ -160,7 +170,7 @@
@Override
public void init() {
- synchronized (CarPowerManagementService.this) {
+ synchronized (mLock) {
mHandlerThread = new HandlerThread(CarLog.TAG_POWER);
mHandlerThread.start();
mHandler = new PowerHandler(mHandlerThread.getLooper());
@@ -180,11 +190,12 @@
@Override
public void release() {
HandlerThread handlerThread;
- synchronized (CarPowerManagementService.this) {
+ synchronized (mLock) {
releaseTimerLocked();
mCurrentState = null;
mHandler.cancelAll();
handlerThread = mHandlerThread;
+ mListenersWeAreWaitingFor.clear();
}
handlerThread.quitSafely();
try {
@@ -194,7 +205,6 @@
}
mSystemInterface.stopDisplayStateMonitoring();
mPowerManagerListeners.kill();
- mListenersWeAreWaitingFor.clear();
mSystemInterface.releaseAllWakeLocks();
}
@@ -212,7 +222,7 @@
@Override
public void onApPowerStateChange(PowerState state) {
PowerHandler handler;
- synchronized (CarPowerManagementService.this) {
+ synchronized (mLock) {
mPendingPowerStates.addFirst(new CpmsState(state));
handler = mHandler;
}
@@ -220,8 +230,11 @@
}
@VisibleForTesting
- protected void clearIsBooting() {
- mIsBooting = false;
+ protected void clearIsBootingOrResuming() {
+ synchronized (mLock) {
+ mIsBooting = false;
+ mIsResuming = false;
+ }
}
/**
@@ -230,7 +243,7 @@
private void onApPowerStateChange(int apState, int carPowerStateListenerState) {
CpmsState newState = new CpmsState(apState, carPowerStateListenerState);
PowerHandler handler;
- synchronized (CarPowerManagementService.this) {
+ synchronized (mLock) {
mPendingPowerStates.addFirst(newState);
handler = mHandler;
}
@@ -240,7 +253,7 @@
private void doHandlePowerStateChange() {
CpmsState state;
PowerHandler handler;
- synchronized (CarPowerManagementService.this) {
+ synchronized (mLock) {
state = mPendingPowerStates.peekFirst();
mPendingPowerStates.clear();
if (state == null) {
@@ -304,10 +317,30 @@
}
private void handleOn() {
- // Do not switch user if it is booting as there can be a race with CarServiceHelperService
- if (mIsBooting) {
- mIsBooting = false;
- } else {
+ // Some OEMs have their own user-switching logic, which may not be coordinated with this
+ // code. To avoid contention, we don't switch users when we coming alive. The OEM's code
+ // should do the switch.
+ boolean allowUserSwitch = true;
+ synchronized (mLock) {
+ if (mIsBooting) {
+ // The system is booting, so don't switch users
+ allowUserSwitch = false;
+ mIsBooting = false;
+ mIsResuming = false;
+ Log.i(CarLog.TAG_POWER, "User switch disallowed while booting");
+ } else if (mIsResuming) {
+ // The system is resuming after a suspension. Optionally disable user switching.
+ allowUserSwitch = !mContext.getResources()
+ .getBoolean(R.bool.config_disableUserSwitchDuringResume);
+ mIsBooting = false;
+ mIsResuming = false;
+ if (!allowUserSwitch) {
+ Log.i(CarLog.TAG_POWER, "User switch disallowed while resuming");
+ }
+ }
+ }
+
+ if (allowUserSwitch) {
int targetUserId = mCarUserManagerHelper.getInitialUser();
if (targetUserId != UserHandle.USER_SYSTEM
&& targetUserId != mCarUserManagerHelper.getCurrentForegroundUserId()) {
@@ -323,9 +356,11 @@
private void handleShutdownPrepare(CpmsState newState) {
mSystemInterface.setDisplayState(false);
// Shutdown on finish if the system doesn't support deep sleep or doesn't allow it.
- mShutdownOnFinish |= !mHal.isDeepSleepAllowed()
- || !mSystemInterface.isSystemSupportingDeepSleep()
- || !newState.mCanSleep;
+ synchronized (mLock) {
+ mShutdownOnFinish |= !mHal.isDeepSleepAllowed()
+ || !mSystemInterface.isSystemSupportingDeepSleep()
+ || !newState.mCanSleep;
+ }
if (newState.mCanPostpone) {
Log.i(CarLog.TAG_POWER, "starting shutdown prepare");
sendPowerManagerEvent(CarPowerStateListener.SHUTDOWN_PREPARE);
@@ -333,7 +368,7 @@
doHandlePreprocessing();
} else {
Log.i(CarLog.TAG_POWER, "starting shutdown immediately");
- synchronized (CarPowerManagementService.this) {
+ synchronized (mLock) {
releaseTimerLocked();
}
// Notify hal that we are shutting down and since it is immediate, don't schedule next
@@ -355,21 +390,27 @@
private void handleWaitForFinish(CpmsState state) {
sendPowerManagerEvent(state.mCarPowerStateListenerState);
+ int wakeupSec;
+ synchronized (mLock) {
+ wakeupSec = mNextWakeupSec;
+ }
switch (state.mCarPowerStateListenerState) {
case CarPowerStateListener.SUSPEND_ENTER:
- mHal.sendSleepEntry(mNextWakeupSec);
+ mHal.sendSleepEntry(wakeupSec);
break;
case CarPowerStateListener.SHUTDOWN_ENTER:
- mHal.sendShutdownStart(mNextWakeupSec);
+ mHal.sendShutdownStart(wakeupSec);
break;
}
}
private void handleFinish() {
- boolean mustShutDown;
boolean simulatedMode;
- synchronized (mSimulationSleepObject) {
+ synchronized (mSimulationWaitObject) {
simulatedMode = mInSimulatedDeepSleepMode;
+ }
+ boolean mustShutDown;
+ synchronized (mLock) {
mustShutDown = mShutdownOnFinish && !simulatedMode;
}
if (mustShutDown) {
@@ -380,15 +421,13 @@
}
}
- @GuardedBy("this")
+ @GuardedBy("mLock")
private void releaseTimerLocked() {
- synchronized (CarPowerManagementService.this) {
- if (mTimer != null) {
- mTimer.cancel();
- }
- mTimer = null;
- mTimerActive = false;
+ if (mTimer != null) {
+ mTimer.cancel();
}
+ mTimer = null;
+ mTimerActive = false;
}
private void doHandlePreprocessing() {
@@ -407,7 +446,7 @@
}
Log.i(CarLog.TAG_POWER, "processing before shutdown expected for: "
+ sShutdownPrepareTimeMs + " ms, adding polling:" + pollingCount);
- synchronized (CarPowerManagementService.this) {
+ synchronized (mLock) {
mProcessingStartTime = SystemClock.elapsedRealtime();
releaseTimerLocked();
mTimer = new Timer();
@@ -433,7 +472,7 @@
// see the list go empty and we will think that we are done.
boolean haveSomeCompleters = false;
PowerManagerCallbackList completingListeners = new PowerManagerCallbackList();
- synchronized (mListenersWeAreWaitingFor) {
+ synchronized (mLock) {
mListenersWeAreWaitingFor.clear();
int idx = mPowerManagerListenersWithCompletion.beginBroadcast();
while (idx-- > 0) {
@@ -475,28 +514,33 @@
// enterDeepSleep should force sleep entry even if wake lock is kept.
mSystemInterface.switchToPartialWakeLock();
PowerHandler handler;
- synchronized (CarPowerManagementService.this) {
+ synchronized (mLock) {
handler = mHandler;
}
handler.cancelProcessingComplete();
- synchronized (CarPowerManagementService.this) {
+ synchronized (mLock) {
mLastSleepEntryTime = SystemClock.elapsedRealtime();
}
int nextListenerState;
if (simulatedMode) {
- simulateSleepByLooping();
+ simulateSleepByWaiting();
nextListenerState = CarPowerStateListener.SHUTDOWN_CANCELLED;
} else {
boolean sleepSucceeded = mSystemInterface.enterDeepSleep();
if (!sleepSucceeded) {
- // VHAL should transition CPMS to shutdown.
+ // Suspend failed! VHAL should transition CPMS to shutdown.
Log.e(CarLog.TAG_POWER, "Sleep did not succeed. Now attempting to shut down.");
mSystemInterface.shutdown();
+ return;
}
nextListenerState = CarPowerStateListener.SUSPEND_EXIT;
}
- // On wake, reset nextWakeup time. If not set again, system will suspend/shutdown forever.
- mNextWakeupSec = 0;
+ // On resume, reset nextWakeup time. If not set again, system will suspend/shutdown forever.
+ synchronized (mLock) {
+ mIsResuming = true;
+ mNextWakeupSec = 0;
+ }
+ Log.i(CarLog.TAG_POWER, "Resuming after suspending");
mSystemInterface.refreshDisplayBrightness();
onApPowerStateChange(CpmsState.WAIT_FOR_VHAL, nextListenerState);
}
@@ -537,26 +581,24 @@
}
private void doHandleProcessingComplete() {
- synchronized (CarPowerManagementService.this) {
+ int listenerState;
+ synchronized (mLock) {
releaseTimerLocked();
if (!mShutdownOnFinish && mLastSleepEntryTime > mProcessingStartTime) {
// entered sleep after processing start. So this could be duplicate request.
Log.w(CarLog.TAG_POWER, "Duplicate sleep entry request, ignore");
return;
}
+ listenerState = mShutdownOnFinish
+ ? CarPowerStateListener.SHUTDOWN_ENTER : CarPowerStateListener.SUSPEND_ENTER;
}
-
- if (mShutdownOnFinish) {
- onApPowerStateChange(CpmsState.WAIT_FOR_FINISH, CarPowerStateListener.SHUTDOWN_ENTER);
- } else {
- onApPowerStateChange(CpmsState.WAIT_FOR_FINISH, CarPowerStateListener.SUSPEND_ENTER);
- }
+ onApPowerStateChange(CpmsState.WAIT_FOR_FINISH, listenerState);
}
@Override
public void onDisplayBrightnessChange(int brightness) {
PowerHandler handler;
- synchronized (CarPowerManagementService.this) {
+ synchronized (mLock) {
handler = mHandler;
}
handler.handleDisplayBrightnessChange(brightness);
@@ -572,7 +614,7 @@
public void handleMainDisplayChanged(boolean on) {
PowerHandler handler;
- synchronized (CarPowerManagementService.this) {
+ synchronized (mLock) {
handler = mHandler;
}
handler.handleMainDisplayStateChange(on);
@@ -586,8 +628,13 @@
mHal.sendDisplayBrightness(brightness);
}
- public synchronized Handler getHandler() {
- return mHandler;
+ /**
+ * Get the PowerHandler that we use to change power states
+ */
+ public Handler getHandler() {
+ synchronized (mLock) {
+ return mHandler;
+ }
}
// Binder interface for general use.
@@ -628,7 +675,9 @@
@Override
public void requestShutdownOnNextSuspend() {
ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_POWER);
- mShutdownOnFinish = true;
+ synchronized (mLock) {
+ mShutdownOnFinish = true;
+ }
}
@Override
@@ -639,27 +688,31 @@
}
@Override
- public synchronized void scheduleNextWakeupTime(int seconds) {
+ public void scheduleNextWakeupTime(int seconds) {
if (seconds < 0) {
- Log.w(CarLog.TAG_POWER, "Next wake up can not be in negative time. Ignoring!");
+ Log.w(CarLog.TAG_POWER, "Next wake up time is negative. Ignoring!");
return;
}
- if (!mHal.isTimedWakeupAllowed()) {
- Log.w(CarLog.TAG_POWER, "Setting timed wakeups are disabled in HAL. Skipping");
- mNextWakeupSec = 0;
- return;
- }
- if (mNextWakeupSec == 0 || mNextWakeupSec > seconds) {
- mNextWakeupSec = seconds;
- } else {
- Log.d(CarLog.TAG_POWER, "Tried to schedule next wake up, but already had shorter "
- + "scheduled time");
+ boolean timedWakeupAllowed = mHal.isTimedWakeupAllowed();
+ synchronized (mLock) {
+ if (!timedWakeupAllowed) {
+ Log.w(CarLog.TAG_POWER, "Setting timed wakeups are disabled in HAL. Skipping");
+ mNextWakeupSec = 0;
+ return;
+ }
+ if (mNextWakeupSec == 0 || mNextWakeupSec > seconds) {
+ // The new value is sooner than the old value. Take the new value.
+ mNextWakeupSec = seconds;
+ } else {
+ Log.d(CarLog.TAG_POWER, "Tried to schedule next wake up, but already had shorter "
+ + "scheduled time");
+ }
}
}
private void finishedImpl(IBinder binder) {
boolean allAreComplete = false;
- synchronized (mListenersWeAreWaitingFor) {
+ synchronized (mLock) {
boolean oneWasRemoved = mListenersWeAreWaitingFor.remove(binder);
allAreComplete = oneWasRemoved && mListenersWeAreWaitingFor.isEmpty();
}
@@ -673,7 +726,7 @@
|| mCurrentState.mState == CpmsState.SIMULATE_SLEEP) {
PowerHandler powerHandler;
// All apps are ready to shutdown/suspend.
- synchronized (CarPowerManagementService.this) {
+ synchronized (mLock) {
if (!mShutdownOnFinish) {
if (mLastSleepEntryTime > mProcessingStartTime
&& mLastSleepEntryTime < SystemClock.elapsedRealtime()) {
@@ -765,7 +818,7 @@
@Override
public void run() {
- synchronized (CarPowerManagementService.this) {
+ synchronized (mLock) {
if (!mTimerActive) {
// Ignore timer expiration since we got cancelled
return;
@@ -927,9 +980,9 @@
}
handler.handlePowerStateChange();
- synchronized (mSimulationSleepObject) {
+ synchronized (mSimulationWaitObject) {
mWakeFromSimulatedSleep = true;
- mSimulationSleepObject.notify();
+ mSimulationWaitObject.notify();
}
}
@@ -940,12 +993,12 @@
* that is not directly derived from a VehicleApPowerStateReq.
*/
public void forceSimulatedSuspend() {
- synchronized (mSimulationSleepObject) {
+ synchronized (mSimulationWaitObject) {
mInSimulatedDeepSleepMode = true;
mWakeFromSimulatedSleep = false;
}
PowerHandler handler;
- synchronized (this) {
+ synchronized (mLock) {
mPendingPowerStates.addFirst(new CpmsState(CpmsState.SIMULATE_SLEEP,
CarPowerStateListener.SHUTDOWN_PREPARE));
handler = mHandler;
@@ -956,19 +1009,20 @@
// In a real Deep Sleep, the hardware removes power from the CPU (but retains power
// on the RAM). This puts the processor to sleep. Upon some external signal, power
// is re-applied to the CPU, and processing resumes right where it left off.
- // We simulate this behavior by simply going into a loop.
- // We exit the loop when forceResume() is called.
- private void simulateSleepByLooping() {
- Log.i(CarLog.TAG_POWER, "Starting to simulate Deep Sleep by looping");
- synchronized (mSimulationSleepObject) {
+ // We simulate this behavior by calling wait().
+ // We continue from wait() when forceSimulatedResume() is called.
+ private void simulateSleepByWaiting() {
+ Log.i(CarLog.TAG_POWER, "Starting to simulate Deep Sleep by waiting");
+ synchronized (mSimulationWaitObject) {
while (!mWakeFromSimulatedSleep) {
try {
- mSimulationSleepObject.wait();
+ mSimulationWaitObject.wait();
} catch (InterruptedException ignored) {
+ Thread.currentThread().interrupt(); // Restore interrupted status
}
}
mInSimulatedDeepSleepMode = false;
}
- Log.i(CarLog.TAG_POWER, "Exit Deep Sleep simulation loop");
+ Log.i(CarLog.TAG_POWER, "Exit Deep Sleep simulation");
}
}
diff --git a/service/src/com/android/car/CarService.java b/service/src/com/android/car/CarService.java
index fdc4995..044c791 100644
--- a/service/src/com/android/car/CarService.java
+++ b/service/src/com/android/car/CarService.java
@@ -99,6 +99,7 @@
linkToDeath(mVehicle, mVehicleDeathRecipient);
ServiceManager.addService("car_service", mICarImpl);
+ ServiceManager.addService("car_stats", mICarImpl.getStatsService());
SystemProperties.set("boot.car_service_created", "1");
super.onCreate();
}
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index d98b780..d2fed46 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -386,6 +386,10 @@
}
}
+ CarStatsService getStatsService() {
+ return mCarStatsService;
+ }
+
public static void assertVehicleHalMockPermission(Context context) {
assertPermission(context, Car.PERMISSION_MOCK_VEHICLE_HAL);
}
diff --git a/service/src/com/android/car/stats/CarStatsService.java b/service/src/com/android/car/stats/CarStatsService.java
index db23355..64fbf1f 100644
--- a/service/src/com/android/car/stats/CarStatsService.java
+++ b/service/src/com/android/car/stats/CarStatsService.java
@@ -16,27 +16,36 @@
package com.android.car.stats;
+import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.StatsLogEventWrapper;
+import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.StatsLog;
import com.android.car.stats.VmsClientLog.ConnectionState;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.car.ICarStatsService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.function.Consumer;
import java.util.function.Function;
/**
- * Implements collection and dumpsys reporting of statistics in CSV format.
+ * Implementation of {@link ICarStatsService}, for reporting pulled atoms via statsd.
+ *
+ * Also implements collection and dumpsys reporting of atoms in CSV format.
*/
-public class CarStatsService {
+public class CarStatsService extends ICarStatsService.Stub {
private static final boolean DEBUG = false;
private static final String TAG = "CarStatsService";
private static final String VMS_CONNECTION_STATS_DUMPSYS_HEADER =
@@ -71,12 +80,14 @@
.thenComparingInt(VmsClientStats::getLayerChannel)
.thenComparingInt(VmsClientStats::getLayerVersion);
+ private final Context mContext;
private final PackageManager mPackageManager;
@GuardedBy("mVmsClientStats")
private final Map<Integer, VmsClientLog> mVmsClientStats = new ArrayMap<>();
public CarStatsService(Context context) {
+ mContext = context;
mPackageManager = context.getPackageManager();
}
@@ -97,9 +108,7 @@
}
}
- /**
- * Dumps metrics in CSV format.
- */
+ @Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
List<String> flags = Arrays.asList(args);
if (args.length == 0 || flags.contains("--vms-client")) {
@@ -107,6 +116,21 @@
}
}
+ @Override
+ public StatsLogEventWrapper[] pullData(int tagId) {
+ mContext.enforceCallingPermission(Manifest.permission.DUMP, null);
+ if (tagId != StatsLog.VMS_CLIENT_STATS) {
+ Log.w(TAG, "Unexpected tagId: " + tagId);
+ return null;
+ }
+
+ List<StatsLogEventWrapper> ret = new ArrayList<>();
+ long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ long wallClockNanos = SystemClock.currentTimeMicro() * 1000L;
+ pullVmsClientStats(tagId, elapsedNanos, wallClockNanos, ret);
+ return ret.toArray(new StatsLogEventWrapper[0]);
+ }
+
private void dumpVmsStats(PrintWriter writer) {
synchronized (mVmsClientStats) {
writer.println(VMS_CONNECTION_STATS_DUMPSYS_HEADER);
@@ -120,11 +144,38 @@
writer.println();
writer.println(VMS_CLIENT_STATS_DUMPSYS_HEADER);
+ dumpVmsClientStats(entry -> writer.println(
+ VMS_CLIENT_STATS_DUMPSYS_FORMAT.apply(entry)));
+ }
+ }
+
+ private void pullVmsClientStats(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ dumpVmsClientStats((entry) -> {
+ StatsLogEventWrapper e =
+ new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(entry.getUid());
+
+ e.writeInt(entry.getLayerType());
+ e.writeInt(entry.getLayerChannel());
+ e.writeInt(entry.getLayerVersion());
+
+ e.writeLong(entry.getTxBytes());
+ e.writeLong(entry.getTxPackets());
+ e.writeLong(entry.getRxBytes());
+ e.writeLong(entry.getRxPackets());
+ e.writeLong(entry.getDroppedBytes());
+ e.writeLong(entry.getDroppedPackets());
+ pulledData.add(e);
+ });
+ }
+
+ private void dumpVmsClientStats(Consumer<VmsClientStats> dumpFn) {
+ synchronized (mVmsClientStats) {
mVmsClientStats.values().stream()
.flatMap(log -> log.getLayerEntries().stream())
.sorted(VMS_CLIENT_STATS_ORDER)
- .forEachOrdered(entry -> writer.println(
- VMS_CLIENT_STATS_DUMPSYS_FORMAT.apply(entry)));
+ .forEachOrdered(dumpFn);
}
}
}
diff --git a/service/src/com/android/car/stats/VmsClientLog.java b/service/src/com/android/car/stats/VmsClientLog.java
index 506a5fc..0fa2198 100644
--- a/service/src/com/android/car/stats/VmsClientLog.java
+++ b/service/src/com/android/car/stats/VmsClientLog.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.car.vms.VmsLayer;
import android.util.ArrayMap;
+import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
@@ -36,15 +37,20 @@
*/
public static class ConnectionState {
// Attempting to connect to the client
- public static final int CONNECTING = 0;
+ public static final int CONNECTING =
+ StatsLog.VMS_CLIENT_CONNECTION_STATE_CHANGED__STATE__CONNECTING;
// Client connection established
- public static final int CONNECTED = 1;
+ public static final int CONNECTED =
+ StatsLog.VMS_CLIENT_CONNECTION_STATE_CHANGED__STATE__CONNECTED;
// Client connection closed unexpectedly
- public static final int DISCONNECTED = 2;
+ public static final int DISCONNECTED =
+ StatsLog.VMS_CLIENT_CONNECTION_STATE_CHANGED__STATE__DISCONNECTED;
// Client connection closed by VMS
- public static final int TERMINATED = 3;
+ public static final int TERMINATED =
+ StatsLog.VMS_CLIENT_CONNECTION_STATE_CHANGED__STATE__TERMINATED;
// Error establishing the client connection
- public static final int CONNECTION_ERROR = 4;
+ public static final int CONNECTION_ERROR =
+ StatsLog.VMS_CLIENT_CONNECTION_STATE_CHANGED__STATE__CONNECTION_ERROR;
}
private final Object mLock = new Object();
@@ -77,6 +83,9 @@
* @param connectionState New connection state
*/
public void logConnectionState(int connectionState) {
+ StatsLog.write(StatsLog.VMS_CLIENT_CONNECTION_STATE_CHANGED,
+ mUid, mPackageName, connectionState);
+
AtomicLong counter;
synchronized (mLock) {
counter = mConnectionStateCounters.computeIfAbsent(connectionState,
diff --git a/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java b/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java
index fa82f90..945a9d6 100644
--- a/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java
@@ -197,7 +197,7 @@
// second processing after wakeup
assertFalse(mDisplayInterface.getDisplayState());
// do not skip user switching part.
- mService.clearIsBooting();
+ mService.clearIsBootingOrResuming();
mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.ON, 0));
assertTrue(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
// user switching should have been requested.