Merge changes from topic "cherrypick-nssnapshot_move-jqegvlbi07"
* changes:
[automerged blank] [MS55.1] Move NetworkStateSnapshot into module folder 2p: d9101cda97
[MS55.1] Move NetworkStateSnapshot into module folder
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index c706a3a..8237383 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -25,6 +25,7 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
@@ -127,28 +128,47 @@
<pre>
digraph {
+ subgraph cluster_legend {
+ label="Legend"
+
+ wakeup_alarm [label="Entering this state requires a wakeup alarm",color=red,shape=box]
+ nonwakeup_alarm [
+ label="This state can be entered from a non-wakeup alarm",color=blue,shape=oval
+ ]
+ no_alarm [label="This state doesn't require an alarm",color=black,shape=diamond]
+ }
+
subgraph deep {
label="deep";
- STATE_ACTIVE [label="STATE_ACTIVE\nScreen on OR Charging OR Alarm going off soon"]
- STATE_INACTIVE [label="STATE_INACTIVE\nScreen off AND Not charging"]
+ STATE_ACTIVE [
+ label="STATE_ACTIVE\nScreen on OR Charging OR Alarm going off soon",
+ color=black,shape=diamond
+ ]
+ STATE_INACTIVE [
+ label="STATE_INACTIVE\nScreen off AND Not charging",color=black,shape=diamond
+ ]
STATE_QUICK_DOZE_DELAY [
label="STATE_QUICK_DOZE_DELAY\n"
+ "Screen off AND Not charging\n"
- + "Location, motion detection, and significant motion monitoring turned off"
+ + "Location, motion detection, and significant motion monitoring turned off",
+ color=black,shape=diamond
]
STATE_IDLE_PENDING [
- label="STATE_IDLE_PENDING\nSignificant motion monitoring turned on"
+ label="STATE_IDLE_PENDING\nSignificant motion monitoring turned on",
+ color=red,shape=box
]
- STATE_SENSING [label="STATE_SENSING\nMonitoring for ANY motion"]
+ STATE_SENSING [label="STATE_SENSING\nMonitoring for ANY motion",color=red,shape=box]
STATE_LOCATING [
- label="STATE_LOCATING\nRequesting location, motion monitoring still on"
+ label="STATE_LOCATING\nRequesting location, motion monitoring still on",
+ color=red,shape=box
]
STATE_IDLE [
label="STATE_IDLE\nLocation and motion detection turned off\n"
- + "Significant motion monitoring state unchanged"
+ + "Significant motion monitoring state unchanged",
+ color=red,shape=box
]
- STATE_IDLE_MAINTENANCE [label="STATE_IDLE_MAINTENANCE\n"]
+ STATE_IDLE_MAINTENANCE [label="STATE_IDLE_MAINTENANCE\n",color=red,shape=box]
STATE_ACTIVE -> STATE_INACTIVE [
label="becomeInactiveIfAppropriateLocked() AND Quick Doze not enabled"
@@ -213,19 +233,22 @@
label="light"
LIGHT_STATE_ACTIVE [
- label="LIGHT_STATE_ACTIVE\nScreen on OR Charging OR Alarm going off soon"
+ label="LIGHT_STATE_ACTIVE\nScreen on OR Charging OR Alarm going off soon",
+ color=black,shape=diamond
]
- LIGHT_STATE_INACTIVE [label="LIGHT_STATE_INACTIVE\nScreen off AND Not charging"]
- LIGHT_STATE_PRE_IDLE [
- label="LIGHT_STATE_PRE_IDLE\n"
- + "Delay going into LIGHT_STATE_IDLE due to some running jobs or alarms"
+ LIGHT_STATE_INACTIVE [
+ label="LIGHT_STATE_INACTIVE\nScreen off AND Not charging",
+ color=black,shape=diamond
]
- LIGHT_STATE_IDLE [label="LIGHT_STATE_IDLE\n"]
+ LIGHT_STATE_IDLE [label="LIGHT_STATE_IDLE\n",color=blue,shape=oval]
LIGHT_STATE_WAITING_FOR_NETWORK [
label="LIGHT_STATE_WAITING_FOR_NETWORK\n"
- + "Coming out of LIGHT_STATE_IDLE, waiting for network"
+ + "Coming out of LIGHT_STATE_IDLE, waiting for network",
+ color=black,shape=diamond
]
- LIGHT_STATE_IDLE_MAINTENANCE [label="LIGHT_STATE_IDLE_MAINTENANCE\n"]
+ LIGHT_STATE_IDLE_MAINTENANCE [
+ label="LIGHT_STATE_IDLE_MAINTENANCE\n",color=red,shape=box
+ ]
LIGHT_STATE_OVERRIDE [
label="LIGHT_STATE_OVERRIDE\nDevice in deep doze, light no longer changing states"
]
@@ -236,16 +259,9 @@
LIGHT_STATE_ACTIVE -> LIGHT_STATE_OVERRIDE [label="deep goes to STATE_IDLE"]
LIGHT_STATE_INACTIVE -> LIGHT_STATE_ACTIVE [label="becomeActiveLocked()"]
- LIGHT_STATE_INACTIVE -> LIGHT_STATE_PRE_IDLE [label="active jobs"]
- LIGHT_STATE_INACTIVE -> LIGHT_STATE_IDLE [label="no active jobs"]
+ LIGHT_STATE_INACTIVE -> LIGHT_STATE_IDLE [label="some time transpires"]
LIGHT_STATE_INACTIVE -> LIGHT_STATE_OVERRIDE [label="deep goes to STATE_IDLE"]
- LIGHT_STATE_PRE_IDLE -> LIGHT_STATE_ACTIVE [label="becomeActiveLocked()"]
- LIGHT_STATE_PRE_IDLE -> LIGHT_STATE_IDLE [
- label="stepLightIdleStateLocked(), exitMaintenanceEarlyIfNeededLocked()"
- ]
- LIGHT_STATE_PRE_IDLE -> LIGHT_STATE_OVERRIDE [label="deep goes to STATE_IDLE"]
-
LIGHT_STATE_IDLE -> LIGHT_STATE_ACTIVE [label="becomeActiveLocked()"]
LIGHT_STATE_IDLE -> LIGHT_STATE_WAITING_FOR_NETWORK [label="no network"]
LIGHT_STATE_IDLE -> LIGHT_STATE_IDLE_MAINTENANCE
@@ -421,9 +437,6 @@
/** Device is inactive (screen off) and we are waiting to for the first light idle. */
@VisibleForTesting
static final int LIGHT_STATE_INACTIVE = 1;
- /** Device is about to go idle for the first time, wait for current work to complete. */
- @VisibleForTesting
- static final int LIGHT_STATE_PRE_IDLE = 3;
/** Device is in the light idle state, trying to stay asleep as much as possible. */
@VisibleForTesting
static final int LIGHT_STATE_IDLE = 4;
@@ -434,7 +447,7 @@
/** Device is in the light idle state, but temporarily out of idle to do regular maintenance. */
@VisibleForTesting
static final int LIGHT_STATE_IDLE_MAINTENANCE = 6;
- /** Device light idle state is overriden, now applying deep doze state. */
+ /** Device light idle state is overridden, now applying deep doze state. */
@VisibleForTesting
static final int LIGHT_STATE_OVERRIDE = 7;
@@ -443,7 +456,6 @@
switch (state) {
case LIGHT_STATE_ACTIVE: return "ACTIVE";
case LIGHT_STATE_INACTIVE: return "INACTIVE";
- case LIGHT_STATE_PRE_IDLE: return "PRE_IDLE";
case LIGHT_STATE_IDLE: return "IDLE";
case LIGHT_STATE_WAITING_FOR_NETWORK: return "WAITING_FOR_NETWORK";
case LIGHT_STATE_IDLE_MAINTENANCE: return "IDLE_MAINTENANCE";
@@ -468,10 +480,10 @@
@GuardedBy("this")
private long mNextLightIdleDelay;
@GuardedBy("this")
- private long mNextLightIdleDelayFlex;
- @GuardedBy("this")
private long mNextLightAlarmTime;
@GuardedBy("this")
+ private long mNextLightMaintenanceAlarmTime;
+ @GuardedBy("this")
private long mNextSensingTimeoutAlarmTime;
/** How long a light idle maintenance window should last. */
@@ -658,13 +670,21 @@
}
};
- private final AlarmManager.OnAlarmListener mLightAlarmListener
- = new AlarmManager.OnAlarmListener() {
- @Override
- public void onAlarm() {
- synchronized (DeviceIdleController.this) {
- stepLightIdleStateLocked("s:alarm");
- }
+ private final AlarmManager.OnAlarmListener mLightAlarmListener = () -> {
+ if (DEBUG) {
+ Slog.d(TAG, "Light progression alarm fired");
+ }
+ synchronized (DeviceIdleController.this) {
+ stepLightIdleStateLocked("s:alarm");
+ }
+ };
+
+ private final AlarmManager.OnAlarmListener mLightMaintenanceAlarmListener = () -> {
+ if (DEBUG) {
+ Slog.d(TAG, "Light maintenance alarm fired");
+ }
+ synchronized (DeviceIdleController.this) {
+ stepLightIdleStateLocked("s:alarm");
}
};
@@ -928,11 +948,7 @@
private static final String KEY_FLEX_TIME_SHORT = "flex_time_short";
private static final String KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT =
"light_after_inactive_to";
- private static final String KEY_LIGHT_PRE_IDLE_TIMEOUT = "light_pre_idle_to";
private static final String KEY_LIGHT_IDLE_TIMEOUT = "light_idle_to";
- private static final String KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX =
- "light_idle_to_initial_flex";
- private static final String KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX = "light_max_idle_to_flex";
private static final String KEY_LIGHT_IDLE_FACTOR = "light_idle_factor";
private static final String KEY_LIGHT_MAX_IDLE_TIMEOUT = "light_max_idle_to";
private static final String KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET =
@@ -978,15 +994,9 @@
private static final long DEFAULT_FLEX_TIME_SHORT =
!COMPRESS_TIME ? 60 * 1000L : 5 * 1000L;
private static final long DEFAULT_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT =
- !COMPRESS_TIME ? 60 * 1000L : 15 * 1000L;
- private static final long DEFAULT_LIGHT_PRE_IDLE_TIMEOUT =
- !COMPRESS_TIME ? 3 * 60 * 1000L : 30 * 1000L;
+ !COMPRESS_TIME ? 4 * 60 * 1000L : 30 * 1000L;
private static final long DEFAULT_LIGHT_IDLE_TIMEOUT =
!COMPRESS_TIME ? 5 * 60 * 1000L : 15 * 1000L;
- private static final long DEFAULT_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX =
- !COMPRESS_TIME ? 60 * 1000L : 5 * 1000L;
- private static final long DEFAULT_LIGHT_MAX_IDLE_TIMEOUT_FLEX =
- !COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L;
private static final float DEFAULT_LIGHT_IDLE_FACTOR = 2f;
private static final long DEFAULT_LIGHT_MAX_IDLE_TIMEOUT =
!COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L;
@@ -1054,15 +1064,6 @@
public long LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = DEFAULT_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT;
/**
- * This is amount of time we will wait from the point where we decide we would
- * like to go idle until we actually do, while waiting for jobs and other current
- * activity to finish.
- *
- * @see #KEY_LIGHT_PRE_IDLE_TIMEOUT
- */
- public long LIGHT_PRE_IDLE_TIMEOUT = DEFAULT_LIGHT_PRE_IDLE_TIMEOUT;
-
- /**
* This is the initial time that we will run in light idle maintenance mode.
*
* @see #KEY_LIGHT_IDLE_TIMEOUT
@@ -1070,21 +1071,6 @@
public long LIGHT_IDLE_TIMEOUT = DEFAULT_LIGHT_IDLE_TIMEOUT;
/**
- * This is the initial alarm window size that we will tolerate for light idle maintenance
- * timing.
- *
- * @see #KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX
- */
- public long LIGHT_IDLE_TIMEOUT_INITIAL_FLEX = DEFAULT_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX;
-
- /**
- * This is the maximum value that {@link #LIGHT_IDLE_TIMEOUT_INITIAL_FLEX} should take.
- *
- * @see #KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX
- */
- public long LIGHT_MAX_IDLE_TIMEOUT_FLEX = DEFAULT_LIGHT_MAX_IDLE_TIMEOUT_FLEX;
-
- /**
* Scaling factor to apply to the light idle mode time each time we complete a cycle.
*
* @see #KEY_LIGHT_IDLE_FACTOR
@@ -1327,24 +1313,10 @@
KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
DEFAULT_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
break;
- case KEY_LIGHT_PRE_IDLE_TIMEOUT:
- LIGHT_PRE_IDLE_TIMEOUT = properties.getLong(
- KEY_LIGHT_PRE_IDLE_TIMEOUT, DEFAULT_LIGHT_PRE_IDLE_TIMEOUT);
- break;
case KEY_LIGHT_IDLE_TIMEOUT:
LIGHT_IDLE_TIMEOUT = properties.getLong(
KEY_LIGHT_IDLE_TIMEOUT, DEFAULT_LIGHT_IDLE_TIMEOUT);
break;
- case KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX:
- LIGHT_IDLE_TIMEOUT_INITIAL_FLEX = properties.getLong(
- KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX,
- DEFAULT_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX);
- break;
- case KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX:
- LIGHT_MAX_IDLE_TIMEOUT_FLEX = properties.getLong(
- KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX,
- DEFAULT_LIGHT_MAX_IDLE_TIMEOUT_FLEX);
- break;
case KEY_LIGHT_IDLE_FACTOR:
LIGHT_IDLE_FACTOR = Math.max(1, properties.getFloat(
KEY_LIGHT_IDLE_FACTOR, DEFAULT_LIGHT_IDLE_FACTOR));
@@ -1497,22 +1469,10 @@
TimeUtils.formatDuration(LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT, pw);
pw.println();
- pw.print(" "); pw.print(KEY_LIGHT_PRE_IDLE_TIMEOUT); pw.print("=");
- TimeUtils.formatDuration(LIGHT_PRE_IDLE_TIMEOUT, pw);
- pw.println();
-
pw.print(" "); pw.print(KEY_LIGHT_IDLE_TIMEOUT); pw.print("=");
TimeUtils.formatDuration(LIGHT_IDLE_TIMEOUT, pw);
pw.println();
- pw.print(" "); pw.print(KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX); pw.print("=");
- TimeUtils.formatDuration(LIGHT_IDLE_TIMEOUT_INITIAL_FLEX, pw);
- pw.println();
-
- pw.print(" "); pw.print(KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX); pw.print("=");
- TimeUtils.formatDuration(LIGHT_MAX_IDLE_TIMEOUT_FLEX, pw);
- pw.println();
-
pw.print(" "); pw.print(KEY_LIGHT_IDLE_FACTOR); pw.print("=");
pw.print(LIGHT_IDLE_FACTOR);
pw.println();
@@ -3088,7 +3048,7 @@
if (conn != mNetworkConnected) {
mNetworkConnected = conn;
if (conn && mLightState == LIGHT_STATE_WAITING_FOR_NETWORK) {
- stepLightIdleStateLocked("network");
+ stepLightIdleStateLocked("network", /* forceProgression */ true);
}
}
}
@@ -3343,7 +3303,11 @@
if (DEBUG) Slog.d(TAG, "Moved from LIGHT_STATE_ACTIVE to LIGHT_STATE_INACTIVE");
resetLightIdleManagementLocked();
scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
- mConstants.FLEX_TIME_SHORT, true);
+ mConstants.FLEX_TIME_SHORT);
+ // After moving in INACTIVE, the maintenance window should start the time inactive
+ // timeout and a single light idle period.
+ scheduleLightMaintenanceAlarmLocked(
+ mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT + mConstants.LIGHT_IDLE_TIMEOUT);
EventLogTags.writeDeviceIdleLight(mLightState, "no activity");
}
}
@@ -3364,9 +3328,9 @@
@GuardedBy("this")
private void resetLightIdleManagementLocked() {
- mNextLightIdleDelay = 0;
- mNextLightIdleDelayFlex = 0;
- mCurLightIdleBudget = 0;
+ mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;
+ mMaintenanceStartTime = 0;
+ mCurLightIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
cancelLightAlarmLocked();
}
@@ -3401,90 +3365,115 @@
}
@GuardedBy("this")
- void stepLightIdleStateLocked(String reason) {
- if (mLightState == LIGHT_STATE_OVERRIDE) {
+ private void stepLightIdleStateLocked(String reason) {
+ stepLightIdleStateLocked(reason, false);
+ }
+
+ @GuardedBy("this")
+ @VisibleForTesting
+ @SuppressLint("WakelockTimeout")
+ void stepLightIdleStateLocked(String reason, boolean forceProgression) {
+ if (mLightState == LIGHT_STATE_ACTIVE || mLightState == LIGHT_STATE_OVERRIDE) {
// If we are already in deep device idle mode, then
// there is nothing left to do for light mode.
return;
}
- if (DEBUG) Slog.d(TAG, "stepLightIdleStateLocked: mLightState=" + mLightState);
+ if (DEBUG) {
+ Slog.d(TAG, "stepLightIdleStateLocked: mLightState=" + lightStateToString(mLightState)
+ + " force=" + forceProgression);
+ }
EventLogTags.writeDeviceIdleLightStep();
- switch (mLightState) {
- case LIGHT_STATE_INACTIVE:
- mCurLightIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
- // Reset the upcoming idle delays.
- mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;
- mNextLightIdleDelayFlex = mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX;
- mMaintenanceStartTime = 0;
- if (!isOpsInactiveLocked()) {
- // We have some active ops going on... give them a chance to finish
- // before going in to our first idle.
- mLightState = LIGHT_STATE_PRE_IDLE;
- EventLogTags.writeDeviceIdleLight(mLightState, reason);
- scheduleLightAlarmLocked(mConstants.LIGHT_PRE_IDLE_TIMEOUT,
- mConstants.FLEX_TIME_SHORT, true);
- break;
+ final long nowElapsed = mInjector.getElapsedRealtime();
+ final boolean crossedMaintenanceTime =
+ mNextLightMaintenanceAlarmTime > 0 && nowElapsed >= mNextLightMaintenanceAlarmTime;
+ final boolean crossedProgressionTime =
+ mNextLightAlarmTime > 0 && nowElapsed >= mNextLightAlarmTime;
+ final boolean enterMaintenance;
+ if (crossedMaintenanceTime) {
+ if (crossedProgressionTime) {
+ enterMaintenance = (mNextLightAlarmTime <= mNextLightMaintenanceAlarmTime);
+ } else {
+ enterMaintenance = true;
+ }
+ } else if (crossedProgressionTime) {
+ enterMaintenance = false;
+ } else if (forceProgression) {
+ // This will happen for adb commands, unit tests,
+ // and when we're in WAITING_FOR_NETWORK and the network connects.
+ enterMaintenance =
+ mLightState == LIGHT_STATE_IDLE
+ || mLightState == LIGHT_STATE_WAITING_FOR_NETWORK;
+ } else {
+ Slog.wtfStack(TAG, "stepLightIdleStateLocked called in invalid state");
+ return;
+ }
+
+ if (enterMaintenance) {
+ if (mNetworkConnected || mLightState == LIGHT_STATE_WAITING_FOR_NETWORK) {
+ // We have been idling long enough, now it is time to do some work.
+ mActiveIdleOpCount = 1;
+ mActiveIdleWakeLock.acquire();
+ mMaintenanceStartTime = SystemClock.elapsedRealtime();
+ if (mCurLightIdleBudget < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {
+ mCurLightIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
+ } else if (mCurLightIdleBudget > mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET) {
+ mCurLightIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET;
}
- // Nothing active, fall through to immediately idle.
- case LIGHT_STATE_PRE_IDLE:
- case LIGHT_STATE_IDLE_MAINTENANCE:
- if (mMaintenanceStartTime != 0) {
- long duration = SystemClock.elapsedRealtime() - mMaintenanceStartTime;
- if (duration < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {
- // We didn't use up all of our minimum budget; add this to the reserve.
- mCurLightIdleBudget +=
- (mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET - duration);
- } else {
- // We used more than our minimum budget; this comes out of the reserve.
- mCurLightIdleBudget -=
- (duration - mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET);
- }
- }
- mMaintenanceStartTime = 0;
- scheduleLightAlarmLocked(mNextLightIdleDelay, mNextLightIdleDelayFlex, false);
mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT,
(long) (mNextLightIdleDelay * mConstants.LIGHT_IDLE_FACTOR));
- mNextLightIdleDelayFlex = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT_FLEX,
- (long) (mNextLightIdleDelayFlex * mConstants.LIGHT_IDLE_FACTOR));
- if (DEBUG) Slog.d(TAG, "Moved to LIGHT_STATE_IDLE.");
- mLightState = LIGHT_STATE_IDLE;
- EventLogTags.writeDeviceIdleLight(mLightState, reason);
- addEvent(EVENT_LIGHT_IDLE, null);
- mGoingIdleWakeLock.acquire();
- mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON_LIGHT);
- break;
- case LIGHT_STATE_IDLE:
- case LIGHT_STATE_WAITING_FOR_NETWORK:
- if (mNetworkConnected || mLightState == LIGHT_STATE_WAITING_FOR_NETWORK) {
- // We have been idling long enough, now it is time to do some work.
- mActiveIdleOpCount = 1;
- mActiveIdleWakeLock.acquire();
- mMaintenanceStartTime = SystemClock.elapsedRealtime();
- if (mCurLightIdleBudget < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {
- mCurLightIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
- } else if (mCurLightIdleBudget > mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET) {
- mCurLightIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET;
- }
- scheduleLightAlarmLocked(mCurLightIdleBudget, mConstants.FLEX_TIME_SHORT, true);
- if (DEBUG) Slog.d(TAG,
- "Moved from LIGHT_STATE_IDLE to LIGHT_STATE_IDLE_MAINTENANCE.");
- mLightState = LIGHT_STATE_IDLE_MAINTENANCE;
- EventLogTags.writeDeviceIdleLight(mLightState, reason);
- addEvent(EVENT_LIGHT_MAINTENANCE, null);
- mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
- } else {
- // We'd like to do maintenance, but currently don't have network
- // connectivity... let's try to wait until the network comes back.
- // We'll only wait for another full idle period, however, and then give up.
- scheduleLightAlarmLocked(mNextLightIdleDelay,
- mNextLightIdleDelayFlex / 2, true);
- if (DEBUG) Slog.d(TAG, "Moved to LIGHT_WAITING_FOR_NETWORK.");
- mLightState = LIGHT_STATE_WAITING_FOR_NETWORK;
- EventLogTags.writeDeviceIdleLight(mLightState, reason);
+ // We're entering MAINTENANCE. It should end curLightIdleBudget time from now.
+ // The next maintenance window should be curLightIdleBudget + nextLightIdleDelay
+ // time from now.
+ scheduleLightAlarmLocked(mCurLightIdleBudget, mConstants.FLEX_TIME_SHORT);
+ scheduleLightMaintenanceAlarmLocked(mCurLightIdleBudget + mNextLightIdleDelay);
+ if (DEBUG) {
+ Slog.d(TAG, "Moved from " + lightStateToString(mLightState)
+ + " to LIGHT_STATE_IDLE_MAINTENANCE");
}
- break;
+ mLightState = LIGHT_STATE_IDLE_MAINTENANCE;
+ EventLogTags.writeDeviceIdleLight(mLightState, reason);
+ addEvent(EVENT_LIGHT_MAINTENANCE, null);
+ mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
+ } else {
+ // We'd like to do maintenance, but currently don't have network
+ // connectivity... let's try to wait until the network comes back.
+ // We'll only wait for another full idle period, however, and then give up.
+ scheduleLightMaintenanceAlarmLocked(mNextLightIdleDelay);
+ mNextLightAlarmTime = 0;
+ if (DEBUG) Slog.d(TAG, "Moved to LIGHT_WAITING_FOR_NETWORK.");
+ mLightState = LIGHT_STATE_WAITING_FOR_NETWORK;
+ EventLogTags.writeDeviceIdleLight(mLightState, reason);
+ }
+ } else {
+ if (mMaintenanceStartTime != 0) {
+ // Cap duration at budget since the non-wakeup alarm to exit maintenance may
+ // not fire at the exact intended time, but once the system is up, we will stop
+ // more ongoing work.
+ long duration = Math.min(mCurLightIdleBudget,
+ SystemClock.elapsedRealtime() - mMaintenanceStartTime);
+ if (duration < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {
+ // We didn't use up all of our minimum budget; add this to the reserve.
+ mCurLightIdleBudget +=
+ (mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET - duration);
+ } else {
+ // We used more than our minimum budget; this comes out of the reserve.
+ mCurLightIdleBudget -=
+ (duration - mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET);
+ }
+ }
+ mMaintenanceStartTime = 0;
+ // We're entering IDLE. We may have used less than curLightIdleBudget for the
+ // maintenance window, so reschedule the alarm starting from now.
+ scheduleLightMaintenanceAlarmLocked(mNextLightIdleDelay);
+ mNextLightAlarmTime = 0;
+ if (DEBUG) Slog.d(TAG, "Moved to LIGHT_STATE_IDLE.");
+ mLightState = LIGHT_STATE_IDLE;
+ EventLogTags.writeDeviceIdleLight(mLightState, reason);
+ addEvent(EVENT_LIGHT_IDLE, null);
+ mGoingIdleWakeLock.acquire();
+ mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON_LIGHT);
}
}
@@ -3839,8 +3828,7 @@
@GuardedBy("this")
void exitMaintenanceEarlyIfNeededLocked() {
- if (mState == STATE_IDLE_MAINTENANCE || mLightState == LIGHT_STATE_IDLE_MAINTENANCE
- || mLightState == LIGHT_STATE_PRE_IDLE) {
+ if (mState == STATE_IDLE_MAINTENANCE || mLightState == LIGHT_STATE_IDLE_MAINTENANCE) {
if (isOpsInactiveLocked()) {
final long now = SystemClock.elapsedRealtime();
if (DEBUG) {
@@ -3853,10 +3841,8 @@
}
if (mState == STATE_IDLE_MAINTENANCE) {
stepIdleStateLocked("s:early");
- } else if (mLightState == LIGHT_STATE_PRE_IDLE) {
- stepLightIdleStateLocked("s:predone");
} else {
- stepLightIdleStateLocked("s:early");
+ stepLightIdleStateLocked("s:early", /* forceProgression */ true);
}
}
}
@@ -3969,6 +3955,10 @@
mNextLightAlarmTime = 0;
mAlarmManager.cancel(mLightAlarmListener);
}
+ if (mNextLightMaintenanceAlarmTime != 0) {
+ mNextLightMaintenanceAlarmTime = 0;
+ mAlarmManager.cancel(mLightMaintenanceAlarmListener);
+ }
}
@GuardedBy("this")
@@ -4035,26 +4025,54 @@
}
@GuardedBy("this")
- void scheduleLightAlarmLocked(long delay, long flex, boolean wakeup) {
+ @VisibleForTesting
+ void scheduleLightAlarmLocked(long delay, long flex) {
if (DEBUG) {
Slog.d(TAG, "scheduleLightAlarmLocked(" + delay
+ (mConstants.USE_WINDOW_ALARMS ? "/" + flex : "")
- + ", wakeup=" + wakeup + ")");
+ + ")");
}
- mNextLightAlarmTime = SystemClock.elapsedRealtime() + delay;
+ mNextLightAlarmTime = mInjector.getElapsedRealtime() + delay;
if (mConstants.USE_WINDOW_ALARMS) {
mAlarmManager.setWindow(
- wakeup ? AlarmManager.ELAPSED_REALTIME_WAKEUP : AlarmManager.ELAPSED_REALTIME,
+ AlarmManager.ELAPSED_REALTIME,
mNextLightAlarmTime, flex,
"DeviceIdleController.light", mLightAlarmListener, mHandler);
} else {
mAlarmManager.set(
- wakeup ? AlarmManager.ELAPSED_REALTIME_WAKEUP : AlarmManager.ELAPSED_REALTIME,
+ AlarmManager.ELAPSED_REALTIME,
mNextLightAlarmTime,
"DeviceIdleController.light", mLightAlarmListener, mHandler);
}
}
+ @GuardedBy("this")
+ @VisibleForTesting
+ void scheduleLightMaintenanceAlarmLocked(long delay) {
+ if (DEBUG) {
+ Slog.d(TAG, "scheduleLightMaintenanceAlarmLocked(" + delay + ")");
+ }
+ mNextLightMaintenanceAlarmTime = mInjector.getElapsedRealtime() + delay;
+ mAlarmManager.setWindow(
+ AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ mNextLightMaintenanceAlarmTime, mConstants.FLEX_TIME_SHORT,
+ "DeviceIdleController.light", mLightMaintenanceAlarmListener, mHandler);
+ }
+
+ @VisibleForTesting
+ long getNextLightAlarmTimeForTesting() {
+ synchronized (this) {
+ return mNextLightAlarmTime;
+ }
+ }
+
+ @VisibleForTesting
+ long getNextLightMaintenanceAlarmTimeForTesting() {
+ synchronized (this) {
+ return mNextLightMaintenanceAlarmTime;
+ }
+ }
+
private void scheduleMotionRegistrationAlarmLocked() {
if (DEBUG) Slog.d(TAG, "scheduleMotionRegistrationAlarmLocked");
long nextMotionRegistrationAlarmTime =
@@ -4424,7 +4442,7 @@
pw.print("Stepped to deep: ");
pw.println(stateToString(mState));
} else if ("light".equals(arg)) {
- stepLightIdleStateLocked("s:shell");
+ stepLightIdleStateLocked("s:shell", /* forceProgression */ true);
pw.print("Stepped to light: "); pw.println(lightStateToString(mLightState));
} else {
pw.println("Unknown idle mode: " + arg);
@@ -4464,7 +4482,7 @@
becomeInactiveIfAppropriateLocked();
int curLightState = mLightState;
while (curLightState != LIGHT_STATE_IDLE) {
- stepLightIdleStateLocked("s:shell");
+ stepLightIdleStateLocked("s:shell", /* forceProgression */ true);
if (curLightState == mLightState) {
pw.print("Unable to go light idle; stopped at ");
pw.println(lightStateToString(mLightState));
@@ -5076,19 +5094,19 @@
if (mNextLightIdleDelay != 0) {
pw.print(" mNextLightIdleDelay=");
TimeUtils.formatDuration(mNextLightIdleDelay, pw);
- if (mConstants.USE_WINDOW_ALARMS) {
- pw.print(" (flex=");
- TimeUtils.formatDuration(mNextLightIdleDelayFlex, pw);
- pw.println(")");
- } else {
- pw.println();
- }
+ pw.println();
}
if (mNextLightAlarmTime != 0) {
pw.print(" mNextLightAlarmTime=");
TimeUtils.formatDuration(mNextLightAlarmTime, SystemClock.elapsedRealtime(), pw);
pw.println();
}
+ if (mNextLightMaintenanceAlarmTime != 0) {
+ pw.print(" mNextLightMaintenanceAlarmTime=");
+ TimeUtils.formatDuration(
+ mNextLightMaintenanceAlarmTime, SystemClock.elapsedRealtime(), pw);
+ pw.println();
+ }
if (mCurLightIdleBudget != 0) {
pw.print(" mCurLightIdleBudget=");
TimeUtils.formatDuration(mCurLightIdleBudget, pw);
diff --git a/core/api/current.txt b/core/api/current.txt
index 8a54759..08387fc 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -111,6 +111,7 @@
field public static final String MANAGE_MEDIA = "android.permission.MANAGE_MEDIA";
field public static final String MANAGE_ONGOING_CALLS = "android.permission.MANAGE_ONGOING_CALLS";
field public static final String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
+ field public static final String MANAGE_WIFI_AUTO_JOIN = "android.permission.MANAGE_WIFI_AUTO_JOIN";
field public static final String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
field public static final String MEDIA_CONTENT_CONTROL = "android.permission.MEDIA_CONTENT_CONTROL";
field public static final String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS";
@@ -121,6 +122,7 @@
field public static final String NFC = "android.permission.NFC";
field public static final String NFC_PREFERRED_PAYMENT_INFO = "android.permission.NFC_PREFERRED_PAYMENT_INFO";
field public static final String NFC_TRANSACTION_EVENT = "android.permission.NFC_TRANSACTION_EVENT";
+ field public static final String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG";
field public static final String PACKAGE_USAGE_STATS = "android.permission.PACKAGE_USAGE_STATS";
field @Deprecated public static final String PERSISTENT_ACTIVITY = "android.permission.PERSISTENT_ACTIVITY";
field public static final String POST_NOTIFICATIONS = "android.permission.POST_NOTIFICATIONS";
@@ -3061,6 +3063,7 @@
method @NonNull public final android.accessibilityservice.AccessibilityButtonController getAccessibilityButtonController();
method @NonNull public final android.accessibilityservice.AccessibilityButtonController getAccessibilityButtonController(int);
method @NonNull @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public final android.accessibilityservice.FingerprintGestureController getFingerprintGestureController();
+ method @Nullable public final android.accessibilityservice.InputMethod getInputMethod();
method @NonNull public final android.accessibilityservice.AccessibilityService.MagnificationController getMagnificationController();
method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
@@ -3073,6 +3076,7 @@
method public boolean isNodeInCache(@NonNull android.view.accessibility.AccessibilityNodeInfo);
method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public final android.os.IBinder onBind(android.content.Intent);
+ method @NonNull public android.accessibilityservice.InputMethod onCreateInputMethod();
method @Deprecated protected boolean onGesture(int);
method public boolean onGesture(@NonNull android.accessibilityservice.AccessibilityGestureEvent);
method public abstract void onInterrupt();
@@ -3260,6 +3264,7 @@
field public static final int FEEDBACK_VISUAL = 8; // 0x8
field public static final int FLAG_ENABLE_ACCESSIBILITY_VOLUME = 128; // 0x80
field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
+ field public static final int FLAG_INPUT_METHOD_EDITOR = 32768; // 0x8000
field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10
field public static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 8192; // 0x2000
field public static final int FLAG_REQUEST_ACCESSIBILITY_BUTTON = 256; // 0x100
@@ -3320,6 +3325,28 @@
method public boolean willContinue();
}
+ public class InputMethod {
+ ctor protected InputMethod(@NonNull android.accessibilityservice.AccessibilityService);
+ method @Nullable public final android.accessibilityservice.InputMethod.AccessibilityInputConnection getCurrentInputConnection();
+ method @Nullable public final android.view.inputmethod.EditorInfo getCurrentInputEditorInfo();
+ method public final boolean getCurrentInputStarted();
+ method public void onFinishInput();
+ method public void onStartInput(@NonNull android.view.inputmethod.EditorInfo, boolean);
+ method public void onUpdateSelection(int, int, int, int, int, int);
+ }
+
+ public final class InputMethod.AccessibilityInputConnection {
+ method public void clearMetaKeyStates(int);
+ method public void commitText(@NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
+ method public void deleteSurroundingText(int, int);
+ method public int getCursorCapsMode(int);
+ method @Nullable public android.view.inputmethod.SurroundingText getSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
+ method public void performContextMenuAction(int);
+ method public void performEditorAction(int);
+ method public void sendKeyEvent(@NonNull android.view.KeyEvent);
+ method public void setSelection(int, int);
+ }
+
public final class MagnificationConfig implements android.os.Parcelable {
method public int describeContents();
method public float getCenterX();
@@ -4243,6 +4270,7 @@
method @Deprecated public final void setProgressBarIndeterminate(boolean);
method @Deprecated public final void setProgressBarIndeterminateVisibility(boolean);
method @Deprecated public final void setProgressBarVisibility(boolean);
+ method public void setRecentsScreenshotEnabled(boolean);
method public void setRequestedOrientation(int);
method public final void setResult(int);
method public final void setResult(int, android.content.Intent);
@@ -7334,6 +7362,7 @@
method public int getCurrentFailedPasswordAttempts();
method @Nullable public java.util.List<java.lang.String> getDelegatePackages(@NonNull android.content.ComponentName, @NonNull String);
method @NonNull public java.util.List<java.lang.String> getDelegatedScopes(@Nullable android.content.ComponentName, @NonNull String);
+ method @Nullable public String getDeviceManagerRoleHolderPackageName();
method public CharSequence getDeviceOwnerLockScreenInfo();
method @Nullable public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
method @Nullable public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull String, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
@@ -7629,6 +7658,7 @@
field public static final String EXTRA_PROVISIONING_MODE = "android.app.extra.PROVISIONING_MODE";
field public static final String EXTRA_PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT = "android.app.extra.PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT";
field public static final String EXTRA_PROVISIONING_SERIAL_NUMBER = "android.app.extra.PROVISIONING_SERIAL_NUMBER";
+ field public static final String EXTRA_PROVISIONING_SHOULD_LAUNCH_RESULT_INTENT = "android.app.extra.PROVISIONING_SHOULD_LAUNCH_RESULT_INTENT";
field public static final String EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS = "android.app.extra.PROVISIONING_SKIP_EDUCATION_SCREENS";
field public static final String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
field @Deprecated public static final String EXTRA_PROVISIONING_SKIP_USER_CONSENT = "android.app.extra.PROVISIONING_SKIP_USER_CONSENT";
@@ -7651,6 +7681,7 @@
field public static final String EXTRA_RESOURCE_ID = "android.app.extra.RESOURCE_ID";
field public static final String EXTRA_RESOURCE_TYPE_DRAWABLE = "android.app.extra.RESOURCE_TYPE_DRAWABLE";
field public static final String EXTRA_RESOURCE_TYPE_STRING = "android.app.extra.RESOURCE_TYPE_STRING";
+ field public static final String EXTRA_RESULT_LAUNCH_INTENT = "android.app.extra.RESULT_LAUNCH_INTENT";
field public static final int FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY = 1; // 0x1
field public static final int FLAG_MANAGED_CAN_ACCESS_PARENT = 2; // 0x2
field public static final int FLAG_PARENT_CAN_ACCESS_MANAGED = 1; // 0x1
@@ -26438,7 +26469,7 @@
method @NonNull public android.net.Ikev2VpnProfile.Builder setAuthPsk(@NonNull byte[]);
method @NonNull public android.net.Ikev2VpnProfile.Builder setAuthUsernamePassword(@NonNull String, @NonNull String, @Nullable java.security.cert.X509Certificate);
method @NonNull public android.net.Ikev2VpnProfile.Builder setBypassable(boolean);
- method @NonNull public android.net.Ikev2VpnProfile.Builder setExcludeLocalRoutes(boolean);
+ method @NonNull public android.net.Ikev2VpnProfile.Builder setLocalRoutesExcluded(boolean);
method @NonNull public android.net.Ikev2VpnProfile.Builder setMaxMtu(int);
method @NonNull public android.net.Ikev2VpnProfile.Builder setMetered(boolean);
method @NonNull public android.net.Ikev2VpnProfile.Builder setProxy(@Nullable android.net.ProxyInfo);
@@ -26516,7 +26547,7 @@
}
public abstract class PlatformVpnProfile {
- method public final boolean getExcludeLocalRoutes();
+ method public final boolean areLocalRoutesExcluded();
method public final boolean getRequiresInternetValidation();
method public final int getType();
method @NonNull public final String getTypeString();
@@ -32003,6 +32034,7 @@
method public boolean isDemoUser();
method public static boolean isHeadlessSystemUserMode();
method public boolean isManagedProfile();
+ method public boolean isProfile();
method public boolean isQuietModeEnabled(android.os.UserHandle);
method public boolean isSystemUser();
method public boolean isUserAGoat();
@@ -38838,6 +38870,7 @@
field public static final int NOTIFICATION_CHANNEL_OR_GROUP_UPDATED = 2; // 0x2
field public static final int REASON_APP_CANCEL = 8; // 0x8
field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9
+ field public static final int REASON_ASSISTANT_CANCEL = 22; // 0x16
field public static final int REASON_CANCEL = 2; // 0x2
field public static final int REASON_CANCEL_ALL = 3; // 0x3
field public static final int REASON_CHANNEL_BANNED = 17; // 0x11
@@ -43283,6 +43316,7 @@
method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
method public boolean isWorldPhone();
method @Deprecated public void listen(android.telephony.PhoneStateListener, int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void rebootModem();
method public void registerTelephonyCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyCallback);
method public void registerTelephonyCallback(boolean, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyCallback);
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
@@ -51984,8 +52018,8 @@
method @NonNull public android.view.accessibility.CaptioningManager.CaptionStyle getUserStyle();
method public boolean isCallCaptioningEnabled();
method public final boolean isEnabled();
- method public final boolean isSystemAudioCaptioningRequested();
- method public final boolean isSystemAudioCaptioningUiRequested();
+ method public final boolean isSystemAudioCaptioningEnabled();
+ method public final boolean isSystemAudioCaptioningUiEnabled();
method public void removeCaptioningChangeListener(@NonNull android.view.accessibility.CaptioningManager.CaptioningChangeListener);
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d7517b2..c3c5367 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -195,7 +195,6 @@
field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE";
field public static final String MANAGE_WALLPAPER_EFFECTS_GENERATION = "android.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION";
field public static final String MANAGE_WEAK_ESCROW_TOKEN = "android.permission.MANAGE_WEAK_ESCROW_TOKEN";
- field public static final String MANAGE_WIFI_AUTO_JOIN = "android.permission.MANAGE_WIFI_AUTO_JOIN";
field public static final String MANAGE_WIFI_COUNTRY_CODE = "android.permission.MANAGE_WIFI_COUNTRY_CODE";
field public static final String MARK_DEVICE_ORGANIZATION_OWNED = "android.permission.MARK_DEVICE_ORGANIZATION_OWNED";
field public static final String MODIFY_APPWIDGET_BIND_PERMISSIONS = "android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS";
@@ -227,7 +226,6 @@
field public static final String OBSERVE_SENSOR_PRIVACY = "android.permission.OBSERVE_SENSOR_PRIVACY";
field public static final String OPEN_ACCESSIBILITY_DETAILS_SETTINGS = "android.permission.OPEN_ACCESSIBILITY_DETAILS_SETTINGS";
field public static final String OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD = "android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD";
- field public static final String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG";
field public static final String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT";
field public static final String PACKET_KEEPALIVE_OFFLOAD = "android.permission.PACKET_KEEPALIVE_OFFLOAD";
field public static final String PEERS_MAC_ADDRESS = "android.permission.PEERS_MAC_ADDRESS";
@@ -1147,11 +1145,16 @@
field public static final String EXTRA_PROVISIONING_ORGANIZATION_NAME = "android.app.extra.PROVISIONING_ORGANIZATION_NAME";
field public static final String EXTRA_PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE = "android.app.extra.PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE";
field public static final String EXTRA_PROVISIONING_ROLE_HOLDER_CUSTOM_USER_CONSENT_INTENT = "android.app.extra.PROVISIONING_ROLE_HOLDER_CUSTOM_USER_CONSENT_INTENT";
+ field public static final String EXTRA_PROVISIONING_ROLE_HOLDER_EXTRAS_BUNDLE = "android.app.extra.PROVISIONING_ROLE_HOLDER_EXTRAS_BUNDLE";
+ field public static final String EXTRA_PROVISIONING_ROLE_HOLDER_PACKAGE_DOWNLOAD_COOKIE_HEADER = "android.app.extra.PROVISIONING_ROLE_HOLDER_PACKAGE_DOWNLOAD_COOKIE_HEADER";
+ field public static final String EXTRA_PROVISIONING_ROLE_HOLDER_PACKAGE_DOWNLOAD_LOCATION = "android.app.extra.PROVISIONING_ROLE_HOLDER_PACKAGE_DOWNLOAD_LOCATION";
+ field public static final String EXTRA_PROVISIONING_ROLE_HOLDER_SIGNATURE_CHECKSUM = "android.app.extra.PROVISIONING_ROLE_HOLDER_SIGNATURE_CHECKSUM";
field public static final String EXTRA_PROVISIONING_SKIP_OWNERSHIP_DISCLAIMER = "android.app.extra.PROVISIONING_SKIP_OWNERSHIP_DISCLAIMER";
field public static final String EXTRA_PROVISIONING_SUPPORTED_MODES = "android.app.extra.PROVISIONING_SUPPORTED_MODES";
field public static final String EXTRA_PROVISIONING_SUPPORT_URL = "android.app.extra.PROVISIONING_SUPPORT_URL";
field public static final String EXTRA_PROVISIONING_TRIGGER = "android.app.extra.PROVISIONING_TRIGGER";
field public static final String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION";
+ field public static final String EXTRA_ROLE_HOLDER_PROVISIONING_INITIATOR_PACKAGE = "android.app.extra.ROLE_HOLDER_PROVISIONING_INITIATOR_PACKAGE";
field public static final String EXTRA_ROLE_HOLDER_STATE = "android.app.extra.ROLE_HOLDER_STATE";
field public static final int FLAG_SUPPORTED_MODES_DEVICE_OWNER = 4; // 0x4
field public static final int FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED = 1; // 0x1
@@ -1359,14 +1362,13 @@
method @NonNull public android.app.ambientcontext.AmbientContextEventRequest.Builder setOptions(@NonNull android.os.PersistableBundle);
}
- public final class AmbientContextEventResponse implements android.os.Parcelable {
- method public int describeContents();
- method @Nullable public android.app.PendingIntent getActionPendingIntent();
- method @NonNull public java.util.List<android.app.ambientcontext.AmbientContextEvent> getEvents();
- method @NonNull public String getPackageName();
- method public int getStatusCode();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.app.ambientcontext.AmbientContextEventResponse> CREATOR;
+ public final class AmbientContextManager {
+ method @NonNull public static java.util.List<android.app.ambientcontext.AmbientContextEvent> getEventsFromIntent(@NonNull android.content.Intent);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT) public void queryAmbientContextServiceStatus(@NonNull java.util.Set<java.lang.Integer>, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT) public void registerObserver(@NonNull android.app.ambientcontext.AmbientContextEventRequest, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT) public void startConsentActivity(@NonNull java.util.Set<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT) public void unregisterObserver();
+ field public static final String EXTRA_AMBIENT_CONTEXT_EVENTS = "android.app.ambientcontext.extra.AMBIENT_CONTEXT_EVENTS";
field public static final int STATUS_ACCESS_DENIED = 5; // 0x5
field public static final int STATUS_MICROPHONE_DISABLED = 4; // 0x4
field public static final int STATUS_NOT_SUPPORTED = 2; // 0x2
@@ -1375,27 +1377,14 @@
field public static final int STATUS_UNKNOWN = 0; // 0x0
}
- public static final class AmbientContextEventResponse.Builder {
- ctor public AmbientContextEventResponse.Builder();
- method @NonNull public android.app.ambientcontext.AmbientContextEventResponse.Builder addEvent(@NonNull android.app.ambientcontext.AmbientContextEvent);
- method @NonNull public android.app.ambientcontext.AmbientContextEventResponse build();
- method @NonNull public android.app.ambientcontext.AmbientContextEventResponse.Builder setActionPendingIntent(@NonNull android.app.PendingIntent);
- method @NonNull public android.app.ambientcontext.AmbientContextEventResponse.Builder setPackageName(@NonNull String);
- method @NonNull public android.app.ambientcontext.AmbientContextEventResponse.Builder setStatusCode(int);
- }
-
- public final class AmbientContextManager {
- method @Nullable public static android.app.ambientcontext.AmbientContextEventResponse getResponseFromIntent(@NonNull android.content.Intent);
- method @RequiresPermission(android.Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT) public void registerObserver(@NonNull android.app.ambientcontext.AmbientContextEventRequest, @NonNull android.app.PendingIntent);
- method @RequiresPermission(android.Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT) public void unregisterObserver();
- field public static final String EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE = "android.app.ambientcontext.extra.AMBIENT_CONTEXT_EVENT_RESPONSE";
- }
-
}
package android.app.assist {
- public class ActivityId {
+ public final class ActivityId implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.assist.ActivityId> CREATOR;
}
public static class AssistStructure.ViewNode {
@@ -2778,8 +2767,8 @@
method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.VirtualDeviceParams> CREATOR;
- field public static final int LOCK_STATE_ALWAYS_LOCKED = 0; // 0x0
field public static final int LOCK_STATE_ALWAYS_UNLOCKED = 1; // 0x1
+ field public static final int LOCK_STATE_DEFAULT = 0; // 0x0
}
public static final class VirtualDeviceParams.Builder {
@@ -6667,7 +6656,7 @@
method @NonNull @RequiresPermission(android.Manifest.permission.TIS_EXTENSION_INTERFACE) public java.util.List<java.lang.String> getAvailableExtensionInterfaceNames(@NonNull String);
method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(String);
method @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public int getClientPid(@NonNull String);
- method public int getClientPriority(int, @Nullable String);
+ method @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public int getClientPriority(int, @Nullable String);
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TUNED_INFO) public java.util.List<android.media.tv.TunedInfo> getCurrentTunedInfos();
method @NonNull @RequiresPermission("android.permission.DVB_DEVICE") public java.util.List<android.media.tv.DvbDeviceInfo> getDvbDeviceList();
method @Nullable @RequiresPermission(android.Manifest.permission.TIS_EXTENSION_INTERFACE) public android.os.IBinder getExtensionInterface(@NonNull String, @NonNull String);
@@ -9659,7 +9648,6 @@
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isManagedProfile(int);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isMediaSharedWithParent();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isPrimaryUser();
- method public boolean isProfile();
method public boolean isRestrictedProfile();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public boolean isRestrictedProfile(@NonNull android.os.UserHandle);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isSameProfileGroup(@NonNull android.os.UserHandle, @NonNull android.os.UserHandle);
@@ -10701,14 +10689,45 @@
package android.service.ambientcontext {
+ public final class AmbientContextDetectionResult implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<android.app.ambientcontext.AmbientContextEvent> getEvents();
+ method @NonNull public String getPackageName();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.ambientcontext.AmbientContextDetectionResult> CREATOR;
+ }
+
+ public static final class AmbientContextDetectionResult.Builder {
+ ctor public AmbientContextDetectionResult.Builder();
+ method @NonNull public android.service.ambientcontext.AmbientContextDetectionResult.Builder addEvent(@NonNull android.app.ambientcontext.AmbientContextEvent);
+ method @NonNull public android.service.ambientcontext.AmbientContextDetectionResult build();
+ method @NonNull public android.service.ambientcontext.AmbientContextDetectionResult.Builder setPackageName(@NonNull String);
+ }
+
public abstract class AmbientContextDetectionService extends android.app.Service {
ctor public AmbientContextDetectionService();
method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
- method public abstract void onStartDetection(@NonNull android.app.ambientcontext.AmbientContextEventRequest, @NonNull String, @NonNull java.util.function.Consumer<android.app.ambientcontext.AmbientContextEventResponse>);
+ method @BinderThread public abstract void onQueryServiceStatus(@NonNull int[], @NonNull String, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionServiceStatus>);
+ method @BinderThread public abstract void onStartDetection(@NonNull android.app.ambientcontext.AmbientContextEventRequest, @NonNull String, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionResult>, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionServiceStatus>);
method public abstract void onStopDetection(@NonNull String);
field public static final String SERVICE_INTERFACE = "android.service.ambientcontext.AmbientContextDetectionService";
}
+ public final class AmbientContextDetectionServiceStatus implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public String getPackageName();
+ method public int getStatusCode();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.ambientcontext.AmbientContextDetectionServiceStatus> CREATOR;
+ }
+
+ public static final class AmbientContextDetectionServiceStatus.Builder {
+ ctor public AmbientContextDetectionServiceStatus.Builder();
+ method @NonNull public android.service.ambientcontext.AmbientContextDetectionServiceStatus build();
+ method @NonNull public android.service.ambientcontext.AmbientContextDetectionServiceStatus.Builder setPackageName(@NonNull String);
+ method @NonNull public android.service.ambientcontext.AmbientContextDetectionServiceStatus.Builder setStatusCode(int);
+ }
+
}
package android.service.appprediction {
@@ -13256,7 +13275,7 @@
method public boolean needsOtaServiceProvisioning();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled();
method @RequiresPermission(android.Manifest.permission.REBOOT) public int prepareForUnattendedReboot();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeCarrierPrivilegesListener(@NonNull android.telephony.TelephonyManager.CarrierPrivilegesListener);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void reportDefaultNetworkStatus(boolean);
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
@@ -15531,8 +15550,8 @@
}
public class CaptioningManager {
- method @RequiresPermission(android.Manifest.permission.SET_SYSTEM_AUDIO_CAPTION) public final void setSystemAudioCaptioningRequested(boolean);
- method @RequiresPermission(android.Manifest.permission.SET_SYSTEM_AUDIO_CAPTION) public final void setSystemAudioCaptioningUiRequested(boolean);
+ method @RequiresPermission(android.Manifest.permission.SET_SYSTEM_AUDIO_CAPTION) public final void setSystemAudioCaptioningEnabled(boolean);
+ method @RequiresPermission(android.Manifest.permission.SET_SYSTEM_AUDIO_CAPTION) public final void setSystemAudioCaptioningUiEnabled(boolean);
}
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 5bd6ca8..bcc7e4a 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -278,6 +278,7 @@
}
public final class GameManager {
+ method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE) public int getGameMode(@NonNull String);
method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE) public boolean isAngleEnabled(@NonNull String);
method public void setGameServiceProvider(@Nullable String);
}
@@ -569,9 +570,13 @@
package android.app.assist {
- public class ActivityId {
+ public final class ActivityId implements android.os.Parcelable {
+ ctor public ActivityId(int, @Nullable android.os.IBinder);
+ method public int describeContents();
method public int getTaskId();
method @Nullable public android.os.IBinder getToken();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.assist.ActivityId> CREATOR;
}
}
@@ -2862,6 +2867,7 @@
method public default void setShouldShowSystemDecors(int, boolean);
method public default void setShouldShowWithInsecureKeyguard(int, boolean);
method public default boolean shouldShowSystemDecors(int);
+ method @Nullable public default android.graphics.Bitmap snapshotTaskForRecents(@IntRange(from=0) int);
field public static final int DISPLAY_IME_POLICY_FALLBACK_DISPLAY = 1; // 0x1
field public static final int DISPLAY_IME_POLICY_HIDE = 2; // 0x2
field public static final int DISPLAY_IME_POLICY_LOCAL = 0; // 0x0
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 50473f1..cf3ca20 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -40,6 +40,8 @@
import android.graphics.Region;
import android.hardware.HardwareBuffer;
import android.hardware.display.DisplayManager;
+import android.inputmethodservice.IInputMethodSessionWrapper;
+import android.inputmethodservice.RemoteInputConnection;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -65,14 +67,23 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityWindowInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputBinding;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodSession;
+import com.android.internal.inputmethod.CancellationGroup;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.view.IInputContext;
+import com.android.internal.view.IInputMethodSession;
+import com.android.internal.view.IInputSessionWithIdCallback;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
@@ -627,6 +638,21 @@
void onAccessibilityButtonAvailabilityChanged(boolean available);
/** This is called when the system action list is changed. */
void onSystemActionsChanged();
+ /** This is called when an app requests ime sessions or when the service is enabled. */
+ void createImeSession(IInputSessionWithIdCallback callback);
+ /**
+ * This is called when InputMethodManagerService requests to set the session enabled or
+ * disabled
+ */
+ void setImeSessionEnabled(InputMethodSession session, boolean enabled);
+ /** This is called when an app binds input or when the service is enabled. */
+ void bindInput(InputBinding binding);
+ /** This is called when an app unbinds input or when the service is disabled. */
+ void unbindInput();
+ /** This is called when an app starts input or when the service is enabled. */
+ void startInput(@Nullable InputConnection inputConnection,
+ @NonNull EditorInfo editorInfo, boolean restarting,
+ @NonNull IBinder startInputToken);
}
/**
@@ -763,6 +789,8 @@
new SparseArray<>(0);
private SoftKeyboardController mSoftKeyboardController;
+ private InputMethod mInputMethod;
+ private boolean mInputMethodInitialized = false;
private final SparseArray<AccessibilityButtonController> mAccessibilityButtonControllers =
new SparseArray<>(0);
@@ -797,6 +825,17 @@
for (int i = 0; i < mMagnificationControllers.size(); i++) {
mMagnificationControllers.valueAt(i).onServiceConnectedLocked();
}
+ AccessibilityServiceInfo info = getServiceInfo();
+ if (info != null) {
+ boolean requestIme = (info.flags
+ & AccessibilityServiceInfo.FLAG_INPUT_METHOD_EDITOR) != 0;
+ if (requestIme && !mInputMethodInitialized) {
+ mInputMethod = onCreateInputMethod();
+ mInputMethodInitialized = true;
+ }
+ } else {
+ Log.e(LOG_TAG, "AccessibilityServiceInfo is null in dispatchServiceConnected");
+ }
}
if (mSoftKeyboardController != null) {
mSoftKeyboardController.onServiceConnected();
@@ -1849,6 +1888,32 @@
}
}
+ /**
+ * The default implementation returns our default {@link InputMethod}. Subclasses can override
+ * it to provide their own customized version. Accessibility services need to set the
+ * {@link AccessibilityServiceInfo#FLAG_INPUT_METHOD_EDITOR} flag to use input method APIs.
+ *
+ * @return the InputMethod.
+ */
+ @NonNull
+ public InputMethod onCreateInputMethod() {
+ return new InputMethod(this);
+ }
+
+ /**
+ * Returns the InputMethod instance after the system calls {@link #onCreateInputMethod()},
+ * which may be used to input text or get editable text selection change notifications. It will
+ * return null if the accessibility service doesn't set the
+ * {@link AccessibilityServiceInfo#FLAG_INPUT_METHOD_EDITOR} flag or the system doesn't call
+ * {@link #onCreateInputMethod()}.
+ *
+ * @return the InputMethod instance
+ */
+ @Nullable
+ public final InputMethod getInputMethod() {
+ return mInputMethod;
+ }
+
private void onSoftKeyboardShowModeChanged(int showMode) {
if (mSoftKeyboardController != null) {
mSoftKeyboardController.dispatchSoftKeyboardShowModeChanged(showMode);
@@ -2657,6 +2722,47 @@
public void onSystemActionsChanged() {
AccessibilityService.this.onSystemActionsChanged();
}
+
+ @Override
+ public void createImeSession(IInputSessionWithIdCallback callback) {
+ if (mInputMethod != null) {
+ mInputMethod.createImeSession(callback);
+ }
+ }
+
+ @Override
+ public void setImeSessionEnabled(InputMethodSession session, boolean enabled) {
+ if (mInputMethod != null) {
+ mInputMethod.setImeSessionEnabled(session, enabled);
+ }
+ }
+
+ @Override
+ public void bindInput(InputBinding binding) {
+ if (mInputMethod != null) {
+ mInputMethod.bindInput(binding);
+ }
+ }
+
+ @Override
+ public void unbindInput() {
+ if (mInputMethod != null) {
+ mInputMethod.unbindInput();
+ }
+ }
+
+ @Override
+ public void startInput(@Nullable InputConnection inputConnection,
+ @NonNull EditorInfo editorInfo, boolean restarting,
+ @NonNull IBinder startInputToken) {
+ if (mInputMethod != null) {
+ if (restarting) {
+ mInputMethod.restartInput(inputConnection, editorInfo);
+ } else {
+ mInputMethod.startInput(inputConnection, editorInfo);
+ }
+ }
+ }
});
}
@@ -2682,6 +2788,11 @@
private static final int DO_ACCESSIBILITY_BUTTON_CLICKED = 12;
private static final int DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED = 13;
private static final int DO_ON_SYSTEM_ACTIONS_CHANGED = 14;
+ private static final int DO_CREATE_IME_SESSION = 15;
+ private static final int DO_SET_IME_SESSION_ENABLED = 16;
+ private static final int DO_BIND_INPUT = 17;
+ private static final int DO_UNBIND_INPUT = 18;
+ private static final int DO_START_INPUT = 19;
private final HandlerCaller mCaller;
@@ -2690,6 +2801,22 @@
private int mConnectionId = AccessibilityInteractionClient.NO_ID;
+ /**
+ * This is not {@null} only between {@link #bindInput(InputBinding)} and
+ * {@link #unbindInput()} so that {@link RemoteInputConnection} can query if
+ * {@link #unbindInput()} has already been called or not, mainly to avoid unnecessary
+ * blocking operations.
+ *
+ * <p>This field must be set and cleared only from the binder thread(s), where the system
+ * guarantees that {@link #bindInput(InputBinding)},
+ * {@link #startInput(IBinder, IInputContext, EditorInfo, boolean)}, and
+ * {@link #unbindInput()} are called with the same order as the original calls
+ * in {@link com.android.server.inputmethod.InputMethodManagerService}.
+ * See {@link IBinder#FLAG_ONEWAY} for detailed semantics.</p>
+ */
+ @Nullable
+ CancellationGroup mCancellationGroup = null;
+
public IAccessibilityServiceClientWrapper(Context context, Looper looper,
Callbacks callback) {
mCallback = callback;
@@ -2783,6 +2910,70 @@
mCaller.sendMessage(mCaller.obtainMessage(DO_ON_SYSTEM_ACTIONS_CHANGED));
}
+ /** This is called when an app requests ime sessions or when the service is enabled. */
+ public void createImeSession(IInputSessionWithIdCallback callback) {
+ final Message message = mCaller.obtainMessageO(DO_CREATE_IME_SESSION, callback);
+ mCaller.sendMessage(message);
+ }
+
+ /**
+ * This is called when InputMethodManagerService requests to set the session enabled or
+ * disabled
+ */
+ public void setImeSessionEnabled(IInputMethodSession session, boolean enabled) {
+ try {
+ InputMethodSession ls = ((IInputMethodSessionWrapper)
+ session).getInternalInputMethodSession();
+ if (ls == null) {
+ Log.w(LOG_TAG, "Session is already finished: " + session);
+ return;
+ }
+ mCaller.sendMessage(mCaller.obtainMessageIO(
+ DO_SET_IME_SESSION_ENABLED, enabled ? 1 : 0, ls));
+ } catch (ClassCastException e) {
+ Log.w(LOG_TAG, "Incoming session not of correct type: " + session, e);
+ }
+ }
+
+ /** This is called when an app binds input or when the service is enabled. */
+ public void bindInput(InputBinding binding) {
+ if (mCancellationGroup != null) {
+ Log.e(LOG_TAG, "bindInput must be paired with unbindInput.");
+ }
+ mCancellationGroup = new CancellationGroup();
+ InputConnection ic = new RemoteInputConnection(new WeakReference<>(() -> mContext),
+ IInputContext.Stub.asInterface(binding.getConnectionToken()),
+ mCancellationGroup);
+ InputBinding nu = new InputBinding(ic, binding);
+ final Message message = mCaller.obtainMessageO(DO_BIND_INPUT, nu);
+ mCaller.sendMessage(message);
+ }
+
+ /** This is called when an app unbinds input or when the service is disabled. */
+ public void unbindInput() {
+ if (mCancellationGroup != null) {
+ // Signal the flag then forget it.
+ mCancellationGroup.cancelAll();
+ mCancellationGroup = null;
+ } else {
+ Log.e(LOG_TAG, "unbindInput must be paired with bindInput.");
+ }
+ mCaller.sendMessage(mCaller.obtainMessage(DO_UNBIND_INPUT));
+ }
+
+ /** This is called when an app starts input or when the service is enabled. */
+ public void startInput(IBinder startInputToken, IInputContext inputContext,
+ EditorInfo editorInfo, boolean restarting) {
+ if (mCancellationGroup == null) {
+ Log.e(LOG_TAG, "startInput must be called after bindInput.");
+ mCancellationGroup = new CancellationGroup();
+ }
+ final Message message = mCaller.obtainMessageOOOOII(DO_START_INPUT, startInputToken,
+ inputContext, editorInfo, mCancellationGroup, restarting ? 1 : 0,
+ 0 /* unused */);
+ mCaller.sendMessage(message);
+ }
+
@Override
public void onMotionEvent(MotionEvent event) {
final Message message = PooledLambda.obtainMessage(
@@ -2948,6 +3139,50 @@
}
return;
}
+ case DO_CREATE_IME_SESSION: {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ IInputSessionWithIdCallback callback =
+ (IInputSessionWithIdCallback) message.obj;
+ mCallback.createImeSession(callback);
+ }
+ return;
+ }
+ case DO_SET_IME_SESSION_ENABLED: {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.setImeSessionEnabled((InputMethodSession) message.obj,
+ message.arg1 != 0);
+ }
+ return;
+ }
+ case DO_BIND_INPUT: {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.bindInput((InputBinding) message.obj);
+ }
+ return;
+ }
+ case DO_UNBIND_INPUT: {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.unbindInput();
+ }
+ return;
+ }
+ case DO_START_INPUT: {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ final SomeArgs args = (SomeArgs) message.obj;
+ final IBinder startInputToken = (IBinder) args.arg1;
+ final IInputContext inputContext = (IInputContext) args.arg2;
+ final EditorInfo info = (EditorInfo) args.arg3;
+ final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4;
+ final boolean restarting = args.argi5 == 1;
+ final InputConnection ic = inputContext != null
+ ? new RemoteInputConnection(new WeakReference<>(() -> mContext),
+ inputContext, cancellationGroup) : null;
+ info.makeCompatible(mContext.getApplicationInfo().targetSdkVersion);
+ mCallback.startInput(ic, info, restarting, startInputToken);
+ args.recycle();
+ }
+ return;
+ }
default:
Log.w(LOG_TAG, "Unknown message type " + message.what);
}
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 6988048..85e7854 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -390,6 +390,15 @@
*/
public static final int FLAG_SEND_MOTION_EVENTS = 0x0004000;
+ /**
+ * This flag makes the AccessibilityService an input method editor with a subset of input
+ * method editor capabilities: get the {@link android.view.inputmethod.InputConnection} and get
+ * text selection change notifications.
+ *
+ * @see AccessibilityService#getInputMethod()
+ */
+ public static final int FLAG_INPUT_METHOD_EDITOR = 0x0008000;
+
/** {@hide} */
public static final int FLAG_FORCE_DIRECT_BOOT_AWARE = 0x00010000;
@@ -497,6 +506,7 @@
* @see #FLAG_ENABLE_ACCESSIBILITY_VOLUME
* @see #FLAG_REQUEST_ACCESSIBILITY_BUTTON
* @see #FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK
+ * @see #FLAG_INPUT_METHOD_EDITOR
*/
public int flags;
@@ -1356,6 +1366,8 @@
return "FLAG_REQUEST_FINGERPRINT_GESTURES";
case FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK:
return "FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK";
+ case FLAG_INPUT_METHOD_EDITOR:
+ return "FLAG_INPUT_METHOD_EDITOR";
default:
return null;
}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index 375383d..94da61f 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -24,6 +24,11 @@
import android.accessibilityservice.MagnificationConfig;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputBinding;
+import com.android.internal.view.IInputContext;
+import com.android.internal.view.IInputMethodSession;
+import com.android.internal.view.IInputSessionWithIdCallback;
/**
* Top-level interface to an accessibility service component.
@@ -63,4 +68,15 @@
void onAccessibilityButtonAvailabilityChanged(boolean available);
void onSystemActionsChanged();
+
+ void createImeSession(IInputSessionWithIdCallback callback);
+
+ void setImeSessionEnabled(IInputMethodSession session, boolean enabled);
+
+ void bindInput(in InputBinding binding);
+
+ void unbindInput();
+
+ void startInput(in IBinder startInputToken, in IInputContext inputContext,
+ in EditorInfo editorInfo, boolean restarting);
}
diff --git a/core/java/android/accessibilityservice/InputMethod.java b/core/java/android/accessibilityservice/InputMethod.java
new file mode 100644
index 0000000..001d804
--- /dev/null
+++ b/core/java/android/accessibilityservice/InputMethod.java
@@ -0,0 +1,637 @@
+/*
+ * Copyright (C) 2022 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 android.accessibilityservice;
+
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.graphics.Rect;
+import android.inputmethodservice.IInputMethodSessionWrapper;
+import android.inputmethodservice.RemoteInputConnection;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.util.Log;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.InputBinding;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSession;
+import android.view.inputmethod.SurroundingText;
+import android.view.inputmethod.TextAttribute;
+
+import com.android.internal.view.IInputContext;
+import com.android.internal.view.IInputSessionWithIdCallback;
+
+import java.util.concurrent.Executor;
+
+/**
+ * This class provides input method APIs. Some public methods such as
+ * @link #onUpdateSelection(int, int, int, int, int, int)} do nothing by default and service
+ * developers should override them as needed. Developers should also override
+ * {@link AccessibilityService#onCreateInputMethod()} to return
+ * their custom InputMethod implementation. Accessibility services also need to set the
+ * {@link AccessibilityServiceInfo#FLAG_INPUT_METHOD_EDITOR} flag to use input method APIs.
+ */
+public class InputMethod {
+ private static final String LOG_TAG = "A11yInputMethod";
+
+ private final AccessibilityService mService;
+ private InputBinding mInputBinding;
+ private InputConnection mInputConnection;
+ private boolean mInputStarted;
+ private InputConnection mStartedInputConnection;
+ private EditorInfo mInputEditorInfo;
+
+ protected InputMethod(@NonNull AccessibilityService service) {
+ mService = service;
+ }
+
+ /**
+ * Retrieve the currently active InputConnection that is bound to
+ * the input method, or null if there is none.
+ */
+ @Nullable
+ public final AccessibilityInputConnection getCurrentInputConnection() {
+ InputConnection ic = mStartedInputConnection;
+ if (ic != null) {
+ return new AccessibilityInputConnection(ic);
+ }
+ if (mInputConnection != null) {
+ return new AccessibilityInputConnection(mInputConnection);
+ }
+ return null;
+ }
+
+ /**
+ * Whether the input has started.
+ */
+ public final boolean getCurrentInputStarted() {
+ return mInputStarted;
+ }
+
+ /**
+ * Get the EditorInfo which describes several attributes of a text editing object
+ * that an accessibility service is communicating with (typically an EditText).
+ */
+ @Nullable
+ public final EditorInfo getCurrentInputEditorInfo() {
+ return mInputEditorInfo;
+ }
+
+ /**
+ * Called to inform the accessibility service that text input has started in an
+ * editor. You should use this callback to initialize the state of your
+ * input to match the state of the editor given to it.
+ *
+ * @param attribute The attributes of the editor that input is starting
+ * in.
+ * @param restarting Set to true if input is restarting in the same
+ * editor such as because the application has changed the text in
+ * the editor. Otherwise will be false, indicating this is a new
+ * session with the editor.
+ */
+ public void onStartInput(@NonNull EditorInfo attribute, boolean restarting) {
+ // Intentionally empty
+ }
+
+ /**
+ * Called to inform the accessibility service that text input has finished in
+ * the last editor. At this point there may be a call to
+ * {@link #onStartInput(EditorInfo, boolean)} to perform input in a
+ * new editor, or the accessibility service may be left idle. This method is
+ * <em>not</em> called when input restarts in the same editor.
+ *
+ * <p>The default
+ * implementation uses the InputConnection to clear any active composing
+ * text; you can override this (not calling the base class implementation)
+ * to perform whatever behavior you would like.
+ */
+ public void onFinishInput() {
+ InputConnection ic = mStartedInputConnection != null ? mStartedInputConnection
+ : mInputConnection;
+ if (ic != null) {
+ ic.finishComposingText();
+ }
+ }
+
+ /**
+ * Called when the application has reported a new selection region of
+ * the text. This is called whether or not the accessibility service has requested
+ * extracted text updates, although if so it will not receive this call
+ * if the extracted text has changed as well.
+ *
+ * <p>Be careful about changing the text in reaction to this call with
+ * methods such as setComposingText, commitText or
+ * deleteSurroundingText. If the cursor moves as a result, this method
+ * will be called again, which may result in an infinite loop.
+ */
+ public void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart,
+ int newSelEnd, int candidatesStart, int candidatesEnd) {
+ // Intentionally empty
+ }
+
+ final void createImeSession(IInputSessionWithIdCallback callback) {
+ InputMethodSession session = onCreateInputMethodSessionInterface();
+ try {
+ IInputMethodSessionWrapper wrap =
+ new IInputMethodSessionWrapper(mService, session, null);
+ callback.sessionCreated(wrap, mService.getConnectionId());
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ final void setImeSessionEnabled(@NonNull InputMethodSession session, boolean enabled) {
+ ((InputMethodSessionForAccessibility) session).setEnabled(enabled);
+ }
+
+ final void bindInput(@NonNull InputBinding binding) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AccessibilityService.bindInput");
+ mInputBinding = binding;
+ mInputConnection = binding.getConnection();
+ Log.v(LOG_TAG, "bindInput(): binding=" + binding
+ + " ic=" + mInputConnection);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+
+ final void unbindInput() {
+ Log.v(LOG_TAG, "unbindInput(): binding=" + mInputBinding
+ + " ic=" + mInputConnection);
+ // Unbind input is per process per display.
+ mInputBinding = null;
+ mInputConnection = null;
+ }
+
+ final void startInput(@Nullable InputConnection ic, @NonNull EditorInfo attribute) {
+ Log.v(LOG_TAG, "startInput(): editor=" + attribute);
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.startInput");
+ doStartInput(ic, attribute, false /* restarting */);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+
+ final void restartInput(@Nullable InputConnection ic, @NonNull EditorInfo attribute) {
+ Log.v(LOG_TAG, "restartInput(): editor=" + attribute);
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.restartInput");
+ doStartInput(ic, attribute, true /* restarting */);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+
+
+ final void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
+ if (!restarting && mInputStarted) {
+ doFinishInput();
+ }
+ mInputStarted = true;
+ mStartedInputConnection = ic;
+ mInputEditorInfo = attribute;
+ Log.v(LOG_TAG, "CALL: onStartInput");
+ onStartInput(attribute, restarting);
+ }
+
+ final void doFinishInput() {
+ Log.v(LOG_TAG, "CALL: doFinishInput");
+ if (mInputStarted) {
+ Log.v(LOG_TAG, "CALL: onFinishInput");
+ onFinishInput();
+ }
+ mInputStarted = false;
+ mStartedInputConnection = null;
+ }
+
+ private InputMethodSession onCreateInputMethodSessionInterface() {
+ return new InputMethodSessionForAccessibility();
+ }
+
+ /**
+ * This class provides the allowed list of {@link InputConnection} APIs for
+ * accessibility services.
+ */
+ public final class AccessibilityInputConnection {
+ private InputConnection mIc;
+ AccessibilityInputConnection(InputConnection ic) {
+ this.mIc = ic;
+ }
+
+ /**
+ * Commit text to the text box and set the new cursor position. This method is
+ * used to allow the IME to provide extra information while setting up text.
+ *
+ * <p>This method commits the contents of the currently composing text, and then
+ * moves the cursor according to {@code newCursorPosition}. If there
+ * is no composing text when this method is called, the new text is
+ * inserted at the cursor position, removing text inside the selection
+ * if any.
+ *
+ * <p>Calling this method will cause the editor to call
+ * {@link #onUpdateSelection(int, int, int, int,
+ * int, int)} on the current accessibility service after the batch input is over.
+ * <strong>Editor authors</strong>, for this to happen you need to
+ * make the changes known to the accessibility service by calling
+ * {@link InputMethodManager#updateSelection(View, int, int, int, int)},
+ * but be careful to wait until the batch edit is over if one is
+ * in progress.</p>
+ *
+ * @param text The text to commit. This may include styles.
+ * @param newCursorPosition The new cursor position around the text,
+ * in Java characters. If > 0, this is relative to the end
+ * of the text - 1; if <= 0, this is relative to the start
+ * of the text. So a value of 1 will always advance the cursor
+ * to the position after the full text being inserted. Note that
+ * this means you can't position the cursor within the text,
+ * because the editor can make modifications to the text
+ * you are providing so it is not possible to correctly specify
+ * locations there.
+ * @param textAttribute The extra information about the text.
+ */
+ public void commitText(@NonNull CharSequence text, int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ if (mIc != null) {
+ mIc.commitText(text, newCursorPosition, textAttribute);
+ }
+ }
+
+ /**
+ * Set the selection of the text editor. To set the cursor
+ * position, start and end should have the same value.
+ *
+ * <p>Since this moves the cursor, calling this method will cause
+ * the editor to call
+ * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int,
+ * int,int, int)} on the current IME after the batch input is over.
+ * <strong>Editor authors</strong>, for this to happen you need to
+ * make the changes known to the input method by calling
+ * {@link InputMethodManager#updateSelection(View, int, int, int, int)},
+ * but be careful to wait until the batch edit is over if one is
+ * in progress.</p>
+ *
+ * <p>This has no effect on the composing region which must stay
+ * unchanged. The order of start and end is not important. In
+ * effect, the region from start to end and the region from end to
+ * start is the same. Editor authors, be ready to accept a start
+ * that is greater than end.</p>
+ *
+ * @param start the character index where the selection should start.
+ * @param end the character index where the selection should end.
+ */
+ public void setSelection(int start, int end) {
+ if (mIc != null) {
+ mIc.setSelection(start, end);
+ }
+ }
+
+ /**
+ * Gets the surrounding text around the current cursor, with <var>beforeLength</var>
+ * characters of text before the cursor (start of the selection), <var>afterLength</var>
+ * characters of text after the cursor (end of the selection), and all of the selected
+ * text. The range are for java characters, not glyphs that can be multiple characters.
+ *
+ * <p>This method may fail either if the input connection has become invalid (such as its
+ * process crashing), or the client is taking too long to respond with the text (it is
+ * given a couple seconds to return), or the protocol is not supported. In any of these
+ * cases, null is returned.
+ *
+ * <p>This method does not affect the text in the editor in any way, nor does it affect the
+ * selection or composing spans.</p>
+ *
+ * <p>If {@link InputConnection#GET_TEXT_WITH_STYLES} is supplied as flags, the editor
+ * should return a {@link android.text.Spanned} with all the spans set on the text.</p>
+ *
+ * <p><strong>Accessibility service authors:</strong> please consider this will trigger an
+ * IPC round-trip that will take some time. Assume this method consumes a lot of time.
+ *
+ * @param beforeLength The expected length of the text before the cursor.
+ * @param afterLength The expected length of the text after the cursor.
+ * @param flags Supplies additional options controlling how the text is returned. May be
+ * either {@code 0} or {@link InputConnection#GET_TEXT_WITH_STYLES}.
+ * @return an {@link android.view.inputmethod.SurroundingText} object describing the
+ * surrounding text and state of selection, or null if the input connection is no longer
+ * valid, or the editor can't comply with the request for some reason, or the application
+ * does not implement this method. The length of the returned text might be less than the
+ * sum of <var>beforeLength</var> and <var>afterLength</var> .
+ * @throws IllegalArgumentException if {@code beforeLength} or {@code afterLength} is
+ * negative.
+ */
+ @Nullable
+ public SurroundingText getSurroundingText(
+ @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength,
+ @InputConnection.GetTextType int flags) {
+ if (mIc != null) {
+ return mIc.getSurroundingText(beforeLength, afterLength, flags);
+ }
+ return null;
+ }
+
+ /**
+ * Delete <var>beforeLength</var> characters of text before the
+ * current cursor position, and delete <var>afterLength</var>
+ * characters of text after the current cursor position, excluding
+ * the selection. Before and after refer to the order of the
+ * characters in the string, not to their visual representation:
+ * this means you don't have to figure out the direction of the
+ * text and can just use the indices as-is.
+ *
+ * <p>The lengths are supplied in Java chars, not in code points
+ * or in glyphs.</p>
+ *
+ * <p>Since this method only operates on text before and after the
+ * selection, it can't affect the contents of the selection. This
+ * may affect the composing span if the span includes characters
+ * that are to be deleted, but otherwise will not change it. If
+ * some characters in the composing span are deleted, the
+ * composing span will persist but get shortened by however many
+ * chars inside it have been removed.</p>
+ *
+ * <p><strong>Accessibility service authors:</strong> please be careful not to
+ * delete only half of a surrogate pair. Also take care not to
+ * delete more characters than are in the editor, as that may have
+ * ill effects on the application. Calling this method will cause
+ * the editor to call
+ * {@link android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
+ * int, int)} on your service after the batch input is over.</p>
+ *
+ * <p><strong>Editor authors:</strong> please be careful of race
+ * conditions in implementing this call. An IME can make a change
+ * to the text or change the selection position and use this
+ * method right away; you need to make sure the effects are
+ * consistent with the results of the latest edits. Also, although
+ * the IME should not send lengths bigger than the contents of the
+ * string, you should check the values for overflows and trim the
+ * indices to the size of the contents to avoid crashes. Since
+ * this changes the contents of the editor, you need to make the
+ * changes known to the input method by calling
+ * {@link InputMethodManager#updateSelection(View, int, int, int, int)},
+ * but be careful to wait until the batch edit is over if one is
+ * in progress.</p>
+ *
+ * @param beforeLength The number of characters before the cursor to be deleted, in code
+ * unit. If this is greater than the number of existing characters between the
+ * beginning of the text and the cursor, then this method does not fail but deletes
+ * all the characters in that range.
+ * @param afterLength The number of characters after the cursor to be deleted, in code unit.
+ * If this is greater than the number of existing characters between the cursor and
+ * the end of the text, then this method does not fail but deletes all the characters
+ * in that range.
+ */
+ public void deleteSurroundingText(int beforeLength, int afterLength) {
+ if (mIc != null) {
+ mIc.deleteSurroundingText(beforeLength, afterLength);
+ }
+ }
+
+ /**
+ * Send a key event to the process that is currently attached
+ * through this input connection. The event will be dispatched
+ * like a normal key event, to the currently focused view; this
+ * generally is the view that is providing this InputConnection,
+ * but due to the asynchronous nature of this protocol that can
+ * not be guaranteed and the focus may have changed by the time
+ * the event is received.
+ *
+ * <p>This method can be used to send key events to the
+ * application. For example, an on-screen keyboard may use this
+ * method to simulate a hardware keyboard. There are three types
+ * of standard keyboards, numeric (12-key), predictive (20-key)
+ * and ALPHA (QWERTY). You can specify the keyboard type by
+ * specify the device id of the key event.</p>
+ *
+ * <p>You will usually want to set the flag
+ * {@link KeyEvent#FLAG_SOFT_KEYBOARD KeyEvent.FLAG_SOFT_KEYBOARD}
+ * on all key event objects you give to this API; the flag will
+ * not be set for you.</p>
+ *
+ * <p>Note that it's discouraged to send such key events in normal
+ * operation; this is mainly for use with
+ * {@link android.text.InputType#TYPE_NULL} type text fields. Use
+ * the {@link #commitText} family of methods to send text to the
+ * application instead.</p>
+ *
+ * @param event The key event.
+ *
+ * @see KeyEvent
+ * @see KeyCharacterMap#NUMERIC
+ * @see KeyCharacterMap#PREDICTIVE
+ * @see KeyCharacterMap#ALPHA
+ */
+ public void sendKeyEvent(@NonNull KeyEvent event) {
+ if (mIc != null) {
+ mIc.sendKeyEvent(event);
+ }
+ }
+
+ /**
+ * Have the editor perform an action it has said it can do.
+ *
+ * @param editorAction This must be one of the action constants for
+ * {@link EditorInfo#imeOptions EditorInfo.imeOptions}, such as
+ * {@link EditorInfo#IME_ACTION_GO EditorInfo.EDITOR_ACTION_GO}, or the value of
+ * {@link EditorInfo#actionId EditorInfo.actionId} if a custom action is available.
+ */
+ public void performEditorAction(int editorAction) {
+ if (mIc != null) {
+ mIc.performEditorAction(editorAction);
+ }
+ }
+
+ /**
+ * Perform a context menu action on the field. The given id may be one of:
+ * {@link android.R.id#selectAll},
+ * {@link android.R.id#startSelectingText}, {@link android.R.id#stopSelectingText},
+ * {@link android.R.id#cut}, {@link android.R.id#copy},
+ * {@link android.R.id#paste}, {@link android.R.id#copyUrl},
+ * or {@link android.R.id#switchInputMethod}
+ */
+ public void performContextMenuAction(int id) {
+ if (mIc != null) {
+ mIc.performContextMenuAction(id);
+ }
+ }
+
+ /**
+ * Retrieve the current capitalization mode in effect at the
+ * current cursor position in the text. See
+ * {@link android.text.TextUtils#getCapsMode TextUtils.getCapsMode}
+ * for more information.
+ *
+ * <p>This method may fail either if the input connection has
+ * become invalid (such as its process crashing) or the client is
+ * taking too long to respond with the text (it is given a couple
+ * seconds to return). In either case, 0 is returned.</p>
+ *
+ * <p>This method does not affect the text in the editor in any
+ * way, nor does it affect the selection or composing spans.</p>
+ *
+ * <p><strong>Editor authors:</strong> please be careful of race
+ * conditions in implementing this call. An IME can change the
+ * cursor position and use this method right away; you need to make
+ * sure the returned value is consistent with the results of the
+ * latest edits and changes to the cursor position.</p>
+ *
+ * @param reqModes The desired modes to retrieve, as defined by
+ * {@link android.text.TextUtils#getCapsMode TextUtils.getCapsMode}. These
+ * constants are defined so that you can simply pass the current
+ * {@link EditorInfo#inputType TextBoxAttribute.contentType} value
+ * directly in to here.
+ * @return the caps mode flags that are in effect at the current
+ * cursor position. See TYPE_TEXT_FLAG_CAPS_* in {@link android.text.InputType}.
+ */
+ public int getCursorCapsMode(int reqModes) {
+ if (mIc != null) {
+ return mIc.getCursorCapsMode(reqModes);
+ }
+ return 0;
+ }
+
+ /**
+ * Clear the given meta key pressed states in the given input
+ * connection.
+ *
+ * <p>This can be used by the accessibility service to clear the meta key states set
+ * by a hardware keyboard with latched meta keys, if the editor
+ * keeps track of these.</p>
+ *
+ * @param states The states to be cleared, may be one or more bits as
+ * per {@link KeyEvent#getMetaState() KeyEvent.getMetaState()}.
+ */
+ public void clearMetaKeyStates(int states) {
+ if (mIc != null) {
+ mIc.clearMetaKeyStates(states);
+ }
+ }
+ }
+
+ /**
+ * Concrete implementation of InputMethodSession that provides all of the standard behavior
+ * for an input method session.
+ */
+ private final class InputMethodSessionForAccessibility implements InputMethodSession {
+ boolean mEnabled = true;
+
+ public void setEnabled(boolean enabled) {
+ mEnabled = enabled;
+ }
+
+ @Override
+ public void finishInput() {
+ if (mEnabled) {
+ doFinishInput();
+ }
+ }
+
+ @Override
+ public void updateSelection(int oldSelStart, int oldSelEnd, int newSelStart,
+ int newSelEnd, int candidatesStart, int candidatesEnd) {
+ if (mEnabled) {
+ InputMethod.this.onUpdateSelection(oldSelEnd, oldSelEnd, newSelStart,
+ newSelEnd, candidatesStart, candidatesEnd);
+ }
+ }
+
+ @Override
+ public void viewClicked(boolean focusChanged) {
+ }
+
+ @Override
+ public void updateCursor(@NonNull Rect newCursor) {
+ }
+
+ @Override
+ public void displayCompletions(
+ @SuppressLint("ArrayReturn") @NonNull CompletionInfo[] completions) {
+ }
+
+ @Override
+ public void updateExtractedText(int token, @NonNull ExtractedText text) {
+ }
+
+ public void dispatchKeyEvent(int seq, @NonNull KeyEvent event,
+ @NonNull @CallbackExecutor Executor executor, @NonNull EventCallback callback) {
+ }
+
+ @Override
+ public void dispatchKeyEvent(int seq, @NonNull KeyEvent event,
+ @NonNull EventCallback callback) {
+ }
+
+ public void dispatchTrackballEvent(int seq, @NonNull MotionEvent event,
+ @NonNull @CallbackExecutor Executor executor, @NonNull EventCallback callback) {
+ }
+
+ @Override
+ public void dispatchTrackballEvent(int seq, @NonNull MotionEvent event,
+ @NonNull EventCallback callback) {
+ }
+
+ public void dispatchGenericMotionEvent(int seq, @NonNull MotionEvent event,
+ @NonNull @CallbackExecutor Executor executor, @NonNull EventCallback callback) {
+ }
+
+ @Override
+ public void dispatchGenericMotionEvent(int seq, @NonNull MotionEvent event,
+ @NonNull EventCallback callback) {
+ }
+
+ @Override
+ public void appPrivateCommand(@NonNull String action, @NonNull Bundle data) {
+ }
+
+ @Override
+ public void toggleSoftInput(int showFlags, int hideFlags) {
+ }
+
+ @Override
+ public void updateCursorAnchorInfo(@NonNull CursorAnchorInfo cursorAnchorInfo) {
+ }
+
+ @Override
+ public void notifyImeHidden() {
+ }
+
+ @Override
+ public void removeImeSurface() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void invalidateInputInternal(EditorInfo editorInfo, IInputContext inputContext,
+ int sessionId) {
+ // TODO(b/217788708): Add automated test.
+ if (mStartedInputConnection instanceof RemoteInputConnection) {
+ final RemoteInputConnection ric =
+ (RemoteInputConnection) mStartedInputConnection;
+ if (!ric.isSameConnection(inputContext)) {
+ // This is not an error, and can be safely ignored.
+ return;
+ }
+ editorInfo.makeCompatible(
+ mService.getApplicationInfo().targetSdkVersion);
+ restartInput(new RemoteInputConnection(ric, sessionId), editorInfo);
+ }
+ }
+ }
+}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index e7b6646..a31aa28 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -8662,8 +8662,18 @@
}
/**
- * If set to true, this indicates to the system that it should never take a
- * screenshot of the activity to be used as a representation while it is not in a started state.
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.S,
+ publicAlternatives = "Use {@link #setRecentsScreenshotEnabled(boolean)} instead.")
+ public void setDisablePreviewScreenshots(boolean disable) {
+ setRecentsScreenshotEnabled(!disable);
+ }
+
+ /**
+ * If set to false, this indicates to the system that it should never take a
+ * screenshot of the activity to be used as a representation in recents screen. By default, this
+ * value is {@code true}.
* <p>
* Note that the system may use the window background of the theme instead to represent
* the window when it is not running.
@@ -8676,12 +8686,10 @@
* {@link android.service.voice.VoiceInteractionService} requests a screenshot via
* {@link android.service.voice.VoiceInteractionSession#SHOW_WITH_SCREENSHOT}.
*
- * @param disable {@code true} to disable preview screenshots; {@code false} otherwise.
- * @hide
+ * @param enabled {@code true} to enable recents screenshots; {@code false} otherwise.
*/
- @UnsupportedAppUsage
- public void setDisablePreviewScreenshots(boolean disable) {
- ActivityClient.getInstance().setDisablePreviewScreenshots(mToken, disable);
+ public void setRecentsScreenshotEnabled(boolean enabled) {
+ ActivityClient.getInstance().setRecentsScreenshotEnabled(mToken, enabled);
}
/**
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index 605a1fa..4715e0f 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -438,9 +438,9 @@
}
}
- void setDisablePreviewScreenshots(IBinder token, boolean disable) {
+ void setRecentsScreenshotEnabled(IBinder token, boolean enabled) {
try {
- getActivityClientController().setDisablePreviewScreenshots(token, disable);
+ getActivityClientController().setRecentsScreenshotEnabled(token, enabled);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index a58ceaa..cce7dd3 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -215,14 +215,6 @@
public abstract boolean isSystemReady();
/**
- * Returns package name given pid.
- *
- * @param pid The pid we are searching package name for.
- */
- @Nullable
- public abstract String getPackageNameByPid(int pid);
-
- /**
* Sets if the given pid has an overlay UI or not.
*
* @param pid The pid we are setting overlay UI for.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 5b8969e..61d1865 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2132,7 +2132,16 @@
Looper.myLooper().quit();
break;
case RECEIVER:
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveComp");
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ ReceiverData rec = (ReceiverData) msg.obj;
+ if (rec.intent != null) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "broadcastReceiveComp: " + rec.intent.getAction());
+ } else {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "broadcastReceiveComp");
+ }
+ }
handleReceiver((ReceiverData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 8181a74..ec2115c 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1998,7 +1998,7 @@
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
String instanceName, Handler handler, Executor executor, UserHandle user) {
// Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser and
- // ActivityManagerService.LocalService.startAndBindSupplementalProcessService
+ // ActivityManagerLocal.bindSupplementalProcessService
IServiceConnection sd;
if (conn == null) {
throw new IllegalArgumentException("connection is null");
diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java
index 040399e..6f49c9e 100644
--- a/core/java/android/app/GameManager.java
+++ b/core/java/android/app/GameManager.java
@@ -109,6 +109,7 @@
*
* @hide
*/
+ @TestApi
@UserHandleAware
@RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
public @GameMode int getGameMode(@NonNull String packageName) {
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index 396e552..f9439cb 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -115,8 +115,8 @@
int enterAnim, int exitAnim, int backgroundColor);
int setVrMode(in IBinder token, boolean enabled, in ComponentName packageName);
- /** See {@link android.app.Activity#setDisablePreviewScreenshots}. */
- oneway void setDisablePreviewScreenshots(in IBinder token, boolean disable);
+ /** See {@link android.app.Activity#setRecentsScreenshotEnabled}. */
+ oneway void setRecentsScreenshotEnabled(in IBinder token, boolean enabled);
/**
* It should only be called from home activity to remove its outdated snapshot. The home
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index c5add66..cc8b182 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -130,6 +130,7 @@
in ProfilerInfo profilerInfo, in Bundle options, int userId);
int startAssistantActivity(in String callingPackage, in String callingFeatureId, int callingPid,
int callingUid, in Intent intent, in String resolvedType, in Bundle options, int userId);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY)")
int startActivityFromGameSession(IApplicationThread caller, in String callingPackage,
in String callingFeatureId, int callingPid, int callingUid, in Intent intent,
int taskId, int userId);
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index a74438a..e0c69df 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -381,10 +381,6 @@
* Force the global system in or out of touch mode. This can be used if your
* instrumentation relies on the UI being in one more or the other when it starts.
*
- * <p><b>Note:</b> Starting from Android {@link Build.VERSION_CODES#TIRAMISU}, this method
- * will only have an effect if the calling process is also the focused window owner or has
- * {@link android.permission#MODIFY_TOUCH_MODE_STATE} permission granted.
- *
* @param inTouch Set to true to be in touch mode, false to be in focus mode.
*/
public void setInTouchMode(boolean inTouch) {
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 38e344e..77c7c6f 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1734,7 +1734,11 @@
return;
}
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveReg");
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "broadcastReceiveReg: " + intent.getAction());
+ }
+
try {
ClassLoader cl = mReceiver.getClass().getClassLoader();
intent.setExtrasClassLoader(cl);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 4aa2d2e..392f52a 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -2573,11 +2573,18 @@
* method will return false regardless of input.
* </p>
* <p>
- * The provided URI must meet the requirements for a URI associated with a
- * {@link Person}: it may be the {@code String} representation of a
- * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}, or a
- * <code>mailto:</code> or <code>tel:</code> schema URI matching an entry in the
- * Contacts database. See also {@link Person.Builder#setUri} and
+ * The provided URI should be a <code>tel:</code> or <code>mailto:</code> schema URI indicating
+ * the source of the call. For an accurate answer regarding whether the caller matches the
+ * user's permitted contacts, the path part of the URI must match an entry the Contacts database
+ * in the appropriate column.
+ * </p>
+ * <p>
+ * Passing in a {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI} is also
+ * permissible, but should only be used for priority contact interruptions and may not provide
+ * accurate results in the case of repeat callers.
+ * </p>
+ * <p>
+ * See also {@link Person.Builder#setUri} and
* {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}
* for more information.
* </p>
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index f5f2fe0..eeb4705 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -25,7 +25,7 @@
import android.app.admin.DevicePolicyManager;
import android.app.admin.IDevicePolicyManager;
import android.app.ambientcontext.AmbientContextManager;
-import android.app.ambientcontext.IAmbientContextEventObserver;
+import android.app.ambientcontext.IAmbientContextManager;
import android.app.appsearch.AppSearchManagerFrameworkInitializer;
import android.app.blob.BlobStoreManagerFrameworkInitializer;
import android.app.cloudsearch.CloudSearchManager;
@@ -1542,8 +1542,8 @@
throws ServiceNotFoundException {
IBinder iBinder = ServiceManager.getServiceOrThrow(
Context.AMBIENT_CONTEXT_SERVICE);
- IAmbientContextEventObserver manager =
- IAmbientContextEventObserver.Stub.asInterface(iBinder);
+ IAmbientContextManager manager =
+ IAmbientContextManager.Stub.asInterface(iBinder);
return new AmbientContextManager(ctx.getOuterContext(), manager);
}});
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index b41b5f00..2af8905 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -63,9 +63,14 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputBinding;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodSession;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.view.IInputSessionWithIdCallback;
import libcore.io.IoUtils;
@@ -1566,6 +1571,29 @@
}
@Override
+ public void createImeSession(IInputSessionWithIdCallback callback) {
+ /* do nothing */
+ }
+
+ @Override
+ public void setImeSessionEnabled(InputMethodSession session, boolean enabled) {
+ }
+
+ @Override
+ public void bindInput(InputBinding binding) {
+ }
+
+ @Override
+ public void unbindInput() {
+ }
+
+ @Override
+ public void startInput(@Nullable InputConnection inputConnection,
+ @NonNull EditorInfo editorInfo, boolean restarting,
+ @NonNull IBinder startInputToken) {
+ }
+
+ @Override
public boolean onGesture(AccessibilityGestureEvent gestureEvent) {
/* do nothing */
return false;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 71d4ab4..e52ae51 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -720,6 +720,125 @@
"android.app.extra.PROVISIONING_ALLOW_OFFLINE";
/**
+ * A String extra holding a url that specifies the download location of the device manager
+ * role holder package.
+ *
+ * <p>This is only meant to be used in cases when a specific variant of the role holder package
+ * is needed (such as a debug variant). If not provided, the default variant of the device
+ * manager role holder package is downloaded.
+ *
+ * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}
+ * or in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_PROVISIONING_ROLE_HOLDER_PACKAGE_DOWNLOAD_LOCATION =
+ "android.app.extra.PROVISIONING_ROLE_HOLDER_PACKAGE_DOWNLOAD_LOCATION";
+
+ /**
+ * A String extra holding the URL-safe base64 encoded SHA-256 checksum of any signature of the
+ * android package archive at the download location specified in {@link
+ * #EXTRA_PROVISIONING_ROLE_HOLDER_PACKAGE_DOWNLOAD_LOCATION}.
+ *
+ * <p>The signatures of an android package archive can be obtained using
+ * {@link android.content.pm.PackageManager#getPackageArchiveInfo} with flag
+ * {@link android.content.pm.PackageManager#GET_SIGNING_CERTIFICATES}.
+ *
+ * <p>If {@link #EXTRA_PROVISIONING_ROLE_HOLDER_PACKAGE_DOWNLOAD_LOCATION} is provided, it must
+ * be accompanied by this extra. The provided checksum must match the checksum of any signature
+ * of the file at the download location. If the checksum does not match an error will be shown
+ * to the user and the user will be asked to factory reset the device.
+ *
+ * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}
+ * or in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_PROVISIONING_ROLE_HOLDER_SIGNATURE_CHECKSUM =
+ "android.app.extra.PROVISIONING_ROLE_HOLDER_SIGNATURE_CHECKSUM";
+
+ /**
+ * A String extra holding a http cookie header which should be used in the http request to the
+ * url specified in {@link #EXTRA_PROVISIONING_ROLE_HOLDER_PACKAGE_DOWNLOAD_LOCATION}.
+ *
+ * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}
+ * or in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_PROVISIONING_ROLE_HOLDER_PACKAGE_DOWNLOAD_COOKIE_HEADER =
+ "android.app.extra.PROVISIONING_ROLE_HOLDER_PACKAGE_DOWNLOAD_COOKIE_HEADER";
+
+ /**
+ * An extra of type {@link android.os.PersistableBundle} that allows the provisioning initiator
+ * to pass data to the device manager role holder.
+ *
+ * <p>The device manager role holder will receive this extra via the {@link
+ * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent.
+ *
+ * <p>The contents of this extra are up to the contract between the provisioning initiator
+ * and the device manager role holder.
+ *
+ * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}
+ * or in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_PROVISIONING_ROLE_HOLDER_EXTRAS_BUNDLE =
+ "android.app.extra.PROVISIONING_ROLE_HOLDER_EXTRAS_BUNDLE";
+
+ /**
+ * A String extra containing the package name of the provisioning initiator.
+ *
+ * <p>Use in an intent with action {@link
+ * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_ROLE_HOLDER_PROVISIONING_INITIATOR_PACKAGE =
+ "android.app.extra.ROLE_HOLDER_PROVISIONING_INITIATOR_PACKAGE";
+
+ /**
+ * An {@link Intent} result extra specifying the {@link Intent} to be launched after
+ * provisioning is finalized.
+ *
+ * <p>If {@link #EXTRA_PROVISIONING_SHOULD_LAUNCH_RESULT_INTENT} is set to {@code false},
+ * this result will be supplied as part of the result {@link Intent} for provisioning actions
+ * such as {@link #ACTION_PROVISION_MANAGED_PROFILE}. This result will also be supplied as
+ * part of the result {@link Intent} for the device manager role holder provisioning actions.
+ */
+ public static final String EXTRA_RESULT_LAUNCH_INTENT =
+ "android.app.extra.RESULT_LAUNCH_INTENT";
+
+ /**
+ * A boolean extra that determines whether the provisioning flow should launch the resulting
+ * launch intent, if one is supplied by the device manager role holder via {@link
+ * #EXTRA_RESULT_LAUNCH_INTENT}. Default value is {@code false}.
+ *
+ * <p>If {@code true}, the resulting intent will be launched by the provisioning flow, if one
+ * is supplied by the device manager role holder.
+ *
+ * <p>If {@code false}, the resulting intent will be returned as {@link
+ * #EXTRA_RESULT_LAUNCH_INTENT} to the provisioning initiator, if one is supplied by the device
+ * manager role holder. It will be the responsibility of the provisioning initiator to launch
+ * this {@link Intent} after provisioning completes.
+ *
+ * <p>This extra is respected when provided via the provisioning intent actions such as {@link
+ * #ACTION_PROVISION_MANAGED_PROFILE}.
+ */
+ public static final String EXTRA_PROVISIONING_SHOULD_LAUNCH_RESULT_INTENT =
+ "android.app.extra.PROVISIONING_SHOULD_LAUNCH_RESULT_INTENT";
+
+ /**
* Action: Bugreport sharing with device owner has been accepted by the user.
*
* @hide
@@ -3585,10 +3704,11 @@
@RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
android.Manifest.permission.INTERACT_ACROSS_USERS})
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @UserHandleAware
public void acknowledgeNewUserDisclaimer() {
if (mService != null) {
try {
- mService.acknowledgeNewUserDisclaimer();
+ mService.acknowledgeNewUserDisclaimer(mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -3596,17 +3716,18 @@
}
/**
- * Checks whether the new managed user disclaimer was viewed by the current user.
+ * Checks whether the new managed user disclaimer was viewed by the user.
*
* @hide
*/
@RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
android.Manifest.permission.INTERACT_ACROSS_USERS})
@TestApi
+ @UserHandleAware
public boolean isNewUserDisclaimerAcknowledged() {
if (mService != null) {
try {
- return mService.isNewUserDisclaimerAcknowledged();
+ return mService.isNewUserDisclaimerAcknowledged(mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -15516,4 +15637,40 @@
}
}
}
+
+ /**
+ * Returns the package name of the device manager role holder.
+ *
+ * <p>If the device manager role holder is not configured for this device, returns {@code null}.
+ */
+ @Nullable
+ public String getDeviceManagerRoleHolderPackageName() {
+ String deviceManagerConfig =
+ mContext.getString(com.android.internal.R.string.config_deviceManager);
+ return extractPackageNameFromDeviceManagerConfig(deviceManagerConfig);
+ }
+
+ /**
+ * Retrieves the package name for a given {@code deviceManagerConfig}.
+ *
+ * <p>Valid configs look like:
+ * <ul>
+ * <li>{@code com.package.name}</li>
+ * <li>{@code com.package.name:<SHA256 checksum>}</li>
+ * </ul>
+ *
+ * <p>If the supplied {@code deviceManagerConfig} is {@code null} or empty, returns
+ * {@code null}.
+ */
+ @Nullable
+ private String extractPackageNameFromDeviceManagerConfig(
+ @Nullable String deviceManagerConfig) {
+ if (TextUtils.isEmpty(deviceManagerConfig)) {
+ return null;
+ }
+ if (deviceManagerConfig.contains(":")) {
+ return deviceManagerConfig.split(":")[0];
+ }
+ return deviceManagerConfig;
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 0e1caca..eedc042 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -271,8 +271,8 @@
int logoutUserInternal(); // AIDL doesn't allow overloading name (logoutUser())
int getLogoutUserId();
List<UserHandle> getSecondaryUsers(in ComponentName who);
- void acknowledgeNewUserDisclaimer();
- boolean isNewUserDisclaimerAcknowledged();
+ void acknowledgeNewUserDisclaimer(int userId);
+ boolean isNewUserDisclaimerAcknowledged(int userId);
void enableSystemApp(in ComponentName admin, in String callerPackage, in String packageName);
int enableSystemAppWithIntent(in ComponentName admin, in String callerPackage, in Intent intent);
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventRequest.java b/core/java/android/app/ambientcontext/AmbientContextEventRequest.java
index 82b16a2..0557acb 100644
--- a/core/java/android/app/ambientcontext/AmbientContextEventRequest.java
+++ b/core/java/android/app/ambientcontext/AmbientContextEventRequest.java
@@ -23,6 +23,9 @@
import android.os.PersistableBundle;
import android.util.ArraySet;
+import com.android.internal.util.AnnotationValidations;
+import com.android.internal.util.Preconditions;
+
import java.util.HashSet;
import java.util.Set;
@@ -36,15 +39,17 @@
@NonNull private final Set<Integer> mEventTypes;
@NonNull private final PersistableBundle mOptions;
- AmbientContextEventRequest(
+ private AmbientContextEventRequest(
@NonNull Set<Integer> eventTypes,
@NonNull PersistableBundle options) {
this.mEventTypes = eventTypes;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mEventTypes);
+ AnnotationValidations.validate(NonNull.class, null, mEventTypes);
+ Preconditions.checkArgument(!eventTypes.isEmpty(), "eventTypes cannot be empty");
+ for (int eventType : eventTypes) {
+ AnnotationValidations.validate(AmbientContextEvent.EventCode.class, null, eventType);
+ }
this.mOptions = options;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mOptions);
+ AnnotationValidations.validate(NonNull.class, null, mOptions);
}
/**
@@ -80,16 +85,20 @@
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
- AmbientContextEventRequest(@NonNull Parcel in) {
+ private AmbientContextEventRequest(@NonNull Parcel in) {
Set<Integer> eventTypes = (Set<Integer>) in.readArraySet(Integer.class.getClassLoader());
PersistableBundle options = (PersistableBundle) in.readTypedObject(
PersistableBundle.CREATOR);
this.mEventTypes = eventTypes;
- com.android.internal.util.AnnotationValidations.validate(
+ AnnotationValidations.validate(
NonNull.class, null, mEventTypes);
+ Preconditions.checkArgument(!eventTypes.isEmpty(), "eventTypes cannot be empty");
+ for (int eventType : eventTypes) {
+ AnnotationValidations.validate(AmbientContextEvent.EventCode.class, null, eventType);
+ }
this.mOptions = options;
- com.android.internal.util.AnnotationValidations.validate(
+ AnnotationValidations.validate(
NonNull.class, null, mOptions);
}
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventResponse.java b/core/java/android/app/ambientcontext/AmbientContextEventResponse.java
deleted file mode 100644
index 472a78b..0000000
--- a/core/java/android/app/ambientcontext/AmbientContextEventResponse.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2022 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 android.app.ambientcontext;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.app.PendingIntent;
-import android.os.Parcelable;
-
-import com.android.internal.util.AnnotationValidations;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Represents a response from the {@code AmbientContextEvent} service.
- *
- * @hide
- */
-@SystemApi
-public final class AmbientContextEventResponse implements Parcelable {
- /**
- * An unknown status.
- */
- public static final int STATUS_UNKNOWN = 0;
- /**
- * The value of the status code that indicates success.
- */
- public static final int STATUS_SUCCESS = 1;
- /**
- * The value of the status code that indicates one or more of the
- * requested events are not supported.
- */
- public static final int STATUS_NOT_SUPPORTED = 2;
- /**
- * The value of the status code that indicates service not available.
- */
- public static final int STATUS_SERVICE_UNAVAILABLE = 3;
- /**
- * The value of the status code that microphone is disabled.
- */
- public static final int STATUS_MICROPHONE_DISABLED = 4;
- /**
- * The value of the status code that the app is not granted access.
- */
- public static final int STATUS_ACCESS_DENIED = 5;
-
- /** @hide */
- @IntDef(prefix = { "STATUS_" }, value = {
- STATUS_UNKNOWN,
- STATUS_SUCCESS,
- STATUS_NOT_SUPPORTED,
- STATUS_SERVICE_UNAVAILABLE,
- STATUS_MICROPHONE_DISABLED,
- STATUS_ACCESS_DENIED
- }) public @interface StatusCode {}
-
- @StatusCode private final int mStatusCode;
- @NonNull private final List<AmbientContextEvent> mEvents;
- @NonNull private final String mPackageName;
- @Nullable private final PendingIntent mActionPendingIntent;
-
- /** @hide */
- public static String statusToString(@StatusCode int value) {
- switch (value) {
- case STATUS_UNKNOWN:
- return "STATUS_UNKNOWN";
- case STATUS_SUCCESS:
- return "STATUS_SUCCESS";
- case STATUS_NOT_SUPPORTED:
- return "STATUS_NOT_SUPPORTED";
- case STATUS_SERVICE_UNAVAILABLE:
- return "STATUS_SERVICE_UNAVAILABLE";
- case STATUS_MICROPHONE_DISABLED:
- return "STATUS_MICROPHONE_DISABLED";
- case STATUS_ACCESS_DENIED:
- return "STATUS_ACCESS_DENIED";
- default: return Integer.toHexString(value);
- }
- }
-
- AmbientContextEventResponse(
- @StatusCode int statusCode,
- @NonNull List<AmbientContextEvent> events,
- @NonNull String packageName,
- @Nullable PendingIntent actionPendingIntent) {
- this.mStatusCode = statusCode;
- AnnotationValidations.validate(StatusCode.class, null, mStatusCode);
- this.mEvents = events;
- AnnotationValidations.validate(NonNull.class, null, mEvents);
- this.mPackageName = packageName;
- AnnotationValidations.validate(NonNull.class, null, mPackageName);
- this.mActionPendingIntent = actionPendingIntent;
- }
-
- /**
- * The status of the response.
- */
- public @StatusCode int getStatusCode() {
- return mStatusCode;
- }
-
- /**
- * The detected event.
- */
- public @NonNull List<AmbientContextEvent> getEvents() {
- return mEvents;
- }
-
- /**
- * The package to deliver the response to.
- */
- public @NonNull String getPackageName() {
- return mPackageName;
- }
-
- /**
- * A {@link PendingIntent} that the client should call to allow further actions by user.
- * For example, with {@link STATUS_ACCESS_DENIED}, the PendingIntent can redirect users to the
- * grant access activity.
- */
- public @Nullable PendingIntent getActionPendingIntent() {
- return mActionPendingIntent;
- }
-
- @Override
- public String toString() {
- return "AmbientContextEventResponse { " + "statusCode = " + mStatusCode + ", "
- + "events = " + mEvents + ", " + "packageName = " + mPackageName + ", "
- + "callbackPendingIntent = " + mActionPendingIntent + " }";
- }
-
- @Override
- public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
- byte flg = 0;
- if (mActionPendingIntent != null) flg |= 0x8;
- dest.writeByte(flg);
- dest.writeInt(mStatusCode);
- dest.writeParcelableList(mEvents, flags);
- dest.writeString(mPackageName);
- if (mActionPendingIntent != null) dest.writeTypedObject(mActionPendingIntent, flags);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- /** @hide */
- @SuppressWarnings({"unchecked", "RedundantCast"})
- AmbientContextEventResponse(@NonNull android.os.Parcel in) {
- byte flg = in.readByte();
- int statusCode = in.readInt();
- List<AmbientContextEvent> events = new ArrayList<>();
- in.readParcelableList(events, AmbientContextEvent.class.getClassLoader(),
- AmbientContextEvent.class);
- String packageName = in.readString();
- PendingIntent callbackPendingIntent = (flg & 0x8) == 0 ? null
- : (PendingIntent) in.readTypedObject(PendingIntent.CREATOR);
-
- this.mStatusCode = statusCode;
- AnnotationValidations.validate(
- StatusCode.class, null, mStatusCode);
- this.mEvents = events;
- AnnotationValidations.validate(
- NonNull.class, null, mEvents);
- this.mPackageName = packageName;
- AnnotationValidations.validate(
- NonNull.class, null, mPackageName);
- this.mActionPendingIntent = callbackPendingIntent;
- }
-
- public static final @NonNull Parcelable.Creator<AmbientContextEventResponse> CREATOR =
- new Parcelable.Creator<AmbientContextEventResponse>() {
- @Override
- public AmbientContextEventResponse[] newArray(int size) {
- return new AmbientContextEventResponse[size];
- }
-
- @Override
- public AmbientContextEventResponse createFromParcel(@NonNull android.os.Parcel in) {
- return new AmbientContextEventResponse(in);
- }
- };
-
- /**
- * A builder for {@link AmbientContextEventResponse}
- */
- @SuppressWarnings("WeakerAccess")
- public static final class Builder {
- private @StatusCode int mStatusCode;
- private @NonNull List<AmbientContextEvent> mEvents;
- private @NonNull String mPackageName;
- private @Nullable PendingIntent mCallbackPendingIntent;
- private long mBuilderFieldsSet = 0L;
-
- public Builder() {
- }
-
- /**
- * The status of the response.
- */
- public @NonNull Builder setStatusCode(@StatusCode int value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x1;
- mStatusCode = value;
- return this;
- }
-
- /**
- * Adds an event to the builder.
- */
- public @NonNull Builder addEvent(@NonNull AmbientContextEvent value) {
- checkNotUsed();
- if (mEvents == null) {
- mBuilderFieldsSet |= 0x2;
- mEvents = new ArrayList<>();
- }
- mEvents.add(value);
- return this;
- }
-
- /**
- * The package to deliver the response to.
- */
- public @NonNull Builder setPackageName(@NonNull String value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x4;
- mPackageName = value;
- return this;
- }
-
- /**
- * A {@link PendingIntent} that the client should call to allow further actions by user.
- * For example, with {@link STATUS_ACCESS_DENIED}, the PendingIntent can redirect users to
- * the grant access activity.
- */
- public @NonNull Builder setActionPendingIntent(@NonNull PendingIntent value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x8;
- mCallbackPendingIntent = value;
- return this;
- }
-
- /** Builds the instance. This builder should not be touched after calling this! */
- public @NonNull AmbientContextEventResponse build() {
- checkNotUsed();
- mBuilderFieldsSet |= 0x10; // Mark builder used
-
- if ((mBuilderFieldsSet & 0x1) == 0) {
- mStatusCode = STATUS_UNKNOWN;
- }
- if ((mBuilderFieldsSet & 0x2) == 0) {
- mEvents = new ArrayList<>();
- }
- if ((mBuilderFieldsSet & 0x4) == 0) {
- mPackageName = "";
- }
- if ((mBuilderFieldsSet & 0x8) == 0) {
- mCallbackPendingIntent = null;
- }
- AmbientContextEventResponse o = new AmbientContextEventResponse(
- mStatusCode,
- mEvents,
- mPackageName,
- mCallbackPendingIntent);
- return o;
- }
-
- private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x10) != 0) {
- throw new IllegalStateException(
- "This Builder should not be reused. Use a new Builder instance instead");
- }
- }
- }
-}
diff --git a/core/java/android/app/ambientcontext/AmbientContextManager.java b/core/java/android/app/ambientcontext/AmbientContextManager.java
index 6841d1b..7f913e7 100644
--- a/core/java/android/app/ambientcontext/AmbientContextManager.java
+++ b/core/java/android/app/ambientcontext/AmbientContextManager.java
@@ -17,116 +17,280 @@
package android.app.ambientcontext;
import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.os.Binder;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import com.android.internal.util.Preconditions;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
/**
- * Allows granted apps to register for particular pre-defined {@link AmbientContextEvent}s.
- * After successful registration, the app receives a callback on the provided {@link PendingIntent}
- * when the requested event is detected.
- * <p />
- *
- * Example:
- *
- * <pre><code>
- * // Create request
- * AmbientContextEventRequest request = new AmbientContextEventRequest.Builder()
- * .addEventType(AmbientContextEvent.EVENT_COUGH)
- * .addEventTYpe(AmbientContextEvent.EVENT_SNORE)
- * .build();
- * // Create PendingIntent
- * Intent intent = new Intent(actionString, null, context, MyBroadcastReceiver.class)
- * .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- * PendingIntent pendingIntent = PendingIntents.getBroadcastMutable(context, 0, intent, 0);
- * // Register for events
- * AmbientContextManager ambientContextManager =
- * context.getSystemService(AmbientContextManager.class);
- * ambientContextManager.registerObserver(request, pendingIntent);
- *
- * // Handle the callback intent in your receiver
- * {@literal @}Override
- * protected void onReceive(Context context, Intent intent) {
- * AmbientContextEventResponse response =
- * AmbientContextManager.getResponseFromIntent(intent);
- * if (response != null) {
- * if (response.getStatusCode() == AmbientContextEventResponse.STATUS_SUCCESS) {
- * // Do something useful with response.getEvent()
- * } else if (response.getStatusCode() == AmbientContextEventResponse.STATUS_ACCESS_DENIED) {
- * // Redirect users to grant access
- * PendingIntent callbackPendingIntent = response.getCallbackPendingIntent();
- * if (callbackPendingIntent != null) {
- * callbackPendingIntent.send();
- * }
- * } else ...
- * }
- * }
- * </code></pre>
+ * Allows granted apps to register for event types defined in {@link AmbientContextEvent}.
+ * After registration, the app receives a Consumer callback of the service status.
+ * If it is {@link STATUS_SUCCESSFUL}, when the requested events are detected, the provided
+ * {@link PendingIntent} callback will receive the list of detected {@link AmbientContextEvent}s.
+ * If it is {@link STATUS_ACCESS_DENIED}, the app can call {@link #startConsentActivity}
+ * to load the consent screen.
*
* @hide
*/
@SystemApi
@SystemService(Context.AMBIENT_CONTEXT_SERVICE)
public final class AmbientContextManager {
-
/**
- * The key of an Intent extra indicating the response.
+ * The bundle key for the service status query result, used in
+ * {@code RemoteCallback#sendResult}.
+ *
+ * @hide
*/
- public static final String EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE =
- "android.app.ambientcontext.extra.AMBIENT_CONTEXT_EVENT_RESPONSE";
+ public static final String STATUS_RESPONSE_BUNDLE_KEY =
+ "android.app.ambientcontext.AmbientContextStatusBundleKey";
/**
- * Allows clients to retrieve the response from the intent.
+ * The key of an intent extra indicating a list of detected {@link AmbientContextEvent}s.
+ * The intent is sent to the app in the app's registered {@link PendingIntent}.
+ */
+ public static final String EXTRA_AMBIENT_CONTEXT_EVENTS =
+ "android.app.ambientcontext.extra.AMBIENT_CONTEXT_EVENTS";
+
+ /**
+ * An unknown status.
+ */
+ public static final int STATUS_UNKNOWN = 0;
+
+ /**
+ * The value of the status code that indicates success.
+ */
+ public static final int STATUS_SUCCESS = 1;
+
+ /**
+ * The value of the status code that indicates one or more of the
+ * requested events are not supported.
+ */
+ public static final int STATUS_NOT_SUPPORTED = 2;
+
+ /**
+ * The value of the status code that indicates service not available.
+ */
+ public static final int STATUS_SERVICE_UNAVAILABLE = 3;
+
+ /**
+ * The value of the status code that microphone is disabled.
+ */
+ public static final int STATUS_MICROPHONE_DISABLED = 4;
+
+ /**
+ * The value of the status code that the app is not granted access.
+ */
+ public static final int STATUS_ACCESS_DENIED = 5;
+
+ /** @hide */
+ @IntDef(prefix = { "STATUS_" }, value = {
+ STATUS_UNKNOWN,
+ STATUS_SUCCESS,
+ STATUS_NOT_SUPPORTED,
+ STATUS_SERVICE_UNAVAILABLE,
+ STATUS_MICROPHONE_DISABLED,
+ STATUS_ACCESS_DENIED
+ }) public @interface StatusCode {}
+
+ /**
+ * Allows clients to retrieve the list of {@link AmbientContextEvent}s from the intent.
+ *
* @param intent received from the PendingIntent callback
*
- * @return the AmbientContextEventResponse, or null if not present
+ * @return the list of events, or an empty list if the intent doesn't have such events.
*/
- @Nullable
- public static AmbientContextEventResponse getResponseFromIntent(
- @NonNull Intent intent) {
- if (intent.hasExtra(AmbientContextManager.EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE)) {
- return intent.getParcelableExtra(EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE);
+ @NonNull public static List<AmbientContextEvent> getEventsFromIntent(@NonNull Intent intent) {
+ if (intent.hasExtra(AmbientContextManager.EXTRA_AMBIENT_CONTEXT_EVENTS)) {
+ return intent.getParcelableArrayListExtra(EXTRA_AMBIENT_CONTEXT_EVENTS);
} else {
- return null;
+ return new ArrayList<>();
}
}
private final Context mContext;
- private final IAmbientContextEventObserver mService;
+ private final IAmbientContextManager mService;
/**
* {@hide}
*/
- public AmbientContextManager(Context context, IAmbientContextEventObserver service) {
+ public AmbientContextManager(Context context, IAmbientContextManager service) {
mContext = context;
mService = service;
}
/**
+ * Queries the {@link AmbientContextEvent} service status for the calling package, and
+ * sends the result to the {@link Consumer} right after the call. This is used by foreground
+ * apps to check whether the requested events are enabled for detection on the device.
+ * If all events are enabled for detection, the response has
+ * {@link AmbientContextManager#STATUS_SUCCESS}.
+ * If any of the events are not consented by user, the response has
+ * {@link AmbientContextManager#STATUS_ACCESS_DENIED}, and the app can
+ * call {@link #startConsentActivity} to redirect the user to the consent screen.
+ * <p />
+ *
+ * Example:
+ *
+ * <pre><code>
+ * Set<Integer> eventTypes = new HashSet<>();
+ * eventTypes.add(AmbientContextEvent.EVENT_COUGH);
+ * eventTypes.add(AmbientContextEvent.EVENT_SNORE);
+ *
+ * // Create Consumer
+ * Consumer<Integer> statusConsumer = response -> {
+ * int status = status.getStatusCode();
+ * if (status == AmbientContextManager.STATUS_SUCCESS) {
+ * // Show user it's enabled
+ * } else if (status == AmbientContextManager.STATUS_ACCESS_DENIED) {
+ * // Send user to grant access
+ * startConsentActivity(eventTypes);
+ * }
+ * };
+ *
+ * // Query status
+ * AmbientContextManager ambientContextManager =
+ * context.getSystemService(AmbientContextManager.class);
+ * ambientContextManager.queryAmbientContextStatus(eventTypes, executor, statusConsumer);
+ * </code></pre>
+ *
+ * @param eventTypes The set of event codes to check status on.
+ * @param executor Executor on which to run the consumer callback.
+ * @param consumer The consumer that handles the status code.
+ */
+ @RequiresPermission(Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT)
+ public void queryAmbientContextServiceStatus(
+ @NonNull @AmbientContextEvent.EventCode Set<Integer> eventTypes,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull @StatusCode Consumer<Integer> consumer) {
+ try {
+ RemoteCallback callback = new RemoteCallback(result -> {
+ int status = result.getInt(STATUS_RESPONSE_BUNDLE_KEY);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> consumer.accept(status));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ });
+ mService.queryServiceStatus(integerSetToIntArray(eventTypes),
+ mContext.getOpPackageName(), callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Requests the consent data host to open an activity that allows users to modify consent.
+ *
+ * @param eventTypes The set of event codes to be consented.
+ */
+ @RequiresPermission(Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT)
+ public void startConsentActivity(
+ @NonNull @AmbientContextEvent.EventCode Set<Integer> eventTypes) {
+ try {
+ mService.startConsentActivity(
+ integerSetToIntArray(eventTypes), mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @NonNull
+ private static int[] integerSetToIntArray(@NonNull Set<Integer> integerSet) {
+ int[] intArray = new int[integerSet.size()];
+ int i = 0;
+ for (Integer type : integerSet) {
+ intArray[i++] = type;
+ }
+ return intArray;
+ }
+
+ /**
* Allows app to register as a {@link AmbientContextEvent} observer. The
* observer receives a callback on the provided {@link PendingIntent} when the requested
* event is detected. Registering another observer from the same package that has already been
* registered will override the previous observer.
+ * <p />
+ *
+ * Example:
+ *
+ * <pre><code>
+ * // Create request
+ * AmbientContextEventRequest request = new AmbientContextEventRequest.Builder()
+ * .addEventType(AmbientContextEvent.EVENT_COUGH)
+ * .addEventType(AmbientContextEvent.EVENT_SNORE)
+ * .build();
+ *
+ * // Create PendingIntent for delivering detection results to my receiver
+ * Intent intent = new Intent(actionString, null, context, MyBroadcastReceiver.class)
+ * .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ * PendingIntent pendingIntent =
+ * PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ *
+ * // Create Consumer of service status
+ * Consumer<Integer> statusConsumer = status -> {
+ * if (status == AmbientContextManager.STATUS_ACCESS_DENIED) {
+ * // User did not consent event detection. See #queryAmbientContextServiceStatus and
+ * // #startConsentActivity
+ * }
+ * };
+ *
+ * // Register as observer
+ * AmbientContextManager ambientContextManager =
+ * context.getSystemService(AmbientContextManager.class);
+ * ambientContextManager.registerObserver(request, pendingIntent, executor, statusConsumer);
+ *
+ * // Handle the list of {@link AmbientContextEvent}s in your receiver
+ * {@literal @}Override
+ * protected void onReceive(Context context, Intent intent) {
+ * List<AmbientContextEvent> events = AmbientContextManager.getEventsFromIntent(intent);
+ * if (!events.isEmpty()) {
+ * // Do something useful with the events.
+ * }
+ * }
+ * </code></pre>
*
* @param request The request with events to observe.
- * @param pendingIntent A mutable {@link PendingIntent} that will be dispatched when any
- * requested event is detected.
+ * @param resultPendingIntent A mutable {@link PendingIntent} that will be dispatched after the
+ * requested events are detected.
+ * @param executor Executor on which to run the consumer callback.
+ * @param statusConsumer A consumer that handles the status code, which is returned
+ * right after the call.
*/
@RequiresPermission(Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT)
public void registerObserver(
@NonNull AmbientContextEventRequest request,
- @NonNull PendingIntent pendingIntent) {
- Preconditions.checkArgument(!pendingIntent.isImmutable());
+ @NonNull PendingIntent resultPendingIntent,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull @StatusCode Consumer<Integer> statusConsumer) {
+ Preconditions.checkArgument(!resultPendingIntent.isImmutable());
try {
- mService.registerObserver(request, pendingIntent);
+ RemoteCallback callback = new RemoteCallback(result -> {
+ int statusCode = result.getInt(STATUS_RESPONSE_BUNDLE_KEY);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> statusConsumer.accept(statusCode));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ });
+ mService.registerObserver(request, resultPendingIntent, callback);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ambientcontext/IAmbientContextEventObserver.aidl b/core/java/android/app/ambientcontext/IAmbientContextManager.aidl
similarity index 66%
rename from core/java/android/app/ambientcontext/IAmbientContextEventObserver.aidl
rename to core/java/android/app/ambientcontext/IAmbientContextManager.aidl
index 9032fe1..3b15bcb 100644
--- a/core/java/android/app/ambientcontext/IAmbientContextEventObserver.aidl
+++ b/core/java/android/app/ambientcontext/IAmbientContextManager.aidl
@@ -18,13 +18,19 @@
import android.app.PendingIntent;
import android.app.ambientcontext.AmbientContextEventRequest;
+import android.os.RemoteCallback;
/**
- * Interface for an AmbientContextEventManager that provides access to AmbientContextEvents.
+ * Interface for an AmbientContextManager that provides access to AmbientContextEvents.
*
* @hide
*/
-oneway interface IAmbientContextEventObserver {
- void registerObserver(in AmbientContextEventRequest request, in PendingIntent pendingIntent);
+oneway interface IAmbientContextManager {
+ void registerObserver(in AmbientContextEventRequest request,
+ in PendingIntent resultPendingIntent,
+ in RemoteCallback statusCallback);
void unregisterObserver(in String callingPackage);
+ void queryServiceStatus(in int[] eventTypes, in String callingPackage,
+ in RemoteCallback statusCallback);
+ void startConsentActivity(in int[] eventTypes, in String callingPackage);
}
\ No newline at end of file
diff --git a/core/java/android/app/assist/ActivityId.java b/core/java/android/app/assist/ActivityId.java
index fb0d056..1cc4b02 100644
--- a/core/java/android/app/assist/ActivityId.java
+++ b/core/java/android/app/assist/ActivityId.java
@@ -22,6 +22,7 @@
import android.annotation.TestApi;
import android.os.IBinder;
import android.os.Parcel;
+import android.os.Parcelable;
import android.service.contentcapture.ContentCaptureService;
import android.view.contentcapture.ContentCaptureContext;
import android.view.translation.UiTranslationManager;
@@ -38,7 +39,8 @@
*/
@Immutable
@SystemApi
-public class ActivityId {
+@TestApi
+public final class ActivityId implements Parcelable {
/**
* The identifier of the task this activity is in.
@@ -53,6 +55,7 @@
/**
* @hide
*/
+ @TestApi
public ActivityId(int taskId, @Nullable IBinder activityId) {
mTaskId = taskId;
mActivityId = activityId;
@@ -87,13 +90,39 @@
}
/**
- * @hide
+ * {@inheritDoc}
*/
- public void writeToParcel(@NonNull Parcel dest, int parcelableFlags) {
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mTaskId);
dest.writeStrongBinder(mActivityId);
}
+ /**
+ * Creates {@link ActivityId} instances from parcels.
+ */
+ @NonNull
+ public static final Parcelable.Creator<ActivityId> CREATOR =
+ new Parcelable.Creator<ActivityId>() {
+ @Override
+ public ActivityId createFromParcel(Parcel parcel) {
+ return new ActivityId(parcel);
+ }
+
+ @Override
+ public ActivityId[] newArray(int size) {
+ return new ActivityId[size];
+ }
+ };
+
@Override
public String toString() {
return "ActivityId { taskId = " + mTaskId + ", activityId = " + mActivityId + " }";
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index bd8ba9e..257530b 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -260,9 +260,9 @@
}
/**
- * Indicates that the application would prefer the CompanionDeviceManager to collect an explicit
- * confirmation from the user before creating an association, even if such confirmation is not
- * required.
+ * Indicates whether the application requires the {@link CompanionDeviceManager} service to
+ * collect an explicit confirmation from the user before creating an association, even if
+ * such confirmation is not required from the service's perspective.
*
* @see Builder#setForceConfirmation(boolean)
*/
@@ -391,9 +391,9 @@
}
/**
- * Indicates whether the application would prefer the CompanionDeviceManager to collect an
- * explicit confirmation from the user before creating an association, even if such
- * confirmation is not required.
+ * Indicates whether the application requires the {@link CompanionDeviceManager} service to
+ * collect an explicit confirmation from the user before creating an association, even if
+ * such confirmation is not required from the service's perspective.
*/
@RequiresPermission(REQUEST_COMPANION_SELF_MANAGED)
@NonNull
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 1d0f7c0..45d0ad5 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -48,15 +48,16 @@
/** @hide */
@IntDef(prefix = "LOCK_STATE_",
- value = {LOCK_STATE_ALWAYS_LOCKED, LOCK_STATE_ALWAYS_UNLOCKED})
+ value = {LOCK_STATE_DEFAULT, LOCK_STATE_ALWAYS_UNLOCKED})
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
public @interface LockState {}
/**
- * Indicates that the lock state of the virtual device should be always locked.
+ * Indicates that the lock state of the virtual device will be the same as the default physical
+ * display.
*/
- public static final int LOCK_STATE_ALWAYS_LOCKED = 0;
+ public static final int LOCK_STATE_DEFAULT = 0;
/**
* Indicates that the lock state of the virtual device should be always unlocked.
@@ -199,7 +200,7 @@
*/
public static final class Builder {
- private @LockState int mLockState = LOCK_STATE_ALWAYS_LOCKED;
+ private @LockState int mLockState = LOCK_STATE_DEFAULT;
private Set<UserHandle> mUsersWithMatchingAccounts;
@Nullable private Set<ComponentName> mBlockedActivities;
@Nullable private Set<ComponentName> mAllowedActivities;
@@ -207,9 +208,9 @@
/**
* Sets the lock state of the device. The permission {@code ADD_ALWAYS_UNLOCKED_DISPLAY}
* is required if this is set to {@link #LOCK_STATE_ALWAYS_UNLOCKED}.
- * The default is {@link #LOCK_STATE_ALWAYS_LOCKED}.
+ * The default is {@link #LOCK_STATE_DEFAULT}.
*
- * @param lockState The lock state, either {@link #LOCK_STATE_ALWAYS_LOCKED} or
+ * @param lockState The lock state, either {@link #LOCK_STATE_DEFAULT} or
* {@link #LOCK_STATE_ALWAYS_UNLOCKED}.
*/
@RequiresPermission(value = ADD_ALWAYS_UNLOCKED_DISPLAY, conditional = true)
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index eccbb40..75356d1 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -41,7 +41,9 @@
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodSession;
-class IInputMethodSessionWrapper extends IInputMethodSession.Stub
+/** @hide */
+// TODO(b/215636776): move IInputMethodSessionWrapper to proper package
+public class IInputMethodSessionWrapper extends IInputMethodSession.Stub
implements HandlerCaller.Callback {
private static final String TAG = "InputMethodWrapper";
diff --git a/core/java/android/inputmethodservice/InputMethodServiceInternal.java b/core/java/android/inputmethodservice/InputMethodServiceInternal.java
index 7cd4ff6..09dbb2735 100644
--- a/core/java/android/inputmethodservice/InputMethodServiceInternal.java
+++ b/core/java/android/inputmethodservice/InputMethodServiceInternal.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Bundle;
import android.view.inputmethod.InputConnection;
@@ -31,8 +32,11 @@
* framework classes for internal use.
*
* <p>CAVEATS: {@link AbstractInputMethodService} does not support all the methods here.</p>
+ *
+ * @hide
*/
-interface InputMethodServiceInternal {
+// TODO(b/215636776): move InputMethodServiceInternal to proper package
+public interface InputMethodServiceInternal {
/**
* @return {@link Context} associated with the service.
*/
@@ -70,7 +74,8 @@
* closed for you after you return.
* @param args additional arguments to the dump request.
*/
- default void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ default void dump(@SuppressLint("UseParcelFileDescriptor") @NonNull FileDescriptor fd,
+ @NonNull PrintWriter fout, @NonNull String[] args) {
}
/**
@@ -81,6 +86,6 @@
* @param where {@code where} parameter to be passed.
* @param icProto {@code icProto} parameter to be passed.
*/
- default void triggerServiceDump(String where, @Nullable byte[] icProto) {
+ default void triggerServiceDump(@NonNull String where, @Nullable byte[] icProto) {
}
}
diff --git a/core/java/android/inputmethodservice/RemoteInputConnection.java b/core/java/android/inputmethodservice/RemoteInputConnection.java
index 9ef2579..6b7815d 100644
--- a/core/java/android/inputmethodservice/RemoteInputConnection.java
+++ b/core/java/android/inputmethodservice/RemoteInputConnection.java
@@ -53,8 +53,11 @@
*
* <p>See also {@link IInputContext} for the actual {@link android.os.Binder} IPC protocols under
* the hood.</p>
+ *
+ * @hide
*/
-final class RemoteInputConnection implements InputConnection {
+// TODO(b/215636776): move RemoteInputConnection to proper package
+public final class RemoteInputConnection implements InputConnection {
private static final String TAG = "RemoteInputConnection";
private static final int MAX_WAIT_TIME_MILLIS = 2000;
@@ -95,7 +98,7 @@
@NonNull
private final CancellationGroup mCancellationGroup;
- RemoteInputConnection(
+ public RemoteInputConnection(
@NonNull WeakReference<InputMethodServiceInternal> inputMethodService,
IInputContext inputContext, @NonNull CancellationGroup cancellationGroup) {
mImsInternal = new InputMethodServiceInternalHolder(inputMethodService);
@@ -108,7 +111,7 @@
return mInvoker.isSameConnection(inputContext);
}
- RemoteInputConnection(@NonNull RemoteInputConnection original, int sessionId) {
+ public RemoteInputConnection(@NonNull RemoteInputConnection original, int sessionId) {
mImsInternal = original.mImsInternal;
mInvoker = original.mInvoker.cloneWithSessionId(sessionId);
mCancellationGroup = original.mCancellationGroup;
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index ec752fd..0fd3e03 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -547,7 +547,8 @@
if (profile.excludeLocalRoutes && !profile.isBypassable) {
Log.w(TAG, "ExcludeLocalRoutes should only be set in the bypassable VPN");
}
- builder.setExcludeLocalRoutes(profile.excludeLocalRoutes && profile.isBypassable);
+
+ builder.setLocalRoutesExcluded(profile.excludeLocalRoutes && profile.isBypassable);
builder.setRequiresInternetValidation(profile.requiresInternetValidation);
return builder.build();
@@ -1104,7 +1105,7 @@
*/
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
- public Builder setExcludeLocalRoutes(boolean excludeLocalRoutes) {
+ public Builder setLocalRoutesExcluded(boolean excludeLocalRoutes) {
mExcludeLocalRoutes = excludeLocalRoutes;
return this;
}
diff --git a/core/java/android/net/PlatformVpnProfile.java b/core/java/android/net/PlatformVpnProfile.java
index 8bd1c8d..c0fb4cf 100644
--- a/core/java/android/net/PlatformVpnProfile.java
+++ b/core/java/android/net/PlatformVpnProfile.java
@@ -83,7 +83,7 @@
/**
* Returns whether the local traffic is exempted from the VPN.
*/
- public final boolean getExcludeLocalRoutes() {
+ public final boolean areLocalRoutesExcluded() {
return mExcludeLocalRoutes;
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index f1d4ba0..de1dc80 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -52,10 +52,9 @@
import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.os.BatterySipper;
-import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.BatteryUsageStatsProvider;
-import com.android.internal.os.PowerCalculator;
+
+import com.google.android.collect.Lists;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -70,6 +69,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
/**
@@ -3539,6 +3539,44 @@
}
}
+ /**
+ * Converts charge in mAh to string.
+ */
+ public static String formatCharge(double power) {
+ return formatValue(power);
+ }
+
+ /**
+ * Converts double to string, limiting small values to 3 significant figures.
+ */
+ private static String formatValue(double value) {
+ if (value == 0) return "0";
+
+ final String format;
+ if (value < .00001) {
+ format = "%.8f";
+ } else if (value < .0001) {
+ format = "%.7f";
+ } else if (value < .001) {
+ format = "%.6f";
+ } else if (value < .01) {
+ format = "%.5f";
+ } else if (value < .1) {
+ format = "%.4f";
+ } else if (value < 1) {
+ format = "%.3f";
+ } else if (value < 10) {
+ format = "%.2f";
+ } else if (value < 100) {
+ format = "%.1f";
+ } else {
+ format = "%.0f";
+ }
+
+ // Use English locale because this is never used in UI (only in checkin and dump).
+ return String.format(Locale.ENGLISH, format, value);
+ }
+
private static long roundUsToMs(long timeUs) {
return (timeUs + 500) / 1000;
}
@@ -4025,7 +4063,7 @@
sb.append(" ");
sb.append(controllerName);
sb.append(" Battery drain: ").append(
- PowerCalculator.formatCharge(powerDrainMaMs / MILLISECONDS_IN_HOUR));
+ formatCharge(powerDrainMaMs / MILLISECONDS_IN_HOUR));
sb.append("mAh");
pw.println(sb.toString());
}
@@ -4424,10 +4462,10 @@
final BatteryUsageStats stats = getBatteryUsageStats(context);
dumpLine(pw, 0 /* uid */, category, POWER_USE_SUMMARY_DATA,
- PowerCalculator.formatCharge(stats.getBatteryCapacity()),
- PowerCalculator.formatCharge(stats.getConsumedPower()),
- PowerCalculator.formatCharge(stats.getDischargedPowerRange().getLower()),
- PowerCalculator.formatCharge(stats.getDischargedPowerRange().getUpper()));
+ formatCharge(stats.getBatteryCapacity()),
+ formatCharge(stats.getConsumedPower()),
+ formatCharge(stats.getDischargedPowerRange().getLower()),
+ formatCharge(stats.getDischargedPowerRange().getUpper()));
final BatteryConsumer deviceConsumer = stats.getAggregateBatteryConsumer(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
for (@BatteryConsumer.PowerComponent int powerComponent = 0;
@@ -4437,7 +4475,7 @@
label = "???";
}
dumpLine(pw, 0 /* uid */, category, POWER_USE_ITEM_DATA, label,
- PowerCalculator.formatCharge(deviceConsumer.getConsumedPower(powerComponent)),
+ formatCharge(deviceConsumer.getConsumedPower(powerComponent)),
shouldHidePowerComponent(powerComponent) ? 1 : 0, "0", "0");
}
@@ -4447,11 +4485,10 @@
for (int i = 0; i < uidBatteryConsumers.size(); i++) {
UidBatteryConsumer consumer = uidBatteryConsumers.get(i);
dumpLine(pw, consumer.getUid(), category, POWER_USE_ITEM_DATA, "uid",
- PowerCalculator.formatCharge(consumer.getConsumedPower()),
+ formatCharge(consumer.getConsumedPower()),
proportionalAttributionCalculator.isSystemBatteryConsumer(consumer) ? 1 : 0,
- PowerCalculator.formatCharge(
- consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)),
- PowerCalculator.formatCharge(
+ formatCharge(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)),
+ formatCharge(
proportionalAttributionCalculator.getProportionalPowerMah(consumer)));
}
@@ -4885,11 +4922,11 @@
}
private void printmAh(PrintWriter printer, double power) {
- printer.print(PowerCalculator.formatCharge(power));
+ printer.print(formatCharge(power));
}
private void printmAh(StringBuilder sb, double power) {
- sb.append(PowerCalculator.formatCharge(power));
+ sb.append(formatCharge(power));
}
/**
@@ -4936,7 +4973,7 @@
sb.setLength(0);
sb.append(prefix);
sb.append(" Estimated battery capacity: ");
- sb.append(PowerCalculator.formatCharge(estimatedBatteryCapacity));
+ sb.append(formatCharge(estimatedBatteryCapacity));
sb.append(" mAh");
pw.println(sb.toString());
}
@@ -4946,7 +4983,7 @@
sb.setLength(0);
sb.append(prefix);
sb.append(" Last learned battery capacity: ");
- sb.append(PowerCalculator.formatCharge(lastLearnedBatteryCapacity / 1000));
+ sb.append(formatCharge(lastLearnedBatteryCapacity / 1000));
sb.append(" mAh");
pw.println(sb.toString());
}
@@ -4955,7 +4992,7 @@
sb.setLength(0);
sb.append(prefix);
sb.append(" Min learned battery capacity: ");
- sb.append(PowerCalculator.formatCharge(minLearnedBatteryCapacity / 1000));
+ sb.append(formatCharge(minLearnedBatteryCapacity / 1000));
sb.append(" mAh");
pw.println(sb.toString());
}
@@ -4964,7 +5001,7 @@
sb.setLength(0);
sb.append(prefix);
sb.append(" Max learned battery capacity: ");
- sb.append(PowerCalculator.formatCharge(maxLearnedBatteryCapacity / 1000));
+ sb.append(formatCharge(maxLearnedBatteryCapacity / 1000));
sb.append(" mAh");
pw.println(sb.toString());
}
@@ -5028,7 +5065,7 @@
sb.setLength(0);
sb.append(prefix);
sb.append(" Discharge: ");
- sb.append(PowerCalculator.formatCharge(dischargeCount / 1000.0));
+ sb.append(formatCharge(dischargeCount / 1000.0));
sb.append(" mAh");
pw.println(sb.toString());
}
@@ -5038,7 +5075,7 @@
sb.setLength(0);
sb.append(prefix);
sb.append(" Screen off discharge: ");
- sb.append(PowerCalculator.formatCharge(dischargeScreenOffCount / 1000.0));
+ sb.append(formatCharge(dischargeScreenOffCount / 1000.0));
sb.append(" mAh");
pw.println(sb.toString());
}
@@ -5048,7 +5085,7 @@
sb.setLength(0);
sb.append(prefix);
sb.append(" Screen doze discharge: ");
- sb.append(PowerCalculator.formatCharge(dischargeScreenDozeCount / 1000.0));
+ sb.append(formatCharge(dischargeScreenDozeCount / 1000.0));
sb.append(" mAh");
pw.println(sb.toString());
}
@@ -5058,7 +5095,7 @@
sb.setLength(0);
sb.append(prefix);
sb.append(" Screen on discharge: ");
- sb.append(PowerCalculator.formatCharge(dischargeScreenOnCount / 1000.0));
+ sb.append(formatCharge(dischargeScreenOnCount / 1000.0));
sb.append(" mAh");
pw.println(sb.toString());
}
@@ -5068,7 +5105,7 @@
sb.setLength(0);
sb.append(prefix);
sb.append(" Device light doze discharge: ");
- sb.append(PowerCalculator.formatCharge(dischargeLightDozeCount / 1000.0));
+ sb.append(formatCharge(dischargeLightDozeCount / 1000.0));
sb.append(" mAh");
pw.println(sb.toString());
}
@@ -5078,7 +5115,7 @@
sb.setLength(0);
sb.append(prefix);
sb.append(" Device deep doze discharge: ");
- sb.append(PowerCalculator.formatCharge(dischargeDeepDozeCount / 1000.0));
+ sb.append(formatCharge(dischargeDeepDozeCount / 1000.0));
sb.append(" mAh");
pw.println(sb.toString());
}
@@ -5212,7 +5249,7 @@
for (int iu = 0; iu < NU; iu++) {
final Uid u = uidStats.valueAt(iu);
- final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelocks
+ final ArrayMap<String, ? extends Uid.Wakelock> wakelocks
= u.getWakelockStats();
for (int iw=wakelocks.size()-1; iw>=0; iw--) {
final Uid.Wakelock wl = wakelocks.valueAt(iw);
@@ -5621,34 +5658,38 @@
.build());
stats.dump(pw, prefix);
- final BatteryStatsHelper helper = new BatteryStatsHelper(context, false, wifiOnly);
- helper.create(this);
- helper.refreshStats(which, UserHandle.USER_ALL);
-
- final List<BatterySipper> sippers = helper.getMobilemsppList();
- if (sippers != null && sippers.size() > 0) {
- pw.print(prefix); pw.println(" Per-app mobile ms per packet:");
+ List<UidMobileRadioStats> uidMobileRadioStats =
+ getUidMobileRadioStats(stats.getUidBatteryConsumers());
+ if (uidMobileRadioStats.size() > 0) {
+ pw.print(prefix);
+ pw.println(" Per-app mobile ms per packet:");
long totalTime = 0;
- for (int i=0; i<sippers.size(); i++) {
- final BatterySipper bs = sippers.get(i);
+ for (int i = 0; i < uidMobileRadioStats.size(); i++) {
+ final UidMobileRadioStats mrs = uidMobileRadioStats.get(i);
sb.setLength(0);
- sb.append(prefix); sb.append(" Uid ");
- UserHandle.formatUid(sb, bs.uidObj.getUid());
+ sb.append(prefix);
+ sb.append(" Uid ");
+ UserHandle.formatUid(sb, mrs.uid);
sb.append(": ");
- sb.append(PowerCalculator.formatCharge(bs.mobilemspp));
- sb.append(" ("); sb.append(bs.mobileRxPackets+bs.mobileTxPackets);
- sb.append(" packets over "); formatTimeMsNoSpace(sb, bs.mobileActive);
- sb.append(") "); sb.append(bs.mobileActiveCount); sb.append("x");
- pw.println(sb.toString());
- totalTime += bs.mobileActive;
+ sb.append(formatValue(mrs.millisecondsPerPacket));
+ sb.append(" (");
+ sb.append(mrs.rxPackets + mrs.txPackets);
+ sb.append(" packets over ");
+ formatTimeMsNoSpace(sb, mrs.radioActiveMs);
+ sb.append(") ");
+ sb.append(mrs.radioActiveCount);
+ sb.append("x");
+ pw.println(sb);
+ totalTime += mrs.radioActiveMs;
}
sb.setLength(0);
sb.append(prefix);
sb.append(" TOTAL TIME: ");
formatTimeMs(sb, totalTime);
- sb.append("("); sb.append(formatRatioLocked(totalTime, whichBatteryRealtime));
+ sb.append("(");
+ sb.append(formatRatioLocked(totalTime, whichBatteryRealtime));
sb.append(")");
- pw.println(sb.toString());
+ pw.println(sb);
pw.println();
}
@@ -5668,13 +5709,13 @@
};
if (reqUid < 0) {
- final Map<String, ? extends BatteryStats.Timer> kernelWakelocks
+ final Map<String, ? extends Timer> kernelWakelocks
= getKernelWakelockStats();
if (kernelWakelocks.size() > 0) {
final ArrayList<TimerEntry> ktimers = new ArrayList<>();
- for (Map.Entry<String, ? extends BatteryStats.Timer> ent
+ for (Map.Entry<String, ? extends Timer> ent
: kernelWakelocks.entrySet()) {
- final BatteryStats.Timer timer = ent.getValue();
+ final Timer timer = ent.getValue();
final long totalTimeMillis = computeWakeLock(timer, rawRealtime, which);
if (totalTimeMillis > 0) {
ktimers.add(new TimerEntry(ent.getKey(), 0, timer, totalTimeMillis));
@@ -5859,8 +5900,7 @@
packets = 1;
}
sb.append(" @ ");
- sb.append(PowerCalculator.formatCharge(
- uidMobileActiveTime / 1000 / (double) packets));
+ sb.append(formatCharge(uidMobileActiveTime / 1000 / (double) packets));
sb.append(" mspp");
pw.println(sb.toString());
}
@@ -6068,7 +6108,7 @@
}
}
- final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelocks
+ final ArrayMap<String, ? extends Uid.Wakelock> wakelocks
= u.getWakelockStats();
long totalFullWakelock = 0, totalPartialWakelock = 0, totalWindowWakelock = 0;
long totalDrawWakelock = 0;
@@ -6296,7 +6336,7 @@
uidActivity |= printTimer(pw, sb, u.getAudioTurnedOnTimer(), rawRealtime, which,
prefix, "Audio");
- final SparseArray<? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
+ final SparseArray<? extends Uid.Sensor> sensors = u.getSensorStats();
final int NSE = sensors.size();
for (int ise=0; ise<NSE; ise++) {
final Uid.Sensor se = sensors.valueAt(ise);
@@ -6439,7 +6479,7 @@
}
}
- final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats
+ final ArrayMap<String, ? extends Uid.Proc> processStats
= u.getProcessStats();
for (int ipr=processStats.size()-1; ipr>=0; ipr--) {
final Uid.Proc ps = processStats.valueAt(ipr);
@@ -6513,7 +6553,7 @@
}
}
- final ArrayMap<String, ? extends BatteryStats.Uid.Pkg> packageStats
+ final ArrayMap<String, ? extends Uid.Pkg> packageStats
= u.getPackageStats();
for (int ipkg=packageStats.size()-1; ipkg>=0; ipkg--) {
pw.print(prefix); pw.print(" Apk "); pw.print(packageStats.keyAt(ipkg));
@@ -6530,7 +6570,7 @@
}
final ArrayMap<String, ? extends Uid.Pkg.Serv> serviceStats = ps.getServiceStats();
for (int isvc=serviceStats.size()-1; isvc>=0; isvc--) {
- final BatteryStats.Uid.Pkg.Serv ss = serviceStats.valueAt(isvc);
+ final Uid.Pkg.Serv ss = serviceStats.valueAt(isvc);
final long startTime = ss.getStartTime(batteryUptime, which);
final int starts = ss.getStarts(which);
final int launches = ss.getLaunches(which);
@@ -8551,7 +8591,10 @@
proto.end(sToken);
}
- private static boolean checkWifiOnly(Context context) {
+ /**
+ * Returns true if the device does not have data-capable telephony.
+ */
+ public static boolean checkWifiOnly(Context context) {
final TelephonyManager tm = context.getSystemService(TelephonyManager.class);
if (tm == null) {
return false;
@@ -8652,4 +8695,57 @@
return false;
}
}
+
+ private static class UidMobileRadioStats {
+ public final int uid;
+ public final long rxPackets;
+ public final long txPackets;
+ public final long radioActiveMs;
+ public final int radioActiveCount;
+ public final double millisecondsPerPacket;
+
+ private UidMobileRadioStats(int uid, long rxPackets, long txPackets, long radioActiveMs,
+ int radioActiveCount, double millisecondsPerPacket) {
+ this.uid = uid;
+ this.txPackets = txPackets;
+ this.rxPackets = rxPackets;
+ this.radioActiveMs = radioActiveMs;
+ this.radioActiveCount = radioActiveCount;
+ this.millisecondsPerPacket = millisecondsPerPacket;
+ }
+ }
+
+ private List<UidMobileRadioStats> getUidMobileRadioStats(
+ List<UidBatteryConsumer> uidBatteryConsumers) {
+ final SparseArray<? extends Uid> uidStats = getUidStats();
+ List<UidMobileRadioStats> uidMobileRadioStats = Lists.newArrayList();
+ for (int i = 0; i < uidBatteryConsumers.size(); i++) {
+ final UidBatteryConsumer consumer = uidBatteryConsumers.get(i);
+ if (consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO) == 0) {
+ continue;
+ }
+
+ final int uid = consumer.getUid();
+ final Uid u = uidStats.get(uid);
+ final long rxPackets = u.getNetworkActivityPackets(
+ BatteryStats.NETWORK_MOBILE_RX_DATA, STATS_SINCE_CHARGED);
+ final long txPackets = u.getNetworkActivityPackets(
+ BatteryStats.NETWORK_MOBILE_TX_DATA, STATS_SINCE_CHARGED);
+ if (rxPackets == 0 && txPackets == 0) {
+ continue;
+ }
+ final long radioActiveMs = u.getMobileRadioActiveTime(STATS_SINCE_CHARGED) / 1000;
+ final int radioActiveCount = u.getMobileRadioActiveCount(STATS_SINCE_CHARGED);
+ final double msPerPacket = (double) radioActiveMs / (rxPackets + txPackets);
+ if (msPerPacket == 0) {
+ continue;
+ }
+ uidMobileRadioStats.add(
+ new UidMobileRadioStats(uid, rxPackets, txPackets, radioActiveMs,
+ radioActiveCount, msPerPacket));
+ }
+ uidMobileRadioStats.sort(
+ (lhs, rhs) -> Double.compare(rhs.millisecondsPerPacket, lhs.millisecondsPerPacket));
+ return uidMobileRadioStats;
+ }
}
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index d41a5fe..58f9336 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -597,20 +597,21 @@
dumpSortedBatteryConsumers(pw, prefix, getUidBatteryConsumers());
dumpSortedBatteryConsumers(pw, prefix, getUserBatteryConsumers());
+ pw.println();
}
private void printPowerComponent(PrintWriter pw, String prefix, String label,
double devicePowerMah, double appsPowerMah, int powerModel, long durationMs) {
StringBuilder sb = new StringBuilder();
sb.append(prefix).append(" ").append(label).append(": ")
- .append(PowerCalculator.formatCharge(devicePowerMah));
+ .append(BatteryStats.formatCharge(devicePowerMah));
if (powerModel != BatteryConsumer.POWER_MODEL_UNDEFINED
&& powerModel != BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
sb.append(" [");
sb.append(BatteryConsumer.powerModelToString(powerModel));
sb.append("]");
}
- sb.append(" apps: ").append(PowerCalculator.formatCharge(appsPowerMah));
+ sb.append(" apps: ").append(BatteryStats.formatCharge(appsPowerMah));
if (durationMs != 0) {
sb.append(" duration: ");
BatteryStats.formatTimeMs(sb, durationMs);
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index 48e1116..6051712 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -26,8 +26,6 @@
import android.util.TypedXmlSerializer;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.os.PowerCalculator;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -170,7 +168,7 @@
separator = " ";
sb.append(key.toShortString());
sb.append("=");
- sb.append(PowerCalculator.formatCharge(componentPower));
+ sb.append(BatteryStats.formatCharge(componentPower));
if (durationMs != 0) {
sb.append(" (");
@@ -194,7 +192,7 @@
separator = " ";
sb.append(getCustomPowerComponentName(customComponentId));
sb.append("=");
- sb.append(PowerCalculator.formatCharge(customComponentPower));
+ sb.append(BatteryStats.formatCharge(customComponentPower));
}
pw.print(sb);
diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java
index 1a082d1..a1ff923 100644
--- a/core/java/android/os/UidBatteryConsumer.java
+++ b/core/java/android/os/UidBatteryConsumer.java
@@ -136,7 +136,7 @@
}
sb.append(" ").append(processStateToString(processState)).append(": ")
- .append(PowerCalculator.formatCharge(power));
+ .append(BatteryStats.formatCharge(power));
}
static UidBatteryConsumer create(BatteryConsumerData data) {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 373179c..c597a1a 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2509,12 +2509,12 @@
}
/**
- * Checks if the calling context user is running in a profile.
+ * Checks if the calling context user is running in a profile. A profile is a user that
+ * typically has its own separate data but shares its UI with some parent user. For example, a
+ * {@link #isManagedProfile() managed profile} is a type of profile.
*
* @return whether the caller is in a profile.
- * @hide
*/
- @SystemApi
@UserHandleAware(
requiresAnyOfPermissionsIfNotCallerProfileGroup = {
android.Manifest.permission.MANAGE_USERS,
diff --git a/core/java/android/os/logcat/ILogcatManagerService.aidl b/core/java/android/os/logcat/ILogcatManagerService.aidl
index 02db274..68b5679 100644
--- a/core/java/android/os/logcat/ILogcatManagerService.aidl
+++ b/core/java/android/os/logcat/ILogcatManagerService.aidl
@@ -22,7 +22,5 @@
interface ILogcatManagerService {
void startThread(in int uid, in int gid, in int pid, in int fd);
void finishThread(in int uid, in int gid, in int pid, in int fd);
- void approve(in int uid, in int gid, in int pid, in int fd);
- void decline(in int uid, in int gid, in int pid, in int fd);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index bf94ab5..464567b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4501,6 +4501,13 @@
public static final String SCREEN_OFF_TIMEOUT = "screen_off_timeout";
/**
+ * The amount of time in milliseconds before the device goes to sleep or begins to dream
+ * after a period of inactivity while it is docked.
+ * @hide
+ */
+ public static final String SCREEN_OFF_TIMEOUT_DOCKED = "screen_off_timeout_docked";
+
+ /**
* The screen backlight brightness between 0 and 255.
*/
@Readable
@@ -10314,6 +10321,34 @@
"nearby_fast_pair_settings_devices_component";
/**
+ * Current provider of the component for requesting ambient context consent.
+ * Default value in @string/config_defaultAmbientContextConsentComponent.
+ * No VALIDATOR as this setting will not be backed up.
+ * @hide
+ */
+ public static final String AMBIENT_CONTEXT_CONSENT_COMPONENT =
+ "ambient_context_consent_component";
+
+ /**
+ * Current provider of the intent extra key for the caller's package name while
+ * requesting ambient context consent.
+ * No VALIDATOR as this setting will not be backed up.
+ * @hide
+ */
+ public static final String AMBIENT_CONTEXT_PACKAGE_NAME_EXTRA_KEY =
+ "ambient_context_package_name_key";
+
+ /**
+ * Current provider of the intent extra key for the event code int array while
+ * requesting ambient context consent.
+ * Default value in @string/config_ambientContextEventArrayExtraKey.
+ * No VALIDATOR as this setting will not be backed up.
+ * @hide
+ */
+ public static final String AMBIENT_CONTEXT_EVENT_ARRAY_EXTRA_KEY =
+ "ambient_context_event_array_key";
+
+ /**
* Controls whether aware is enabled.
* @hide
*/
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 3ff0161..5ff9263 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -2216,651 +2216,6 @@
}
/**
- * Columns for the "rcs_*" tables used by {@link android.telephony.ims.RcsMessageStore} classes.
- *
- * @hide - not meant for public use
- */
- public interface RcsColumns {
- // TODO(sahinc): Turn this to true once the schema finalizes, so that people can update
- // their messaging databases. NOTE: move the switch/case update in MmsSmsDatabaseHelper to
- // the latest version of the database before turning this flag to true.
- boolean IS_RCS_TABLE_SCHEMA_CODE_COMPLETE = false;
-
- /**
- * The authority for the content provider
- */
- String AUTHORITY = "rcs";
-
- /**
- * The URI to start building upon to use {@link com.android.providers.telephony.RcsProvider}
- */
- Uri CONTENT_AND_AUTHORITY = Uri.parse("content://" + AUTHORITY);
-
- /**
- * The value to be used whenever a transaction that expects an integer to be returned
- * failed.
- */
- int TRANSACTION_FAILED = Integer.MIN_VALUE;
-
- /**
- * The value that denotes a timestamp was not set before (e.g. a message that is not
- * delivered yet will not have a DELIVERED_TIMESTAMP)
- */
- long TIMESTAMP_NOT_SET = 0;
-
- /**
- * The table that {@link android.telephony.ims.RcsThread} gets persisted to
- */
- interface RcsThreadColumns {
- /**
- * The path that should be used for referring to
- * {@link android.telephony.ims.RcsThread}s in
- * {@link com.android.providers.telephony.RcsProvider} URIs.
- */
- String RCS_THREAD_URI_PART = "thread";
-
- /**
- * The URI to query or modify {@link android.telephony.ims.RcsThread} via the content
- * provider.
- */
- Uri RCS_THREAD_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, RCS_THREAD_URI_PART);
-
- /**
- * The unique identifier of an {@link android.telephony.ims.RcsThread}
- */
- String RCS_THREAD_ID_COLUMN = "rcs_thread_id";
- }
-
- /**
- * The table that {@link android.telephony.ims.Rcs1To1Thread} gets persisted to
- */
- interface Rcs1To1ThreadColumns extends RcsThreadColumns {
- /**
- * The path that should be used for referring to
- * {@link android.telephony.ims.Rcs1To1Thread}s in
- * {@link com.android.providers.telephony.RcsProvider} URIs.
- */
- String RCS_1_TO_1_THREAD_URI_PART = "p2p_thread";
-
- /**
- * The URI to query or modify {@link android.telephony.ims.Rcs1To1Thread}s via the
- * content provider. Can also insert to this URI to create a new 1-to-1 thread. When
- * performing an insert, ensure that the provided content values contain the other
- * participant's ID under the key
- * {@link RcsParticipantColumns.RCS_PARTICIPANT_ID_COLUMN}
- */
- Uri RCS_1_TO_1_THREAD_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
- RCS_1_TO_1_THREAD_URI_PART);
-
- /**
- * The SMS/MMS thread to fallback to in case of an RCS outage
- */
- String FALLBACK_THREAD_ID_COLUMN = "rcs_fallback_thread_id";
- }
-
- /**
- * The table that {@link android.telephony.ims.RcsGroupThread} gets persisted to
- */
- interface RcsGroupThreadColumns extends RcsThreadColumns {
- /**
- * The path that should be used for referring to
- * {@link android.telephony.ims.RcsGroupThread}s in
- * {@link com.android.providers.telephony.RcsProvider} URIs.
- */
- String RCS_GROUP_THREAD_URI_PART = "group_thread";
-
- /**
- * The URI to query or modify {@link android.telephony.ims.RcsGroupThread}s via the
- * content provider
- */
- Uri RCS_GROUP_THREAD_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
- RCS_GROUP_THREAD_URI_PART);
-
- /**
- * The owner/admin of the {@link android.telephony.ims.RcsGroupThread}
- */
- String OWNER_PARTICIPANT_COLUMN = "owner_participant";
-
- /**
- * The user visible name of the group
- */
- String GROUP_NAME_COLUMN = "group_name";
-
- /**
- * The user visible icon of the group
- */
- String GROUP_ICON_COLUMN = "group_icon";
-
- /**
- * The RCS conference URI for this group
- */
- String CONFERENCE_URI_COLUMN = "conference_uri";
- }
-
- /**
- * The view that enables polling from all types of RCS threads at once
- */
- interface RcsUnifiedThreadColumns extends RcsThreadColumns, Rcs1To1ThreadColumns,
- RcsGroupThreadColumns {
- /**
- * The type of this {@link android.telephony.ims.RcsThread}
- */
- String THREAD_TYPE_COLUMN = "thread_type";
-
- /**
- * Integer returned as a result from a database query that denotes the thread is 1 to 1
- */
- int THREAD_TYPE_1_TO_1 = 0;
-
- /**
- * Integer returned as a result from a database query that denotes the thread is 1 to 1
- */
- int THREAD_TYPE_GROUP = 1;
- }
-
- /**
- * The table that {@link android.telephony.ims.RcsParticipant} gets persisted to
- */
- interface RcsParticipantColumns {
- /**
- * The path that should be used for referring to
- * {@link android.telephony.ims.RcsParticipant}s in
- * {@link com.android.providers.telephony.RcsProvider} URIs.
- */
- String RCS_PARTICIPANT_URI_PART = "participant";
-
- /**
- * The URI to query or modify {@link android.telephony.ims.RcsParticipant}s via the
- * content provider
- */
- Uri RCS_PARTICIPANT_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
- RCS_PARTICIPANT_URI_PART);
-
- /**
- * The unique identifier of the entry in the database
- */
- String RCS_PARTICIPANT_ID_COLUMN = "rcs_participant_id";
-
- /**
- * A foreign key on canonical_address table, also used by SMS/MMS
- */
- String CANONICAL_ADDRESS_ID_COLUMN = "canonical_address_id";
-
- /**
- * The user visible RCS alias for this participant.
- */
- String RCS_ALIAS_COLUMN = "rcs_alias";
- }
-
- /**
- * Additional constants to enable access to {@link android.telephony.ims.RcsParticipant}
- * related data
- */
- interface RcsParticipantHelpers extends RcsParticipantColumns {
- /**
- * The view that unifies "rcs_participant" and "canonical_addresses" tables for easy
- * access to participant address.
- */
- String RCS_PARTICIPANT_WITH_ADDRESS_VIEW = "rcs_participant_with_address_view";
-
- /**
- * The view that unifies "rcs_participant", "canonical_addresses" and
- * "rcs_thread_participant" junction table to get full information on participants that
- * contribute to threads.
- */
- String RCS_PARTICIPANT_WITH_THREAD_VIEW = "rcs_participant_with_thread_view";
- }
-
- /**
- * The table that {@link android.telephony.ims.RcsMessage} gets persisted to
- */
- interface RcsMessageColumns {
- /**
- * Denotes the type of this message (i.e.
- * {@link android.telephony.ims.RcsIncomingMessage} or
- * {@link android.telephony.ims.RcsOutgoingMessage}
- */
- String MESSAGE_TYPE_COLUMN = "rcs_message_type";
-
- /**
- * The unique identifier for the message in the database - i.e. the primary key.
- */
- String MESSAGE_ID_COLUMN = "rcs_message_row_id";
-
- /**
- * The globally unique RCS identifier for the message. Please see 4.4.5.2 - GSMA
- * RCC.53 (RCS Device API 1.6 Specification)
- */
- String GLOBAL_ID_COLUMN = "rcs_message_global_id";
-
- /**
- * The subscription where this message was sent from/to.
- */
- String SUB_ID_COLUMN = "sub_id";
-
- /**
- * The sending status of the message.
- * @see android.telephony.ims.RcsMessage.RcsMessageStatus
- */
- String STATUS_COLUMN = "status";
-
- /**
- * The creation timestamp of the message.
- */
- String ORIGINATION_TIMESTAMP_COLUMN = "origination_timestamp";
-
- /**
- * The text content of the message.
- */
- String MESSAGE_TEXT_COLUMN = "rcs_text";
-
- /**
- * The latitude content of the message, if it contains a location.
- */
- String LATITUDE_COLUMN = "latitude";
-
- /**
- * The longitude content of the message, if it contains a location.
- */
- String LONGITUDE_COLUMN = "longitude";
- }
-
- /**
- * The table that additional information of {@link android.telephony.ims.RcsIncomingMessage}
- * gets persisted to.
- */
- interface RcsIncomingMessageColumns extends RcsMessageColumns {
- /**
- The path that should be used for referring to
- * {@link android.telephony.ims.RcsIncomingMessage}s in
- * {@link com.android.providers.telephony.RcsProvider} URIs.
- */
- String INCOMING_MESSAGE_URI_PART = "incoming_message";
-
- /**
- * The URI to query incoming messages through
- * {@link com.android.providers.telephony.RcsProvider}
- */
- Uri INCOMING_MESSAGE_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
- INCOMING_MESSAGE_URI_PART);
-
- /**
- * The ID of the {@link android.telephony.ims.RcsParticipant} that sent this message
- */
- String SENDER_PARTICIPANT_ID_COLUMN = "sender_participant";
-
- /**
- * The timestamp of arrival for this message.
- */
- String ARRIVAL_TIMESTAMP_COLUMN = "arrival_timestamp";
-
- /**
- * The time when the recipient has read this message.
- */
- String SEEN_TIMESTAMP_COLUMN = "seen_timestamp";
- }
-
- /**
- * The table that additional information of {@link android.telephony.ims.RcsOutgoingMessage}
- * gets persisted to.
- */
- interface RcsOutgoingMessageColumns extends RcsMessageColumns {
- /**
- * The path that should be used for referring to
- * {@link android.telephony.ims.RcsOutgoingMessage}s in
- * {@link com.android.providers.telephony.RcsProvider} URIs.
- */
- String OUTGOING_MESSAGE_URI_PART = "outgoing_message";
-
- /**
- * The URI to query or modify {@link android.telephony.ims.RcsOutgoingMessage}s via the
- * content provider
- */
- Uri OUTGOING_MESSAGE_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
- OUTGOING_MESSAGE_URI_PART);
- }
-
- /**
- * The delivery information of an {@link android.telephony.ims.RcsOutgoingMessage}
- */
- interface RcsMessageDeliveryColumns extends RcsOutgoingMessageColumns {
- /**
- * The path that should be used for referring to
- * {@link android.telephony.ims.RcsOutgoingMessageDelivery}s in
- * {@link com.android.providers.telephony.RcsProvider} URIs.
- */
- String DELIVERY_URI_PART = "delivery";
-
- /**
- * The timestamp of delivery of this message.
- */
- String DELIVERED_TIMESTAMP_COLUMN = "delivered_timestamp";
-
- /**
- * The time when the recipient has read this message.
- */
- String SEEN_TIMESTAMP_COLUMN = "seen_timestamp";
- }
-
- /**
- * The views that allow querying {@link android.telephony.ims.RcsIncomingMessage} and
- * {@link android.telephony.ims.RcsOutgoingMessage} at the same time.
- */
- interface RcsUnifiedMessageColumns extends RcsIncomingMessageColumns,
- RcsOutgoingMessageColumns {
- /**
- * The path that is used to query all {@link android.telephony.ims.RcsMessage} in
- * {@link com.android.providers.telephony.RcsProvider} URIs.
- */
- String UNIFIED_MESSAGE_URI_PART = "message";
-
- /**
- * The URI to query all types of {@link android.telephony.ims.RcsMessage}s
- */
- Uri UNIFIED_MESSAGE_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
- UNIFIED_MESSAGE_URI_PART);
-
- /**
- * The name of the view that unites rcs_message and rcs_incoming_message tables.
- */
- String UNIFIED_INCOMING_MESSAGE_VIEW = "unified_incoming_message_view";
-
- /**
- * The name of the view that unites rcs_message and rcs_outgoing_message tables.
- */
- String UNIFIED_OUTGOING_MESSAGE_VIEW = "unified_outgoing_message_view";
-
- /**
- * The column that shows from which table the message entry came from.
- */
- String MESSAGE_TYPE_COLUMN = "message_type";
-
- /**
- * Integer returned as a result from a database query that denotes that the message is
- * an incoming message
- */
- int MESSAGE_TYPE_INCOMING = 1;
-
- /**
- * Integer returned as a result from a database query that denotes that the message is
- * an outgoing message
- */
- int MESSAGE_TYPE_OUTGOING = 0;
- }
-
- /**
- * The table that {@link android.telephony.ims.RcsFileTransferPart} gets persisted to.
- */
- interface RcsFileTransferColumns {
- /**
- * The path that should be used for referring to
- * {@link android.telephony.ims.RcsFileTransferPart}s in
- * {@link com.android.providers.telephony.RcsProvider} URIs.
- */
- String FILE_TRANSFER_URI_PART = "file_transfer";
-
- /**
- * The URI to query or modify {@link android.telephony.ims.RcsFileTransferPart}s via the
- * content provider
- */
- Uri FILE_TRANSFER_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
- FILE_TRANSFER_URI_PART);
-
- /**
- * The globally unique file transfer ID for this RCS file transfer.
- */
- String FILE_TRANSFER_ID_COLUMN = "rcs_file_transfer_id";
-
- /**
- * The RCS session ID for this file transfer. The ID is implementation dependent but
- * should be unique.
- */
- String SESSION_ID_COLUMN = "session_id";
-
- /**
- * The URI that points to the content of this file transfer
- */
- String CONTENT_URI_COLUMN = "content_uri";
-
- /**
- * The file type of this file transfer in bytes. The validity of types is not enforced
- * in {@link android.telephony.ims.RcsMessageStore} APIs.
- */
- String CONTENT_TYPE_COLUMN = "content_type";
-
- /**
- * The size of the file transfer in bytes.
- */
- String FILE_SIZE_COLUMN = "file_size";
-
- /**
- * Number of bytes that was successfully transmitted for this file transfer
- */
- String SUCCESSFULLY_TRANSFERRED_BYTES = "transfer_offset";
-
- /**
- * The status of this file transfer
- * @see android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus
- */
- String TRANSFER_STATUS_COLUMN = "transfer_status";
-
- /**
- * The on-screen width of the file transfer, if it contains multi-media
- */
- String WIDTH_COLUMN = "width";
-
- /**
- * The on-screen height of the file transfer, if it contains multi-media
- */
- String HEIGHT_COLUMN = "height";
-
- /**
- * The duration of the content in milliseconds if this file transfer contains
- * multi-media
- */
- String DURATION_MILLIS_COLUMN = "duration";
-
- /**
- * The URI to the preview of the content of this file transfer
- */
- String PREVIEW_URI_COLUMN = "preview_uri";
-
- /**
- * The type of the preview of the content of this file transfer. The validity of types
- * is not enforced in {@link android.telephony.ims.RcsMessageStore} APIs.
- */
- String PREVIEW_TYPE_COLUMN = "preview_type";
- }
-
- /**
- * The table that holds the information for
- * {@link android.telephony.ims.RcsGroupThreadEvent} and its subclasses.
- */
- interface RcsThreadEventColumns {
- /**
- * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to
- * refer to participant joined events (example URI:
- * {@code content://rcs/group_thread/3/participant_joined_event})
- */
- String PARTICIPANT_JOINED_URI_PART = "participant_joined_event";
-
- /**
- * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to
- * refer to participant left events. (example URI:
- * {@code content://rcs/group_thread/3/participant_left_event/4})
- */
- String PARTICIPANT_LEFT_URI_PART = "participant_left_event";
-
- /**
- * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to
- * refer to name changed events. (example URI:
- * {@code content://rcs/group_thread/3/name_changed_event})
- */
- String NAME_CHANGED_URI_PART = "name_changed_event";
-
- /**
- * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to
- * refer to icon changed events. (example URI:
- * {@code content://rcs/group_thread/3/icon_changed_event})
- */
- String ICON_CHANGED_URI_PART = "icon_changed_event";
-
- /**
- * The unique ID of this event in the database, i.e. the primary key
- */
- String EVENT_ID_COLUMN = "event_id";
-
- /**
- * The type of this event
- *
- * @see RcsEventTypes
- */
- String EVENT_TYPE_COLUMN = "event_type";
-
- /**
- * The timestamp in milliseconds of when this event happened
- */
- String TIMESTAMP_COLUMN = "origination_timestamp";
-
- /**
- * The participant that generated this event
- */
- String SOURCE_PARTICIPANT_ID_COLUMN = "source_participant";
-
- /**
- * The receiving participant of this event if this was an
- * {@link android.telephony.ims.RcsGroupThreadParticipantJoinedEvent} or
- * {@link android.telephony.ims.RcsGroupThreadParticipantLeftEvent}
- */
- String DESTINATION_PARTICIPANT_ID_COLUMN = "destination_participant";
-
- /**
- * The URI for the new icon of the group thread if this was an
- * {@link android.telephony.ims.RcsGroupThreadIconChangedEvent}
- */
- String NEW_ICON_URI_COLUMN = "new_icon_uri";
-
- /**
- * The URI for the new name of the group thread if this was an
- * {@link android.telephony.ims.RcsGroupThreadNameChangedEvent}
- */
- String NEW_NAME_COLUMN = "new_name";
- }
-
- /**
- * The table that {@link android.telephony.ims.RcsParticipantAliasChangedEvent} gets
- * persisted to
- */
- interface RcsParticipantEventColumns {
- /**
- * The path that should be used for referring to
- * {@link android.telephony.ims.RcsParticipantAliasChangedEvent}s in
- * {@link com.android.providers.telephony.RcsProvider} URIs.
- */
- String ALIAS_CHANGE_EVENT_URI_PART = "alias_change_event";
-
- /**
- * The new alias of the participant
- */
- String NEW_ALIAS_COLUMN = "new_alias";
- }
-
- /**
- * These values are used in {@link com.android.providers.telephony.RcsProvider} to determine
- * what kind of event is present in the storage.
- */
- interface RcsEventTypes {
- /**
- * Integer constant that is stored in the
- * {@link com.android.providers.telephony.RcsProvider} database that denotes the event
- * is of type {@link android.telephony.ims.RcsParticipantAliasChangedEvent}
- */
- int PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE = 1;
-
- /**
- * Integer constant that is stored in the
- * {@link com.android.providers.telephony.RcsProvider} database that denotes the event
- * is of type {@link android.telephony.ims.RcsGroupThreadParticipantJoinedEvent}
- */
- int PARTICIPANT_JOINED_EVENT_TYPE = 2;
-
- /**
- * Integer constant that is stored in the
- * {@link com.android.providers.telephony.RcsProvider} database that denotes the event
- * is of type {@link android.telephony.ims.RcsGroupThreadParticipantLeftEvent}
- */
- int PARTICIPANT_LEFT_EVENT_TYPE = 4;
-
- /**
- * Integer constant that is stored in the
- * {@link com.android.providers.telephony.RcsProvider} database that denotes the event
- * is of type {@link android.telephony.ims.RcsGroupThreadIconChangedEvent}
- */
- int ICON_CHANGED_EVENT_TYPE = 8;
-
- /**
- * Integer constant that is stored in the
- * {@link com.android.providers.telephony.RcsProvider} database that denotes the event
- * is of type {@link android.telephony.ims.RcsGroupThreadNameChangedEvent}
- */
- int NAME_CHANGED_EVENT_TYPE = 16;
- }
-
- /**
- * The view that allows unified querying across all events
- */
- interface RcsUnifiedEventHelper extends RcsParticipantEventColumns, RcsThreadEventColumns {
- /**
- * The path that should be used for referring to
- * {@link android.telephony.ims.RcsEvent}s in
- * {@link com.android.providers.telephony.RcsProvider} URIs.
- */
- String RCS_EVENT_QUERY_URI_PATH = "event";
-
- /**
- * The URI to query {@link android.telephony.ims.RcsEvent}s via the content provider.
- */
- Uri RCS_EVENT_QUERY_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
- RCS_EVENT_QUERY_URI_PATH);
- }
-
- /**
- * Allows RCS specific canonical address handling.
- */
- interface RcsCanonicalAddressHelper {
- /**
- * Returns the canonical address ID for a canonical address, if now row exists, this
- * will add a row and return its ID. This helper works against the same table used by
- * the SMS and MMS threads, but is accessible only by the phone process for use by RCS
- * message storage.
- *
- * @throws IllegalArgumentException if unable to retrieve or create the canonical
- * address entry.
- */
- static long getOrCreateCanonicalAddressId(
- ContentResolver contentResolver, String canonicalAddress) {
-
- Uri.Builder uriBuilder = CONTENT_AND_AUTHORITY.buildUpon();
- uriBuilder.appendPath("canonical-address");
- uriBuilder.appendQueryParameter("address", canonicalAddress);
- Uri uri = uriBuilder.build();
-
- try (Cursor cursor = contentResolver.query(uri, null, null, null)) {
- if (cursor != null && cursor.moveToFirst()) {
- return cursor.getLong(cursor.getColumnIndex(CanonicalAddressesColumns._ID));
- } else {
- Rlog.e(TAG, "getOrCreateCanonicalAddressId returned no rows");
- }
- }
-
- Rlog.e(TAG, "getOrCreateCanonicalAddressId failed");
- throw new IllegalArgumentException(
- "Unable to find or allocate a canonical address ID");
- }
- }
- }
-
- /**
* Contains all MMS messages.
*/
public static final class Mms implements BaseMmsColumns {
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventResponse.aidl b/core/java/android/service/ambientcontext/AmbientContextDetectionResult.aidl
similarity index 80%
rename from core/java/android/app/ambientcontext/AmbientContextEventResponse.aidl
rename to core/java/android/service/ambientcontext/AmbientContextDetectionResult.aidl
index 4dc6466..4bb29b2 100644
--- a/core/java/android/app/ambientcontext/AmbientContextEventResponse.aidl
+++ b/core/java/android/service/ambientcontext/AmbientContextDetectionResult.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.app.ambientcontext;
+package android.service.ambientcontext;
-parcelable AmbientContextEventResponse;
\ No newline at end of file
+parcelable AmbientContextDetectionResult;
\ No newline at end of file
diff --git a/core/java/android/service/ambientcontext/AmbientContextDetectionResult.java b/core/java/android/service/ambientcontext/AmbientContextDetectionResult.java
new file mode 100644
index 0000000..227194e
--- /dev/null
+++ b/core/java/android/service/ambientcontext/AmbientContextDetectionResult.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2022 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 android.service.ambientcontext;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.app.ambientcontext.AmbientContextEvent;
+import android.os.Parcelable;
+
+import com.android.internal.util.AnnotationValidations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a {@code AmbientContextEvent} detection result reported by the detection service.
+ *
+ * @hide
+ */
+@SystemApi
+public final class AmbientContextDetectionResult implements Parcelable {
+
+ /**
+ * The bundle key for this class of object, used in {@code RemoteCallback#sendResult}.
+ *
+ * @hide
+ */
+ public static final String RESULT_RESPONSE_BUNDLE_KEY =
+ "android.app.ambientcontext.AmbientContextDetectionResultBundleKey";
+ @NonNull private final List<AmbientContextEvent> mEvents;
+ @NonNull private final String mPackageName;
+
+ AmbientContextDetectionResult(
+ @NonNull List<AmbientContextEvent> events,
+ @NonNull String packageName) {
+ this.mEvents = events;
+ AnnotationValidations.validate(NonNull.class, null, mEvents);
+ this.mPackageName = packageName;
+ AnnotationValidations.validate(NonNull.class, null, mPackageName);
+ }
+
+ /**
+ * A list of detected event.
+ */
+ @SuppressLint("ConcreteCollection")
+ public @NonNull List<AmbientContextEvent> getEvents() {
+ return mEvents;
+ }
+
+ /**
+ * The package to deliver the response to.
+ */
+ public @NonNull String getPackageName() {
+ return mPackageName;
+ }
+
+ @Override
+ public String toString() {
+ return "AmbientContextEventResponse { "
+ + "events = " + mEvents + ", " + "packageName = " + mPackageName + " }";
+ }
+
+ @Override
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ byte flg = 0;
+ dest.writeByte(flg);
+ dest.writeParcelableList(mEvents, flags);
+ dest.writeString(mPackageName);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ AmbientContextDetectionResult(@NonNull android.os.Parcel in) {
+ byte flg = in.readByte();
+ ArrayList<AmbientContextEvent> events = new ArrayList<>();
+ in.readParcelableList(events, AmbientContextEvent.class.getClassLoader(),
+ AmbientContextEvent.class);
+ String packageName = in.readString();
+
+ this.mEvents = events;
+ AnnotationValidations.validate(
+ NonNull.class, null, mEvents);
+ this.mPackageName = packageName;
+ AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ }
+
+ public static final @NonNull Creator<AmbientContextDetectionResult> CREATOR =
+ new Creator<AmbientContextDetectionResult>() {
+ @Override
+ public AmbientContextDetectionResult[] newArray(int size) {
+ return new AmbientContextDetectionResult[size];
+ }
+
+ @Override
+ public AmbientContextDetectionResult createFromParcel(@NonNull android.os.Parcel in) {
+ return new AmbientContextDetectionResult(in);
+ }
+ };
+
+ /**
+ * A builder for {@link AmbientContextDetectionResult}
+ */
+ @SuppressWarnings("WeakerAccess")
+ public static final class Builder {
+ private @NonNull ArrayList<AmbientContextEvent> mEvents;
+ private @NonNull String mPackageName;
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder() {
+ }
+
+ /**
+ * Adds an event to the builder.
+ */
+ public @NonNull Builder addEvent(@NonNull AmbientContextEvent value) {
+ checkNotUsed();
+ if (mEvents == null) {
+ mBuilderFieldsSet |= 0x1;
+ mEvents = new ArrayList<>();
+ }
+ mEvents.add(value);
+ return this;
+ }
+
+ /**
+ * The package to deliver the response to.
+ */
+ public @NonNull Builder setPackageName(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mPackageName = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull AmbientContextDetectionResult build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x1) == 0) {
+ mEvents = new ArrayList<>();
+ }
+ if ((mBuilderFieldsSet & 0x2) == 0) {
+ mPackageName = "";
+ }
+ AmbientContextDetectionResult o = new AmbientContextDetectionResult(
+ mEvents,
+ mPackageName);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x4) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+}
diff --git a/core/java/android/service/ambientcontext/AmbientContextDetectionService.java b/core/java/android/service/ambientcontext/AmbientContextDetectionService.java
index dccfe36..6224aa1 100644
--- a/core/java/android/service/ambientcontext/AmbientContextDetectionService.java
+++ b/core/java/android/service/ambientcontext/AmbientContextDetectionService.java
@@ -16,13 +16,13 @@
package android.service.ambientcontext;
+import android.annotation.BinderThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.Service;
import android.app.ambientcontext.AmbientContextEvent;
import android.app.ambientcontext.AmbientContextEventRequest;
-import android.app.ambientcontext.AmbientContextEventResponse;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
@@ -64,15 +64,6 @@
public static final String SERVICE_INTERFACE =
"android.service.ambientcontext.AmbientContextDetectionService";
- /**
- * The key for the bundle the parameter of {@code RemoteCallback#sendResult}. Implementation
- * should set bundle result with this key.
- *
- * @hide
- */
- public static final String RESPONSE_BUNDLE_KEY =
- "android.service.ambientcontext.EventResponseKey";
-
@Nullable
@Override
public final IBinder onBind(@NonNull Intent intent) {
@@ -82,19 +73,30 @@
@Override
public void startDetection(
@NonNull AmbientContextEventRequest request, String packageName,
- RemoteCallback callback) {
+ RemoteCallback detectionResultCallback, RemoteCallback statusCallback) {
Objects.requireNonNull(request);
- Objects.requireNonNull(callback);
- Consumer<AmbientContextEventResponse> consumer =
- response -> {
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(detectionResultCallback);
+ Objects.requireNonNull(statusCallback);
+ Consumer<AmbientContextDetectionResult> detectionResultConsumer =
+ result -> {
Bundle bundle = new Bundle();
bundle.putParcelable(
- AmbientContextDetectionService.RESPONSE_BUNDLE_KEY,
- response);
- callback.sendResult(bundle);
+ AmbientContextDetectionResult.RESULT_RESPONSE_BUNDLE_KEY,
+ result);
+ detectionResultCallback.sendResult(bundle);
+ };
+ Consumer<AmbientContextDetectionServiceStatus> statusConsumer =
+ status -> {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(
+ AmbientContextDetectionServiceStatus
+ .STATUS_RESPONSE_BUNDLE_KEY,
+ status);
+ statusCallback.sendResult(bundle);
};
AmbientContextDetectionService.this.onStartDetection(
- request, packageName, consumer);
+ request, packageName, detectionResultConsumer, statusConsumer);
Slog.d(TAG, "startDetection " + request);
}
@@ -104,29 +106,52 @@
Objects.requireNonNull(packageName);
AmbientContextDetectionService.this.onStopDetection(packageName);
}
+
+ /** {@inheritDoc} */
+ @Override
+ public void queryServiceStatus(
+ @AmbientContextEvent.EventCode int[] eventTypes,
+ String packageName,
+ RemoteCallback callback) {
+ Objects.requireNonNull(eventTypes);
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(callback);
+ Consumer<AmbientContextDetectionServiceStatus> consumer =
+ response -> {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(
+ AmbientContextDetectionServiceStatus
+ .STATUS_RESPONSE_BUNDLE_KEY,
+ response);
+ callback.sendResult(bundle);
+ };
+ AmbientContextDetectionService.this.onQueryServiceStatus(
+ eventTypes, packageName, consumer);
+ }
};
}
return null;
}
/**
- * Starts detection and provides detected events to the consumer. The ongoing detection will
- * keep running, until onStopDetection is called. If there were previously requested
+ * Starts detection and provides detected events to the statusConsumer. The ongoing detection
+ * will keep running, until onStopDetection is called. If there were previously requested
* detection from the same package, the previous request will be replaced with the new request.
* The implementation should keep track of whether the user consented each requested
- * AmbientContextEvent for the app. If not consented, the response should set status
- * STATUS_ACCESS_DENIED and include an action PendingIntent for the app to redirect the user
- * to the consent screen.
+ * AmbientContextEvent for the app. If not consented, the statusConsumer should get a response
+ * with STATUS_ACCESS_DENIED.
*
- * @param request The request with events to detect, optional detection window and other
- * options.
+ * @param request The request with events to detect.
* @param packageName the requesting app's package name
- * @param consumer the consumer for the detected event
+ * @param detectionResultConsumer the consumer for the detected event
+ * @param statusConsumer the consumer for the service status.
*/
+ @BinderThread
public abstract void onStartDetection(
@NonNull AmbientContextEventRequest request,
@NonNull String packageName,
- @NonNull Consumer<AmbientContextEventResponse> consumer);
+ @NonNull Consumer<AmbientContextDetectionResult> detectionResultConsumer,
+ @NonNull Consumer<AmbientContextDetectionServiceStatus> statusConsumer);
/**
* Stops detection of the events. Events that are not being detected will be ignored.
@@ -134,4 +159,19 @@
* @param packageName stops detection for the given package.
*/
public abstract void onStopDetection(@NonNull String packageName);
+
+ /**
+ * Called when a query for the detection status occurs. The implementation should check
+ * the detection status of the requested events for the package, and provide results in a
+ * {@link AmbientContextDetectionServiceStatus} for the consumer.
+ *
+ * @param eventTypes The events to check for status.
+ * @param packageName the requesting app's package name
+ * @param consumer the consumer for the query results
+ */
+ @BinderThread
+ public abstract void onQueryServiceStatus(
+ @NonNull int[] eventTypes,
+ @NonNull String packageName,
+ @NonNull Consumer<AmbientContextDetectionServiceStatus> consumer);
}
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventResponse.aidl b/core/java/android/service/ambientcontext/AmbientContextDetectionServiceStatus.aidl
similarity index 79%
copy from core/java/android/app/ambientcontext/AmbientContextEventResponse.aidl
copy to core/java/android/service/ambientcontext/AmbientContextDetectionServiceStatus.aidl
index 4dc6466..979cf7b 100644
--- a/core/java/android/app/ambientcontext/AmbientContextEventResponse.aidl
+++ b/core/java/android/service/ambientcontext/AmbientContextDetectionServiceStatus.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.app.ambientcontext;
+package android.service.ambientcontext;
-parcelable AmbientContextEventResponse;
\ No newline at end of file
+parcelable AmbientContextDetectionServiceStatus;
\ No newline at end of file
diff --git a/core/java/android/service/ambientcontext/AmbientContextDetectionServiceStatus.java b/core/java/android/service/ambientcontext/AmbientContextDetectionServiceStatus.java
new file mode 100644
index 0000000..3e92f39
--- /dev/null
+++ b/core/java/android/service/ambientcontext/AmbientContextDetectionServiceStatus.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2022 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 android.service.ambientcontext;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.ambientcontext.AmbientContextManager;
+import android.app.ambientcontext.AmbientContextManager.StatusCode;
+import android.os.Parcelable;
+
+import com.android.internal.util.AnnotationValidations;
+
+/**
+ * Represents a status for the {@code AmbientContextDetectionService}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class AmbientContextDetectionServiceStatus implements Parcelable {
+ /**
+ * The bundle key for this class of object, used in {@code RemoteCallback#sendResult}.
+ *
+ * @hide
+ */
+ public static final String STATUS_RESPONSE_BUNDLE_KEY =
+ "android.app.ambientcontext.AmbientContextServiceStatusBundleKey";
+
+ @StatusCode private final int mStatusCode;
+ @NonNull private final String mPackageName;
+
+ AmbientContextDetectionServiceStatus(
+ @StatusCode int statusCode,
+ @NonNull String packageName) {
+ this.mStatusCode = statusCode;
+ AnnotationValidations.validate(StatusCode.class, null, mStatusCode);
+ this.mPackageName = packageName;
+ }
+
+ /**
+ * The status of the service.
+ */
+ public @StatusCode int getStatusCode() {
+ return mStatusCode;
+ }
+
+ /**
+ * The package to deliver the response to.
+ */
+ public @NonNull String getPackageName() {
+ return mPackageName;
+ }
+
+ @Override
+ public String toString() {
+ return "AmbientContextDetectionServiceStatus { " + "statusCode = " + mStatusCode + ", "
+ + "packageName = " + mPackageName + " }";
+ }
+
+ @Override
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ byte flg = 0;
+ dest.writeByte(flg);
+ dest.writeInt(mStatusCode);
+ dest.writeString(mPackageName);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ AmbientContextDetectionServiceStatus(@NonNull android.os.Parcel in) {
+ byte flg = in.readByte();
+ int statusCode = in.readInt();
+ String packageName = in.readString();
+
+ this.mStatusCode = statusCode;
+ AnnotationValidations.validate(
+ StatusCode.class, null, mStatusCode);
+ this.mPackageName = packageName;
+ AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ }
+
+ public static final @NonNull Creator<AmbientContextDetectionServiceStatus> CREATOR =
+ new Creator<AmbientContextDetectionServiceStatus>() {
+ @Override
+ public AmbientContextDetectionServiceStatus[] newArray(int size) {
+ return new AmbientContextDetectionServiceStatus[size];
+ }
+
+ @Override
+ public AmbientContextDetectionServiceStatus createFromParcel(
+ @NonNull android.os.Parcel in) {
+ return new AmbientContextDetectionServiceStatus(in);
+ }
+ };
+
+ /**
+ * A builder for {@link AmbientContextDetectionServiceStatus}
+ */
+ @SuppressWarnings("WeakerAccess")
+ public static final class Builder {
+ private @StatusCode int mStatusCode;
+ private @NonNull String mPackageName;
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder() {
+ }
+
+ /**
+ * Sets the status of the service.
+ */
+ public @NonNull Builder setStatusCode(@StatusCode int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mStatusCode = value;
+ return this;
+ }
+
+ /**
+ * The package to deliver the response to.
+ */
+ public @NonNull Builder setPackageName(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mPackageName = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull AmbientContextDetectionServiceStatus build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x1) == 0) {
+ mStatusCode = AmbientContextManager.STATUS_UNKNOWN;
+ }
+ if ((mBuilderFieldsSet & 0x2) == 0) {
+ mPackageName = "";
+ }
+ AmbientContextDetectionServiceStatus o = new AmbientContextDetectionServiceStatus(
+ mStatusCode,
+ mPackageName);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x4) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+}
diff --git a/core/java/android/service/ambientcontext/IAmbientContextDetectionService.aidl b/core/java/android/service/ambientcontext/IAmbientContextDetectionService.aidl
index 1c6e25e..50c89c0 100644
--- a/core/java/android/service/ambientcontext/IAmbientContextDetectionService.aidl
+++ b/core/java/android/service/ambientcontext/IAmbientContextDetectionService.aidl
@@ -26,6 +26,8 @@
*/
oneway interface IAmbientContextDetectionService {
void startDetection(in AmbientContextEventRequest request, in String packageName,
- in RemoteCallback callback);
+ in RemoteCallback detectionResultCallback, in RemoteCallback statusCallback);
void stopDetection(in String packageName);
+ void queryServiceStatus(in int[] eventTypes, in String packageName,
+ in RemoteCallback callback);
}
\ No newline at end of file
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index ae39d3d..b9e60a1 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -256,6 +256,8 @@
public static final int REASON_CHANNEL_REMOVED = 20;
/** Notification was canceled due to the app's storage being cleared */
public static final int REASON_CLEAR_DATA = 21;
+ /** Notification was canceled due to an assistant adjustment update. */
+ public static final int REASON_ASSISTANT_CANCEL = 22;
/**
* @hide
@@ -279,7 +281,10 @@
REASON_UNAUTOBUNDLED,
REASON_CHANNEL_BANNED,
REASON_SNOOZED,
- REASON_TIMEOUT
+ REASON_TIMEOUT,
+ REASON_CHANNEL_REMOVED,
+ REASON_CLEAR_DATA,
+ REASON_ASSISTANT_CANCEL,
})
public @interface NotificationCancelReason{};
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c5ccc18..36baa04 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -941,4 +941,14 @@
* @hide
*/
void unregisterTaskFpsCallback(in IOnFpsCallbackListener listener);
+
+ /**
+ * Take a snapshot using the same path that's used for Recents. This is used for Testing only.
+ *
+ * @param taskId to take the snapshot of
+ *
+ * Returns a bitmap of the screenshot or {@code null} if it was unable to screenshot.
+ * @hide
+ */
+ Bitmap snapshotTaskForRecents(int taskId);
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 7f115fa..d3061e2 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -2779,9 +2779,13 @@
* is allowed as a convenience.
*/
public Transaction() {
- mNativeObject = nativeCreateTransaction();
- mFreeNativeResources
- = sRegistry.registerNativeAllocation(this, mNativeObject);
+ this(nativeCreateTransaction());
+ }
+
+ private Transaction(long nativeObject) {
+ mNativeObject = nativeObject;
+ mFreeNativeResources =
+ sRegistry.registerNativeAllocation(this, mNativeObject);
}
private Transaction(Parcel in) {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 83a59e7..a2fcf80 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -527,20 +527,12 @@
mRequestedVisible = false;
updateSurface();
- tryReleaseSurfaces();
// We don't release this as part of releaseSurfaces as
// that is also called on transient visibility changes. We can't
// recreate this Surface, so only release it when we are fully
// detached.
- if (mSurfacePackage != null) {
- final SurfaceControl sc = mSurfacePackage.getSurfaceControl();
- if (sc != null && sc.isValid()) {
- mTmpTransaction.reparent(sc, null).apply();
- }
- mSurfacePackage.release();
- mSurfacePackage = null;
- }
+ tryReleaseSurfaces(true /* releaseSurfacePackage*/);
mHaveFrame = false;
super.onDetachedFromWindow();
@@ -871,7 +863,7 @@
return t;
}
- private void tryReleaseSurfaces() {
+ private void tryReleaseSurfaces(boolean releaseSurfacePackage) {
mSurfaceAlpha = 1f;
synchronized (mSurfaceControlLock) {
@@ -881,9 +873,26 @@
mBlastBufferQueue = null;
}
- ViewRootImpl viewRoot = getViewRootImpl();
Transaction transaction = new Transaction();
- releaseSurfaces(transaction);
+ if (mSurfaceControl != null) {
+ transaction.remove(mSurfaceControl);
+ mSurfaceControl = null;
+ }
+ if (mBackgroundControl != null) {
+ transaction.remove(mBackgroundControl);
+ mBackgroundControl = null;
+ }
+ if (mBlastSurfaceControl != null) {
+ transaction.remove(mBlastSurfaceControl);
+ mBlastSurfaceControl = null;
+ }
+
+ if (releaseSurfacePackage && mSurfacePackage != null) {
+ mSurfacePackage.release();
+ mSurfacePackage = null;
+ }
+
+ ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot != null) {
viewRoot.applyTransactionOnDraw(transaction);
} else {
@@ -892,22 +901,6 @@
}
}
- private void releaseSurfaces(Transaction transaction) {
- if (mSurfaceControl != null) {
- transaction.remove(mSurfaceControl);
- mSurfaceControl = null;
- }
- if (mBackgroundControl != null) {
- transaction.remove(mBackgroundControl);
- mBackgroundControl = null;
- }
- if (mBlastSurfaceControl != null) {
- transaction.remove(mBlastSurfaceControl);
- mBlastSurfaceControl = null;
- }
- }
-
-
// The position update listener is used to safely share the surface size between render thread
// workers and the UI thread. Both threads need to know the surface size to determine the scale.
// The parent layer scales the surface size to view size. The child (BBQ) layer scales
@@ -1048,7 +1041,7 @@
if (viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
notifySurfaceDestroyed();
- tryReleaseSurfaces();
+ tryReleaseSurfaces(false /* releaseSurfacePackage*/);
return;
}
@@ -1189,7 +1182,7 @@
} finally {
mIsCreating = false;
if (mSurfaceControl != null && !mSurfaceCreated) {
- tryReleaseSurfaces();
+ tryReleaseSurfaces(false /* releaseSurfacePackage*/);
}
}
} catch (Exception ex) {
@@ -1835,43 +1828,24 @@
* @param p The SurfacePackage to embed.
*/
public void setChildSurfacePackage(@NonNull SurfaceControlViewHost.SurfacePackage p) {
- setChildSurfacePackage(p, false /* applyTransactionOnDraw */);
- }
-
- /**
- * Similar to setChildSurfacePackage, but using the BLAST queue so the transaction can be
- * synchronized with the ViewRootImpl frame.
- * @hide
- */
- public void setChildSurfacePackageOnDraw(
- @NonNull SurfaceControlViewHost.SurfacePackage p) {
- setChildSurfacePackage(p, true /* applyTransactionOnDraw */);
- }
-
- /**
- * @param applyTransactionOnDraw Whether to apply transaction at onDraw or immediately.
- */
- private void setChildSurfacePackage(
- @NonNull SurfaceControlViewHost.SurfacePackage p, boolean applyTransactionOnDraw) {
final SurfaceControl lastSc = mSurfacePackage != null ?
mSurfacePackage.getSurfaceControl() : null;
+ final SurfaceControl.Transaction transaction = new Transaction();
if (mSurfaceControl != null) {
if (lastSc != null) {
- mTmpTransaction.reparent(lastSc, null);
+ transaction.reparent(lastSc, null);
mSurfacePackage.release();
}
- reparentSurfacePackage(mTmpTransaction, p);
- applyTransaction(applyTransactionOnDraw);
+ reparentSurfacePackage(transaction, p);
+ final ViewRootImpl viewRoot = getViewRootImpl();
+ if (viewRoot != null) {
+ viewRoot.applyTransactionOnDraw(transaction);
+ } else {
+ transaction.apply();
+ }
}
mSurfacePackage = p;
- }
-
- private void applyTransaction(boolean applyTransactionOnDraw) {
- if (applyTransactionOnDraw) {
- getViewRootImpl().applyTransactionOnDraw(mTmpTransaction);
- } else {
- mTmpTransaction.apply();
- }
+ invalidate();
}
private void reparentSurfacePackage(SurfaceControl.Transaction t,
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 777e89d..39fc2c0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4169,19 +4169,22 @@
+ " didProduceBuffer=" + didProduceBuffer);
}
+ Transaction tmpTransaction = new Transaction();
+ tmpTransaction.merge(mRtBLASTSyncTransaction);
+
// If frame wasn't drawn, clear out the next transaction so it doesn't affect the next
// draw attempt. The next transaction and transaction complete callback were only set
// for the current draw attempt.
if (!didProduceBuffer) {
mBlastBufferQueue.setSyncTransaction(null);
- // Apply the transactions that were sent to mergeWithNextTransaction since the
+ // Get the transactions that were sent to mergeWithNextTransaction since the
// frame didn't draw on this vsync. It's possible the frame will draw later, but
// it's better to not be sync than to block on a frame that may never come.
- mBlastBufferQueue.applyPendingTransactions(mRtLastAttemptedDrawFrameNum);
+ Transaction pendingTransactions = mBlastBufferQueue.gatherPendingTransactions(
+ mRtLastAttemptedDrawFrameNum);
+ tmpTransaction.merge(pendingTransactions);
}
- Transaction tmpTransaction = new Transaction();
- tmpTransaction.merge(mRtBLASTSyncTransaction);
// Post at front of queue so the buffer can be processed immediately and allow RT
// to continue processing new buffers. If RT tries to process buffers before the sync
// buffer is applied, the new buffers will not get acquired and could result in a
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index ca7f900..771d40b 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -97,6 +97,7 @@
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
+import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Point;
@@ -4886,4 +4887,21 @@
*/
@SystemApi
default void unregisterTaskFpsCallback(@NonNull TaskFpsCallback callback) {}
+
+ /**
+ * Take a snapshot using the same path that's used for Recents. This is used for Testing only.
+ *
+ * @param taskId to take the snapshot of
+ *
+ * @return a bitmap of the screenshot or {@code null} if it was unable to screenshot. The
+ * screenshot can fail if the taskId is invalid or if there's no SurfaceControl associated with
+ * that task.
+ *
+ * @hide
+ */
+ @TestApi
+ @Nullable
+ default Bitmap snapshotTaskForRecents(@IntRange(from = 0) int taskId) {
+ return null;
+ }
}
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index c16703ef..f4353eb 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -32,6 +32,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
@@ -439,4 +440,14 @@
} catch (RemoteException e) {
}
}
+
+ @Override
+ public Bitmap snapshotTaskForRecents(int taskId) {
+ try {
+ return WindowManagerGlobal.getWindowManagerService().snapshotTaskForRecents(taskId);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ return null;
+ }
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index e54ed18..c6cd2e8 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -1773,8 +1773,7 @@
* @param userId The user Id.
* @hide
*/
- @RequiresPermission(Manifest.permission.SET_SYSTEM_AUDIO_CAPTION)
- public void setSystemAudioCaptioningRequested(boolean isEnabled, int userId) {
+ public void setSystemAudioCaptioningEnabled(boolean isEnabled, int userId) {
final IAccessibilityManager service;
synchronized (mLock) {
service = getServiceLocked();
@@ -1783,7 +1782,7 @@
}
}
try {
- service.setSystemAudioCaptioningRequested(isEnabled, userId);
+ service.setSystemAudioCaptioningEnabled(isEnabled, userId);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -1796,7 +1795,7 @@
* @return the system audio caption UI enabled state.
* @hide
*/
- public boolean isSystemAudioCaptioningUiRequested(int userId) {
+ public boolean isSystemAudioCaptioningUiEnabled(int userId) {
final IAccessibilityManager service;
synchronized (mLock) {
service = getServiceLocked();
@@ -1805,7 +1804,7 @@
}
}
try {
- return service.isSystemAudioCaptioningUiRequested(userId);
+ return service.isSystemAudioCaptioningUiEnabled(userId);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -1818,8 +1817,7 @@
* @param userId The user Id.
* @hide
*/
- @RequiresPermission(Manifest.permission.SET_SYSTEM_AUDIO_CAPTION)
- public void setSystemAudioCaptioningUiRequested(boolean isEnabled, int userId) {
+ public void setSystemAudioCaptioningUiEnabled(boolean isEnabled, int userId) {
final IAccessibilityManager service;
synchronized (mLock) {
service = getServiceLocked();
@@ -1828,7 +1826,7 @@
}
}
try {
- service.setSystemAudioCaptioningUiRequested(isEnabled, userId);
+ service.setSystemAudioCaptioningUiEnabled(isEnabled, userId);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/accessibility/CaptioningManager.java b/core/java/android/view/accessibility/CaptioningManager.java
index 4f9781b..e960bec 100644
--- a/core/java/android/view/accessibility/CaptioningManager.java
+++ b/core/java/android/view/accessibility/CaptioningManager.java
@@ -152,7 +152,7 @@
/**
* @return the system audio caption enabled state.
*/
- public final boolean isSystemAudioCaptioningRequested() {
+ public final boolean isSystemAudioCaptioningEnabled() {
return Secure.getIntForUser(mContentResolver, Secure.ODI_CAPTIONS_ENABLED,
SYSTEM_AUDIO_CAPTIONING_DEFAULT_ENABLED ? 1 : 0, mContext.getUserId()) == 1;
}
@@ -169,9 +169,9 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.SET_SYSTEM_AUDIO_CAPTION)
- public final void setSystemAudioCaptioningRequested(boolean isEnabled) {
+ public final void setSystemAudioCaptioningEnabled(boolean isEnabled) {
if (mAccessibilityManager != null) {
- mAccessibilityManager.setSystemAudioCaptioningRequested(isEnabled,
+ mAccessibilityManager.setSystemAudioCaptioningEnabled(isEnabled,
mContext.getUserId());
}
}
@@ -179,9 +179,9 @@
/**
* @return the system audio caption UI enabled state.
*/
- public final boolean isSystemAudioCaptioningUiRequested() {
+ public final boolean isSystemAudioCaptioningUiEnabled() {
return mAccessibilityManager != null
- && mAccessibilityManager.isSystemAudioCaptioningUiRequested(mContext.getUserId());
+ && mAccessibilityManager.isSystemAudioCaptioningUiEnabled(mContext.getUserId());
}
/**
@@ -196,9 +196,9 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.SET_SYSTEM_AUDIO_CAPTION)
- public final void setSystemAudioCaptioningUiRequested(boolean isEnabled) {
+ public final void setSystemAudioCaptioningUiEnabled(boolean isEnabled) {
if (mAccessibilityManager != null) {
- mAccessibilityManager.setSystemAudioCaptioningUiRequested(isEnabled,
+ mAccessibilityManager.setSystemAudioCaptioningUiEnabled(isEnabled,
mContext.getUserId());
}
}
@@ -300,7 +300,7 @@
}
private void notifySystemAudioCaptionChanged() {
- final boolean enabled = isSystemAudioCaptioningRequested();
+ final boolean enabled = isSystemAudioCaptioningEnabled();
synchronized (mListeners) {
for (CaptioningChangeListener listener : mListeners) {
listener.onSystemAudioCaptioningChanged(enabled);
@@ -309,7 +309,7 @@
}
private void notifySystemAudioCaptionUiChanged() {
- final boolean enabled = isSystemAudioCaptioningUiRequested();
+ final boolean enabled = isSystemAudioCaptioningUiEnabled();
synchronized (mListeners) {
for (CaptioningChangeListener listener : mListeners) {
listener.onSystemAudioCaptioningUiChanged(enabled);
@@ -686,7 +686,7 @@
* @param isEnabled The system audio captioning enabled state.
* @param userId The user Id.
*/
- void setSystemAudioCaptioningRequested(boolean isEnabled, int userId);
+ void setSystemAudioCaptioningEnabled(boolean isEnabled, int userId);
/**
* Gets the system audio caption UI enabled state.
@@ -694,7 +694,7 @@
* @param userId The user Id.
* @return the system audio caption UI enabled state.
*/
- boolean isSystemAudioCaptioningUiRequested(int userId);
+ boolean isSystemAudioCaptioningUiEnabled(int userId);
/**
* Sets the system audio caption UI enabled state.
@@ -702,6 +702,6 @@
* @param isEnabled The system audio captioning UI enabled state.
* @param userId The user Id.
*/
- void setSystemAudioCaptioningUiRequested(boolean isEnabled, int userId);
+ void setSystemAudioCaptioningUiEnabled(boolean isEnabled, int userId);
}
}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 645ddf5..418132a 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -101,13 +101,11 @@
boolean isAudioDescriptionByDefaultEnabled();
- // Requires Manifest.permission.SET_SYSTEM_AUDIO_CAPTION
- // System process only
- void setSystemAudioCaptioningRequested(boolean isEnabled, int userId);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.SET_SYSTEM_AUDIO_CAPTION)")
+ void setSystemAudioCaptioningEnabled(boolean isEnabled, int userId);
- boolean isSystemAudioCaptioningUiRequested(int userId);
+ boolean isSystemAudioCaptioningUiEnabled(int userId);
- // Requires Manifest.permission.SET_SYSTEM_AUDIO_CAPTION
- // System process only
- void setSystemAudioCaptioningUiRequested(boolean isEnabled, int userId);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.SET_SYSTEM_AUDIO_CAPTION)")
+ void setSystemAudioCaptioningUiEnabled(boolean isEnabled, int userId);
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index f480b24..c713a54 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -115,6 +115,7 @@
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
/**
* Central system API to the overall input method framework (IMF) architecture,
@@ -426,6 +427,8 @@
int mCursorSelEnd;
int mCursorCandStart;
int mCursorCandEnd;
+ int mInitialSelStart;
+ int mInitialSelEnd;
/**
* The instance that has previously been sent to the input method.
@@ -468,6 +471,13 @@
@Nullable
@GuardedBy("mH")
private InputMethodSessionWrapper mCurrentInputMethodSession = null;
+ /**
+ * Encapsulates IPCs to the currently connected AccessibilityServices.
+ */
+ @Nullable
+ @GuardedBy("mH")
+ private final SparseArray<InputMethodSessionWrapper> mAccessibilityInputMethodSession =
+ new SparseArray<>();
InputChannel mCurChannel;
ImeInputEventSender mCurSender;
@@ -499,6 +509,8 @@
static final int MSG_TIMEOUT_INPUT_EVENT = 6;
static final int MSG_FLUSH_INPUT_EVENT = 7;
static final int MSG_REPORT_FULLSCREEN_MODE = 10;
+ static final int MSG_BIND_ACCESSIBILITY_SERVICE = 11;
+ static final int MSG_UNBIND_ACCESSIBILITY_SERVICE = 12;
private static boolean isAutofillUIShowing(View servedView) {
AutofillManager afm = servedView.getContext().getSystemService(AutofillManager.class);
@@ -626,6 +638,7 @@
if (mCurrentInputMethodSession != null) {
mCurrentInputMethodSession.finishInput();
}
+ forAccessibilitySessions(InputMethodSessionWrapper::finishInput);
}
}
@@ -881,6 +894,7 @@
if (mBindSequence != sequence) {
return;
}
+ clearAllAccessibilityBindingLocked();
clearBindingLocked();
// If we were actively using the last input method, then
// we would like to re-connect to the next input method.
@@ -896,6 +910,73 @@
}
return;
}
+ case MSG_BIND_ACCESSIBILITY_SERVICE: {
+ final int id = msg.arg1;
+ final InputBindResult res = (InputBindResult) msg.obj;
+ if (DEBUG) {
+ Log.i(TAG, "handleMessage: MSG_BIND_ACCESSIBILITY " + res.sequence
+ + "," + res.id);
+ }
+ synchronized (mH) {
+ if (mBindSequence < 0 || mBindSequence != res.sequence) {
+ Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence
+ + ", given seq=" + res.sequence);
+ if (res.channel != null && res.channel != mCurChannel) {
+ res.channel.dispose();
+ }
+ return;
+ }
+
+ // Since IMM can start inputting text before a11y sessions are back,
+ // we send a notification so that the a11y service knows the session is
+ // registered and update the a11y service with the current cursor positions.
+ if (res.accessibilitySessions != null) {
+ InputMethodSessionWrapper wrapper =
+ InputMethodSessionWrapper.createOrNull(
+ res.accessibilitySessions.get(id));
+ if (wrapper != null) {
+ mAccessibilityInputMethodSession.put(id, wrapper);
+ if (mServedInputConnection != null) {
+ wrapper.updateSelection(mInitialSelStart, mInitialSelEnd,
+ mCursorSelStart, mCursorSelEnd, mCursorCandStart,
+ mCursorCandEnd);
+ } else {
+ // If an a11y service binds before input starts, we should still
+ // send a notification because the a11y service doesn't know it
+ // binds before or after input starts, it may wonder if it binds
+ // after input starts, why it doesn't receive a notification of
+ // the current cursor positions.
+ wrapper.updateSelection(-1, -1,
+ -1, -1, -1,
+ -1);
+ }
+ }
+ }
+ mBindSequence = res.sequence;
+ }
+ startInputInner(StartInputReason.BOUND_ACCESSIBILITY_SESSION_TO_IMMS, null,
+ 0, 0, 0);
+ return;
+ }
+ case MSG_UNBIND_ACCESSIBILITY_SERVICE: {
+ final int sequence = msg.arg1;
+ final int id = msg.arg2;
+ if (DEBUG) {
+ Log.i(TAG, "handleMessage: MSG_UNBIND_ACCESSIBILITY_SERVICE "
+ + sequence + " id=" + id);
+ }
+ synchronized (mH) {
+ if (mBindSequence != sequence) {
+ if (DEBUG) {
+ Log.i(TAG, "mBindSequence =" + mBindSequence + " sequence ="
+ + sequence + " id=" + id);
+ }
+ return;
+ }
+ clearAccessibilityBindingLocked(id);
+ }
+ return;
+ }
case MSG_SET_ACTIVE: {
final boolean active = msg.arg1 != 0;
final boolean fullscreen = msg.arg2 != 0;
@@ -996,11 +1077,21 @@
}
@Override
+ public void onBindAccessibilityService(InputBindResult res, int id) {
+ mH.obtainMessage(MSG_BIND_ACCESSIBILITY_SERVICE, id, 0, res).sendToTarget();
+ }
+
+ @Override
public void onUnbindMethod(int sequence, @UnbindReason int unbindReason) {
mH.obtainMessage(MSG_UNBIND, sequence, unbindReason).sendToTarget();
}
@Override
+ public void onUnbindAccessibilityService(int sequence, int id) {
+ mH.obtainMessage(MSG_UNBIND_ACCESSIBILITY_SERVICE, sequence, id).sendToTarget();
+ }
+
+ @Override
public void setActive(boolean active, boolean fullscreen, boolean reportToImeController) {
mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, fullscreen ? 1 : 0,
reportToImeController).sendToTarget();
@@ -1420,16 +1511,36 @@
/**
* Reset all of the state associated with being bound to an input method.
*/
+ @GuardedBy("mH")
void clearBindingLocked() {
if (DEBUG) Log.v(TAG, "Clearing binding!");
clearConnectionLocked();
setInputChannelLocked(null);
+ // We only reset sequence number for input method, but not accessibility.
mBindSequence = -1;
mCurId = null;
mCurMethod = null; // for @UnsupportedAppUsage
mCurrentInputMethodSession = null;
}
+ /**
+ * Reset all of the state associated with being bound to an accessibility service.
+ */
+ @GuardedBy("mH")
+ void clearAccessibilityBindingLocked(int id) {
+ if (DEBUG) Log.v(TAG, "Clearing accessibility binding " + id);
+ mAccessibilityInputMethodSession.remove(id);
+ }
+
+ /**
+ * Reset all of the state associated with being bound to all ccessibility services.
+ */
+ @GuardedBy("mH")
+ void clearAllAccessibilityBindingLocked() {
+ if (DEBUG) Log.v(TAG, "Clearing all accessibility bindings");
+ mAccessibilityInputMethodSession.clear();
+ }
+
void setInputChannelLocked(InputChannel channel) {
if (mCurChannel == channel) {
return;
@@ -1938,6 +2049,8 @@
editorInfo.setInitialSurroundingTextInternal(textSnapshot.getSurroundingText());
mCurrentInputMethodSession.invalidateInput(editorInfo, mServedInputConnection,
sessionId);
+ forAccessibilitySessions(wrapper -> wrapper.invalidateInput(editorInfo,
+ mServedInputConnection, sessionId));
}
}
@@ -2080,6 +2193,8 @@
if (ic != null) {
mCursorSelStart = tba.initialSelStart;
mCursorSelEnd = tba.initialSelEnd;
+ mInitialSelStart = mCursorSelStart;
+ mInitialSelEnd = mCursorSelEnd;
mCursorCandStart = -1;
mCursorCandEnd = -1;
mCursorRect.setEmpty();
@@ -2128,6 +2243,17 @@
mBindSequence = res.sequence;
mCurMethod = res.method; // for @UnsupportedAppUsage
mCurrentInputMethodSession = InputMethodSessionWrapper.createOrNull(res.method);
+ mAccessibilityInputMethodSession.clear();
+ if (res.accessibilitySessions != null) {
+ for (int i = 0; i < res.accessibilitySessions.size(); i++) {
+ InputMethodSessionWrapper wrapper = InputMethodSessionWrapper.createOrNull(
+ res.accessibilitySessions.valueAt(i));
+ if (wrapper != null) {
+ mAccessibilityInputMethodSession.append(
+ res.accessibilitySessions.keyAt(i), wrapper);
+ }
+ }
+ }
mCurId = res.id;
} else if (res.channel != null && res.channel != mCurChannel) {
res.channel.dispose();
@@ -2137,8 +2263,10 @@
mRestartOnNextWindowFocus = true;
break;
}
- if (mCurrentInputMethodSession != null && mCompletions != null) {
- mCurrentInputMethodSession.displayCompletions(mCompletions);
+ if (mCompletions != null) {
+ if (mCurrentInputMethodSession != null) {
+ mCurrentInputMethodSession.displayCompletions(mCompletions);
+ }
}
}
@@ -2369,6 +2497,8 @@
mCursorCandEnd = candidatesEnd;
mCurrentInputMethodSession.updateSelection(
oldSelStart, oldSelEnd, selStart, selEnd, candidatesStart, candidatesEnd);
+ forAccessibilitySessions(wrapper -> wrapper.updateSelection(oldSelStart,
+ oldSelEnd, selStart, selEnd, candidatesStart, candidatesEnd));
}
}
}
@@ -3233,6 +3363,11 @@
} else {
p.println(" mCurMethod= null");
}
+ for (int i = 0; i < mAccessibilityInputMethodSession.size(); i++) {
+ p.println(" mAccessibilityInputMethodSession("
+ + mAccessibilityInputMethodSession.keyAt(i) + ")="
+ + mAccessibilityInputMethodSession.valueAt(i));
+ }
p.println(" mCurRootView=" + mCurRootView);
p.println(" mServedView=" + getServedViewLocked());
p.println(" mNextServedView=" + getNextServedViewLocked());
@@ -3377,4 +3512,10 @@
}
}
}
+
+ private void forAccessibilitySessions(Consumer<InputMethodSessionWrapper> consumer) {
+ for (int i = 0; i < mAccessibilityInputMethodSession.size(); i++) {
+ consumer.accept(mAccessibilityInputMethodSession.valueAt(i));
+ }
+ }
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9efa583..c0c7641 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -12295,7 +12295,7 @@
EXTRA_DATA_RENDERING_INFO_KEY,
EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
));
- info.setTextSelectable(isTextSelectable());
+ info.setTextSelectable(isTextSelectable() || isTextEditable());
} else {
info.setAvailableExtraData(Arrays.asList(
EXTRA_DATA_RENDERING_INFO_KEY
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
index b90e628..34a3418 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -493,7 +493,7 @@
Log.d(TAG, "Transferring surface " + mSurfaceView.toString());
}
- mSurfaceView.setChildSurfacePackageOnDraw(mSurfacePackage);
+ mSurfaceView.setChildSurfacePackage(mSurfacePackage);
}
void initIconAnimation(Drawable iconDrawable, long duration) {
diff --git a/core/java/com/android/internal/inputmethod/InputBindResult.java b/core/java/com/android/internal/inputmethod/InputBindResult.java
index 1357bac..e838401 100644
--- a/core/java/com/android/internal/inputmethod/InputBindResult.java
+++ b/core/java/com/android/internal/inputmethod/InputBindResult.java
@@ -25,6 +25,7 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.SparseArray;
import android.view.InputChannel;
import com.android.internal.view.IInputMethodSession;
@@ -53,7 +54,8 @@
ResultCode.ERROR_NOT_IME_TARGET_WINDOW,
ResultCode.ERROR_NO_EDITOR,
ResultCode.ERROR_DISPLAY_ID_MISMATCH,
- ResultCode.ERROR_INVALID_DISPLAY_ID
+ ResultCode.ERROR_INVALID_DISPLAY_ID,
+ ResultCode.SUCCESS_WITH_ACCESSIBILITY_SESSION
})
public @interface ResultCode {
/**
@@ -168,6 +170,7 @@
* display.
*/
int ERROR_INVALID_DISPLAY_ID = 15;
+ int SUCCESS_WITH_ACCESSIBILITY_SESSION = 16;
}
@ResultCode
@@ -179,6 +182,11 @@
public final IInputMethodSession method;
/**
+ * The accessibility services.
+ */
+ public SparseArray<IInputMethodSession> accessibilitySessions;
+
+ /**
* The input channel used to send input events to this IME.
*/
public final InputChannel channel;
@@ -204,6 +212,8 @@
*
* @param result A result code defined in {@link ResultCode}.
* @param method {@link IInputMethodSession} to interact with the IME.
+ * @param accessibilitySessions {@link IInputMethodSession} to interact with accessibility
+ * services.
* @param channel {@link InputChannel} to forward input events to the IME.
* @param id The {@link String} representations of the IME, which is the same as
* {@link android.view.inputmethod.InputMethodInfo#getId()} and
@@ -213,10 +223,12 @@
* {@code suppressesSpellChecker="true"}.
*/
public InputBindResult(@ResultCode int result,
- IInputMethodSession method, InputChannel channel, String id, int sequence,
+ IInputMethodSession method, SparseArray<IInputMethodSession> accessibilitySessions,
+ InputChannel channel, String id, int sequence,
boolean isInputMethodSuppressingSpellChecker) {
this.result = result;
this.method = method;
+ this.accessibilitySessions = accessibilitySessions;
this.channel = channel;
this.id = id;
this.sequence = sequence;
@@ -226,6 +238,19 @@
private InputBindResult(Parcel source) {
result = source.readInt();
method = IInputMethodSession.Stub.asInterface(source.readStrongBinder());
+ int n = source.readInt();
+ if (n < 0) {
+ accessibilitySessions = null;
+ } else {
+ accessibilitySessions = new SparseArray<>(n);
+ while (n > 0) {
+ int key = source.readInt();
+ IInputMethodSession value =
+ IInputMethodSession.Stub.asInterface(source.readStrongBinder());
+ accessibilitySessions.append(key, value);
+ n--;
+ }
+ }
if (source.readInt() != 0) {
channel = InputChannel.CREATOR.createFromParcel(source);
} else {
@@ -254,6 +279,18 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(result);
dest.writeStrongInterface(method);
+ if (accessibilitySessions == null) {
+ dest.writeInt(-1);
+ } else {
+ int n = accessibilitySessions.size();
+ dest.writeInt(n);
+ int i = 0;
+ while (i < n) {
+ dest.writeInt(accessibilitySessions.keyAt(i));
+ dest.writeStrongInterface(accessibilitySessions.valueAt(i));
+ i++;
+ }
+ }
if (channel != null) {
dest.writeInt(1);
channel.writeToParcel(dest, flags);
@@ -329,7 +366,7 @@
}
private static InputBindResult error(@ResultCode int result) {
- return new InputBindResult(result, null, null, null, -1, false);
+ return new InputBindResult(result, null, null, null, null, -1, false);
}
/**
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index bf094db..d669768 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -64,6 +64,8 @@
return "DEACTIVATED_BY_IMMS";
case StartInputReason.SESSION_CREATED_BY_IME:
return "SESSION_CREATED_BY_IME";
+ case StartInputReason.BOUND_ACCESSIBILITY_SESSION_TO_IMMS:
+ return "BOUND_ACCESSIBILITY_SESSION_TO_IMMS";
default:
return "Unknown=" + reason;
}
@@ -91,6 +93,8 @@
return "SWITCH_IME_FAILED";
case UnbindReason.SWITCH_USER:
return "SWITCH_USER";
+ case UnbindReason.ACCESSIBILITY_SERVICE_DISABLED:
+ return "ACCESSIBILITY_SERVICE_DISABLED";
default:
return "Unknown=" + reason;
}
diff --git a/core/java/com/android/internal/inputmethod/StartInputReason.java b/core/java/com/android/internal/inputmethod/StartInputReason.java
index 2ba708d..1263466 100644
--- a/core/java/com/android/internal/inputmethod/StartInputReason.java
+++ b/core/java/com/android/internal/inputmethod/StartInputReason.java
@@ -38,7 +38,9 @@
StartInputReason.UNBOUND_FROM_IMMS,
StartInputReason.ACTIVATED_BY_IMMS,
StartInputReason.DEACTIVATED_BY_IMMS,
- StartInputReason.SESSION_CREATED_BY_IME})
+ StartInputReason.SESSION_CREATED_BY_IME,
+ StartInputReason.SESSION_CREATED_BY_ACCESSIBILITY,
+ StartInputReason.BOUND_ACCESSIBILITY_SESSION_TO_IMMS})
public @interface StartInputReason {
/**
* Reason is not specified.
@@ -96,4 +98,14 @@
* {@link com.android.internal.view.IInputSessionCallback#sessionCreated}.
*/
int SESSION_CREATED_BY_IME = 10;
+ /**
+ * {@link android.accessibilityservice.AccessibilityService} is responding to
+ * {@link com.android.internal.view.IInputSessionWithIdCallback#sessionCreated}.
+ */
+ int SESSION_CREATED_BY_ACCESSIBILITY = 11;
+ /**
+ * {@link android.view.inputmethod.InputMethodManager} is responding to
+ * {@link com.android.internal.view.IInputMethodClient#onBindAccessibilityService(InputBindResult, int)}.
+ */
+ int BOUND_ACCESSIBILITY_SESSION_TO_IMMS = 12;
}
diff --git a/core/java/com/android/internal/inputmethod/UnbindReason.java b/core/java/com/android/internal/inputmethod/UnbindReason.java
index f0f18f1..e926625 100644
--- a/core/java/com/android/internal/inputmethod/UnbindReason.java
+++ b/core/java/com/android/internal/inputmethod/UnbindReason.java
@@ -34,7 +34,9 @@
UnbindReason.DISCONNECT_IME,
UnbindReason.NO_IME,
UnbindReason.SWITCH_IME_FAILED,
- UnbindReason.SWITCH_USER})
+ UnbindReason.SWITCH_USER,
+ UnbindReason.ACCESSIBILITY_SERVICE_DISABLED
+})
public @interface UnbindReason {
/**
* Reason is not specified.
@@ -66,4 +68,5 @@
* user's active IME.
*/
int SWITCH_USER = 6;
+ int ACCESSIBILITY_SERVICE_DISABLED = 7;
}
diff --git a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
index d8e89b4..888f830 100644
--- a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
+++ b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
@@ -22,10 +22,6 @@
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
-import android.os.UserHandle;
-import android.util.SparseArray;
-
-import java.util.List;
/**
* Estimates power consumed by the ambient display
@@ -67,29 +63,6 @@
powerMah, powerModel);
}
- /**
- * Ambient display power is the additional power the screen takes while in ambient display/
- * screen doze/ always-on display (interchangeable terms) mode. Ambient display power should
- * be hidden {@link BatteryStatsHelper#shouldHideSipper(BatterySipper)}, but should not be
- * included in smearing {@link BatteryStatsHelper#removeHiddenBatterySippers(List)}.
- */
- @Override
- public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
- final long measuredEnergyUC = batteryStats.getScreenDozeMeasuredBatteryConsumptionUC();
- final long durationMs = calculateDuration(batteryStats, rawRealtimeUs, statsType);
- final int powerModel = getPowerModel(measuredEnergyUC);
- final double powerMah = calculateTotalPower(powerModel, batteryStats, rawRealtimeUs,
- measuredEnergyUC);
- if (powerMah > 0) {
- BatterySipper bs = new BatterySipper(BatterySipper.DrainType.AMBIENT_DISPLAY, null, 0);
- bs.usagePowerMah = powerMah;
- bs.usageTimeMs = durationMs;
- bs.sumPower();
- sippers.add(bs);
- }
- }
-
private long calculateDuration(BatteryStats batteryStats, long rawRealtimeUs, int statsType) {
return batteryStats.getScreenDozeTime(rawRealtimeUs, statsType) / 1000;
}
diff --git a/core/java/com/android/internal/os/BatteryChargeCalculator.java b/core/java/com/android/internal/os/BatteryChargeCalculator.java
index 71a1463..912ec8f 100644
--- a/core/java/com/android/internal/os/BatteryChargeCalculator.java
+++ b/core/java/com/android/internal/os/BatteryChargeCalculator.java
@@ -20,10 +20,6 @@
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
-import android.os.UserHandle;
-import android.util.SparseArray;
-
-import java.util.List;
/**
* Estimates the battery discharge amounts.
@@ -81,10 +77,4 @@
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
.setConsumedPower(dischargeMah);
}
-
- @Override
- public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
- // Not implemented. The computation is done by BatteryStatsHelper
- }
}
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
deleted file mode 100644
index dfd561a..0000000
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Copyright (C) 2009 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.internal.os;
-
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.BatteryStats.Uid;
-import android.os.Build;
-
-import java.util.List;
-
-/**
- * Contains power usage of an application, system service, or hardware type.
- *
- * @deprecated Please use BatteryStatsManager.getBatteryUsageStats instead.
- */
-@Deprecated
-public class BatterySipper implements Comparable<BatterySipper> {
- @UnsupportedAppUsage
- public int userId;
- @UnsupportedAppUsage
- public Uid uidObj;
- @UnsupportedAppUsage
- public DrainType drainType;
-
- /**
- * Smeared power from screen usage.
- * We split the screen usage power and smear them among apps, based on activity time.
- * The actual screen usage power may be measured or estimated, affecting the granularity and
- * accuracy of the smearing, but the smearing algorithm is essentially the same.
- */
- public double screenPowerMah;
-
- /**
- * Smeared power using proportional method.
- *
- * we smear power usage from hidden sippers to all apps proportionally.(except for screen usage)
- *
- * @see BatteryStatsHelper#shouldHideSipper(BatterySipper)
- * @see BatteryStatsHelper#removeHiddenBatterySippers(List)
- */
- public double proportionalSmearMah;
-
- /**
- * Total power that adding the smeared power.
- *
- * @see #sumPower()
- */
- public double totalSmearedPowerMah;
-
- /**
- * Total power before smearing
- */
- @UnsupportedAppUsage
- public double totalPowerMah;
-
- /**
- * Whether we should hide this sipper
- *
- * @see BatteryStatsHelper#shouldHideSipper(BatterySipper)
- */
- public boolean shouldHide;
-
- /**
- * Generic usage time in milliseconds.
- */
- @UnsupportedAppUsage
- public long usageTimeMs;
-
- /**
- * Generic power usage in mAh.
- */
- public double usagePowerMah;
-
- // Subsystem usage times.
- public long audioTimeMs;
- public long bluetoothRunningTimeMs;
- public long cameraTimeMs;
- @UnsupportedAppUsage
- public long cpuFgTimeMs;
- @UnsupportedAppUsage
- public long cpuTimeMs;
- public long flashlightTimeMs;
- @UnsupportedAppUsage
- public long gpsTimeMs;
- public long videoTimeMs;
- @UnsupportedAppUsage
- public long wakeLockTimeMs;
- @UnsupportedAppUsage
- public long wifiRunningTimeMs;
-
- public long mobileRxPackets;
- public long mobileTxPackets;
- public long mobileActive;
- public int mobileActiveCount;
- public double mobilemspp; // milliseconds per packet
- public long wifiRxPackets;
- public long wifiTxPackets;
- public long mobileRxBytes;
- public long mobileTxBytes;
- public long wifiRxBytes;
- public long wifiTxBytes;
- public long btRxBytes;
- public long btTxBytes;
- public double percent;
- public double noCoveragePercent;
- @UnsupportedAppUsage
- public String[] mPackages;
- @UnsupportedAppUsage
- public String packageWithHighestDrain;
-
- // Measured in mAh (milli-ampere per hour).
- // These are included when summed.
- public double audioPowerMah;
- public double bluetoothPowerMah;
- public double cameraPowerMah;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public double cpuPowerMah;
- public double flashlightPowerMah;
- public double gpsPowerMah;
- public double mobileRadioPowerMah;
- public double sensorPowerMah;
- public double videoPowerMah;
- public double wakeLockPowerMah;
- public double wifiPowerMah;
- public double systemServiceCpuPowerMah;
- public double[] customMeasuredPowerMah;
-
- // Power that is re-attributed to other sippers. For example, for System Server
- // this represents the power attributed to apps requesting system services.
- // The value should be negative or zero.
- public double powerReattributedToOtherSippersMah;
-
- // Do not include this sipper in results because it is included
- // in an aggregate sipper.
- public boolean isAggregated;
-
- // ****************
- // This list must be kept current with atoms.proto (frameworks/base/cmds/statsd/src/atoms.proto)
- // so the ordinal values (and therefore the order) must never change.
- // ****************
- @UnsupportedAppUsage(implicitMember =
- "values()[Lcom/android/internal/os/BatterySipper$DrainType;")
- public enum DrainType {
- AMBIENT_DISPLAY,
- @UnsupportedAppUsage
- APP,
- BLUETOOTH,
- CAMERA,
- CELL,
- FLASHLIGHT,
- IDLE,
- MEMORY,
- OVERCOUNTED,
- PHONE,
- SCREEN,
- UNACCOUNTED,
- USER,
- WIFI,
- }
-
- @UnsupportedAppUsage
- public BatterySipper(DrainType drainType, Uid uid, double value) {
- this.totalPowerMah = value;
- this.drainType = drainType;
- uidObj = uid;
- }
-
- public void computeMobilemspp() {
- long packets = mobileRxPackets + mobileTxPackets;
- mobilemspp = packets > 0 ? (mobileActive / (double) packets) : 0;
- }
-
- @Override
- public int compareTo(BatterySipper other) {
- // Over-counted always goes to the bottom.
- if (drainType != other.drainType) {
- if (drainType == DrainType.OVERCOUNTED) {
- // This is "larger"
- return 1;
- } else if (other.drainType == DrainType.OVERCOUNTED) {
- return -1;
- }
- }
- // Return the flipped value because we want the items in descending order
- return Double.compare(other.totalPowerMah, totalPowerMah);
- }
-
- /**
- * Gets a list of packages associated with the current user
- */
- @UnsupportedAppUsage
- public String[] getPackages() {
- return mPackages;
- }
-
- @UnsupportedAppUsage
- public int getUid() {
- // Bail out if the current sipper is not an App sipper.
- if (uidObj == null) {
- return 0;
- }
- return uidObj.getUid();
- }
-
- /**
- * Add stats from other to this BatterySipper.
- */
- @UnsupportedAppUsage
- public void add(BatterySipper other) {
- totalPowerMah += other.totalPowerMah;
- usageTimeMs += other.usageTimeMs;
- usagePowerMah += other.usagePowerMah;
- audioTimeMs += other.audioTimeMs;
- cpuTimeMs += other.cpuTimeMs;
- gpsTimeMs += other.gpsTimeMs;
- wifiRunningTimeMs += other.wifiRunningTimeMs;
- cpuFgTimeMs += other.cpuFgTimeMs;
- videoTimeMs += other.videoTimeMs;
- wakeLockTimeMs += other.wakeLockTimeMs;
- cameraTimeMs += other.cameraTimeMs;
- flashlightTimeMs += other.flashlightTimeMs;
- bluetoothRunningTimeMs += other.bluetoothRunningTimeMs;
- mobileRxPackets += other.mobileRxPackets;
- mobileTxPackets += other.mobileTxPackets;
- mobileActive += other.mobileActive;
- mobileActiveCount += other.mobileActiveCount;
- wifiRxPackets += other.wifiRxPackets;
- wifiTxPackets += other.wifiTxPackets;
- mobileRxBytes += other.mobileRxBytes;
- mobileTxBytes += other.mobileTxBytes;
- wifiRxBytes += other.wifiRxBytes;
- wifiTxBytes += other.wifiTxBytes;
- btRxBytes += other.btRxBytes;
- btTxBytes += other.btTxBytes;
- audioPowerMah += other.audioPowerMah;
- wifiPowerMah += other.wifiPowerMah;
- gpsPowerMah += other.gpsPowerMah;
- cpuPowerMah += other.cpuPowerMah;
- sensorPowerMah += other.sensorPowerMah;
- mobileRadioPowerMah += other.mobileRadioPowerMah;
- wakeLockPowerMah += other.wakeLockPowerMah;
- cameraPowerMah += other.cameraPowerMah;
- flashlightPowerMah += other.flashlightPowerMah;
- bluetoothPowerMah += other.bluetoothPowerMah;
- screenPowerMah += other.screenPowerMah;
- videoPowerMah += other.videoPowerMah;
- proportionalSmearMah += other.proportionalSmearMah;
- totalSmearedPowerMah += other.totalSmearedPowerMah;
- systemServiceCpuPowerMah += other.systemServiceCpuPowerMah;
- if (other.customMeasuredPowerMah != null) {
- if (customMeasuredPowerMah == null) {
- customMeasuredPowerMah = new double[other.customMeasuredPowerMah.length];
- }
- if (customMeasuredPowerMah.length == other.customMeasuredPowerMah.length) {
- // This should always be true.
- for (int idx = 0; idx < other.customMeasuredPowerMah.length; idx++) {
- customMeasuredPowerMah[idx] += other.customMeasuredPowerMah[idx];
- }
- }
- }
- powerReattributedToOtherSippersMah += other.powerReattributedToOtherSippersMah;
- }
-
- /**
- * Sum all the powers and store the value into `value`.
- * Also sum the {@code smearedTotalPowerMah} by adding smeared powerMah.
- *
- * @return the sum of all the power in this BatterySipper.
- */
- public double sumPower() {
- totalPowerMah = usagePowerMah + wifiPowerMah + gpsPowerMah + cpuPowerMah +
- sensorPowerMah + mobileRadioPowerMah + wakeLockPowerMah + cameraPowerMah +
- flashlightPowerMah + bluetoothPowerMah + audioPowerMah + videoPowerMah
- + systemServiceCpuPowerMah;
- if (customMeasuredPowerMah != null) {
- for (int idx = 0; idx < customMeasuredPowerMah.length; idx++) {
- totalPowerMah += customMeasuredPowerMah[idx];
- }
- }
-
- // powerAttributedToOtherSippersMah is negative or zero
- totalPowerMah = totalPowerMah + powerReattributedToOtherSippersMah;
-
- totalSmearedPowerMah = totalPowerMah + screenPowerMah + proportionalSmearMah;
-
- return totalPowerMah;
- }
-}
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
deleted file mode 100644
index 4515a09..0000000
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ /dev/null
@@ -1,703 +0,0 @@
-/*
- * Copyright (C) 2009 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.internal.os;
-
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.hardware.SensorManager;
-import android.os.BatteryStats;
-import android.os.BatteryStats.Uid;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.MemoryFile;
-import android.os.Parcel;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.SELinux;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.telephony.TelephonyManager;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.os.BatterySipper.DrainType;
-import com.android.internal.util.ArrayUtils;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A helper class for retrieving the power usage information for all applications and services.
- *
- * The caller must initialize this class as soon as activity object is ready to use (for example, in
- * onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy().
- *
- * @deprecated Please use BatteryStatsManager.getBatteryUsageStats instead.
- */
-@Deprecated
-public class BatteryStatsHelper {
- private static final boolean DEBUG = false;
-
- private static final String TAG = BatteryStatsHelper.class.getSimpleName();
-
- private static BatteryStats sStatsXfer;
- private static Intent sBatteryBroadcastXfer;
- private static ArrayMap<File, BatteryStats> sFileXfer = new ArrayMap<>();
-
- final private Context mContext;
- final private boolean mCollectBatteryBroadcast;
- final private boolean mWifiOnly;
-
- private List<PowerCalculator> mPowerCalculators;
-
- @UnsupportedAppUsage
- private IBatteryStats mBatteryInfo;
- private BatteryStats mStats;
- private Intent mBatteryBroadcast;
- @UnsupportedAppUsage
- private PowerProfile mPowerProfile;
-
- private String[] mSystemPackageArray;
- private String[] mServicepackageArray;
- private PackageManager mPackageManager;
-
- /**
- * List of apps using power.
- */
- @UnsupportedAppUsage
- private final List<BatterySipper> mUsageList = new ArrayList<>();
-
- private final List<BatterySipper> mMobilemsppList = new ArrayList<>();
-
- private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
-
- long mRawRealtimeUs;
- long mRawUptimeUs;
- long mBatteryRealtimeUs;
- long mBatteryUptimeUs;
- long mBatteryTimeRemainingUs;
- long mChargeTimeRemainingUs;
-
- private long mStatsPeriod = 0;
-
- // The largest entry by power.
- private double mMaxPower = 1;
-
- // The largest real entry by power (not undercounted or overcounted).
- private double mMaxRealPower = 1;
-
- // Total computed power.
- private double mComputedPower;
- private double mTotalPower;
- private double mMinDrainedPower;
- private double mMaxDrainedPower;
-
- public static boolean checkWifiOnly(Context context) {
- final TelephonyManager tm = context.getSystemService(TelephonyManager.class);
- if (tm == null) {
- return false;
- }
- return !tm.isDataCapable();
- }
-
- @UnsupportedAppUsage
- public BatteryStatsHelper(Context context) {
- this(context, true);
- }
-
- @UnsupportedAppUsage
- public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast) {
- this(context, collectBatteryBroadcast, checkWifiOnly(context));
- }
-
- @UnsupportedAppUsage
- public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly) {
- mContext = context;
- mCollectBatteryBroadcast = collectBatteryBroadcast;
- mWifiOnly = wifiOnly;
- mPackageManager = context.getPackageManager();
-
- final Resources resources = context.getResources();
- mSystemPackageArray = resources.getStringArray(
- com.android.internal.R.array.config_batteryPackageTypeSystem);
- mServicepackageArray = resources.getStringArray(
- com.android.internal.R.array.config_batteryPackageTypeService);
- }
-
- public void storeStatsHistoryInFile(String fname) {
- synchronized (sFileXfer) {
- File path = makeFilePath(mContext, fname);
- sFileXfer.put(path, this.getStats());
- FileOutputStream fout = null;
- try {
- fout = new FileOutputStream(path);
- Parcel hist = Parcel.obtain();
- getStats().writeToParcelWithoutUids(hist, 0);
- byte[] histData = hist.marshall();
- fout.write(histData);
- } catch (IOException e) {
- Log.w(TAG, "Unable to write history to file", e);
- } finally {
- if (fout != null) {
- try {
- fout.close();
- } catch (IOException e) {
- }
- }
- }
- }
- }
-
- public static BatteryStats statsFromFile(Context context, String fname) {
- synchronized (sFileXfer) {
- File path = makeFilePath(context, fname);
- BatteryStats stats = sFileXfer.get(path);
- if (stats != null) {
- return stats;
- }
- FileInputStream fin = null;
- try {
- fin = new FileInputStream(path);
- byte[] data = readFully(fin);
- Parcel parcel = Parcel.obtain();
- parcel.unmarshall(data, 0, data.length);
- parcel.setDataPosition(0);
- return com.android.internal.os.BatteryStatsImpl.CREATOR.createFromParcel(parcel);
- } catch (IOException e) {
- Log.w(TAG, "Unable to read history to file", e);
- } finally {
- if (fin != null) {
- try {
- fin.close();
- } catch (IOException e) {
- }
- }
- }
- }
- return getStats(IBatteryStats.Stub.asInterface(
- ServiceManager.getService(BatteryStats.SERVICE_NAME)), true);
- }
-
- @UnsupportedAppUsage
- public static void dropFile(Context context, String fname) {
- makeFilePath(context, fname).delete();
- }
-
- private static File makeFilePath(Context context, String fname) {
- return new File(context.getFilesDir(), fname);
- }
-
- /** Clears the current stats and forces recreating for future use. */
- @UnsupportedAppUsage
- public void clearStats() {
- mStats = null;
- }
-
- @UnsupportedAppUsage
- public BatteryStats getStats() {
- return getStats(true /* updateAll */);
- }
-
- /** Retrieves stats from BatteryService, optionally getting updated numbers */
- public BatteryStats getStats(boolean updateAll) {
- if (mStats == null) {
- load(updateAll);
- }
- return mStats;
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public Intent getBatteryBroadcast() {
- if (mBatteryBroadcast == null && mCollectBatteryBroadcast) {
- load();
- }
- return mBatteryBroadcast;
- }
-
- public PowerProfile getPowerProfile() {
- return mPowerProfile;
- }
-
- public void create(BatteryStats stats) {
- mPowerProfile = new PowerProfile(mContext);
- mStats = stats;
- }
-
- @UnsupportedAppUsage
- public void create(Bundle icicle) {
- if (icicle != null) {
- mStats = sStatsXfer;
- mBatteryBroadcast = sBatteryBroadcastXfer;
- }
- mBatteryInfo = IBatteryStats.Stub.asInterface(
- ServiceManager.getService(BatteryStats.SERVICE_NAME));
- mPowerProfile = new PowerProfile(mContext);
- }
-
- @UnsupportedAppUsage
- public void storeState() {
- sStatsXfer = mStats;
- sBatteryBroadcastXfer = mBatteryBroadcast;
- }
-
- /**
- * Refreshes the power usage list.
- */
- @UnsupportedAppUsage
- public void refreshStats(int statsType, int asUser) {
- SparseArray<UserHandle> users = new SparseArray<>(1);
- users.put(asUser, new UserHandle(asUser));
- refreshStats(statsType, users);
- }
-
- /**
- * Refreshes the power usage list.
- */
- @UnsupportedAppUsage
- public void refreshStats(int statsType, List<UserHandle> asUsers) {
- final int n = asUsers.size();
- SparseArray<UserHandle> users = new SparseArray<>(n);
- for (int i = 0; i < n; ++i) {
- UserHandle userHandle = asUsers.get(i);
- users.put(userHandle.getIdentifier(), userHandle);
- }
- refreshStats(statsType, users);
- }
-
- /**
- * Refreshes the power usage list.
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public void refreshStats(int statsType, SparseArray<UserHandle> asUsers) {
- refreshStats(statsType, asUsers, SystemClock.elapsedRealtime() * 1000,
- SystemClock.uptimeMillis() * 1000);
- }
-
- public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs,
- long rawUptimeUs) {
- if (statsType != BatteryStats.STATS_SINCE_CHARGED) {
- Log.w(TAG, "refreshStats called for statsType " + statsType + " but only "
- + "STATS_SINCE_CHARGED is supported. Using STATS_SINCE_CHARGED instead.");
- }
-
- // Initialize mStats if necessary.
- getStats();
-
- mMaxPower = 0;
- mMaxRealPower = 0;
- mComputedPower = 0;
- mTotalPower = 0;
-
- mUsageList.clear();
- mMobilemsppList.clear();
-
- if (mStats == null) {
- return;
- }
-
- if (mPowerCalculators == null) {
- mPowerCalculators = new ArrayList<>();
-
- // Power calculators are applied in the order of registration
- mPowerCalculators.add(new CpuPowerCalculator(mPowerProfile));
- mPowerCalculators.add(new MemoryPowerCalculator(mPowerProfile));
- mPowerCalculators.add(new WakelockPowerCalculator(mPowerProfile));
- if (!mWifiOnly) {
- mPowerCalculators.add(new MobileRadioPowerCalculator(mPowerProfile));
- }
- mPowerCalculators.add(new WifiPowerCalculator(mPowerProfile));
- mPowerCalculators.add(new BluetoothPowerCalculator(mPowerProfile));
- mPowerCalculators.add(new SensorPowerCalculator(
- mContext.getSystemService(SensorManager.class)));
- mPowerCalculators.add(new GnssPowerCalculator(mPowerProfile));
- mPowerCalculators.add(new CameraPowerCalculator(mPowerProfile));
- mPowerCalculators.add(new FlashlightPowerCalculator(mPowerProfile));
- mPowerCalculators.add(new MediaPowerCalculator(mPowerProfile));
- mPowerCalculators.add(new PhonePowerCalculator(mPowerProfile));
- mPowerCalculators.add(new ScreenPowerCalculator(mPowerProfile));
- mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile));
- mPowerCalculators.add(new SystemServicePowerCalculator(mPowerProfile));
- mPowerCalculators.add(new IdlePowerCalculator(mPowerProfile));
- mPowerCalculators.add(new CustomMeasuredPowerCalculator(mPowerProfile));
-
- mPowerCalculators.add(new UserPowerCalculator());
- }
-
- for (int i = 0, size = mPowerCalculators.size(); i < size; i++) {
- mPowerCalculators.get(i).reset();
- }
-
- mStatsType = statsType;
- mRawUptimeUs = rawUptimeUs;
- mRawRealtimeUs = rawRealtimeUs;
- mBatteryUptimeUs = mStats.getBatteryUptime(rawUptimeUs);
- mBatteryRealtimeUs = mStats.getBatteryRealtime(rawRealtimeUs);
- mBatteryTimeRemainingUs = mStats.computeBatteryTimeRemaining(rawRealtimeUs);
- mChargeTimeRemainingUs = mStats.computeChargeTimeRemaining(rawRealtimeUs);
- mStatsPeriod = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType);
-
- if (DEBUG) {
- Log.d(TAG, "Raw time: realtime=" + (rawRealtimeUs / 1000) + " uptime="
- + (rawUptimeUs / 1000));
- Log.d(TAG, "Battery time: realtime=" + (mBatteryRealtimeUs / 1000) + " uptime="
- + (mBatteryUptimeUs / 1000));
- Log.d(TAG, "Battery type time: realtime=" + (mStatsPeriod / 1000) + " uptime="
- + (mStats.computeBatteryUptime(rawRealtimeUs, mStatsType) / 1000));
- }
- mMinDrainedPower = (mStats.getLowDischargeAmountSinceCharge()
- * mPowerProfile.getBatteryCapacity()) / 100;
- mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge()
- * mPowerProfile.getBatteryCapacity()) / 100;
-
- // Create list of (almost all) sippers, calculate their usage, and put them in mUsageList.
- processAppUsage(asUsers);
-
- Collections.sort(mUsageList);
-
- Collections.sort(mMobilemsppList,
- (lhs, rhs) -> Double.compare(rhs.mobilemspp, lhs.mobilemspp));
-
- // At this point, we've sorted the list so we are guaranteed the max values are at the top.
- // We have only added real powers so far.
- if (!mUsageList.isEmpty()) {
- mMaxRealPower = mMaxPower = mUsageList.get(0).totalPowerMah;
- final int usageListCount = mUsageList.size();
- for (int i = 0; i < usageListCount; i++) {
- mComputedPower += mUsageList.get(i).totalPowerMah;
- }
- }
-
- if (DEBUG) {
- Log.d(TAG, "Accuracy: total computed=" + PowerCalculator.formatCharge(mComputedPower)
- + ", min discharge=" + PowerCalculator.formatCharge(mMinDrainedPower)
- + ", max discharge=" + PowerCalculator.formatCharge(mMaxDrainedPower));
- }
-
- mTotalPower = mComputedPower;
- if (mStats.getLowDischargeAmountSinceCharge() > 1) {
- if (mMinDrainedPower > mComputedPower) {
- double amount = mMinDrainedPower - mComputedPower;
- mTotalPower = mMinDrainedPower;
- BatterySipper bs = new BatterySipper(DrainType.UNACCOUNTED, null, amount);
-
- // Insert the BatterySipper in its sorted position.
- int index = Collections.binarySearch(mUsageList, bs);
- if (index < 0) {
- index = -(index + 1);
- }
- mUsageList.add(index, bs);
- mMaxPower = Math.max(mMaxPower, amount);
- } else if (mMaxDrainedPower < mComputedPower) {
- double amount = mComputedPower - mMaxDrainedPower;
-
- // Insert the BatterySipper in its sorted position.
- BatterySipper bs = new BatterySipper(DrainType.OVERCOUNTED, null, amount);
- int index = Collections.binarySearch(mUsageList, bs);
- if (index < 0) {
- index = -(index + 1);
- }
- mUsageList.add(index, bs);
- mMaxPower = Math.max(mMaxPower, amount);
- }
- }
-
- // Smear it!
- final double hiddenPowerMah = removeHiddenBatterySippers(mUsageList);
- final double totalRemainingPower = getTotalPower() - hiddenPowerMah;
- if (Math.abs(totalRemainingPower) > 1e-3) {
- for (int i = 0, size = mUsageList.size(); i < size; i++) {
- final BatterySipper sipper = mUsageList.get(i);
- if (!sipper.shouldHide) {
- sipper.proportionalSmearMah = hiddenPowerMah
- * ((sipper.totalPowerMah + sipper.screenPowerMah)
- / totalRemainingPower);
- sipper.sumPower();
- }
- }
- }
- }
-
- private void processAppUsage(SparseArray<UserHandle> asUsers) {
- final SparseArray<? extends Uid> uidStats = mStats.getUidStats();
-
- final ArrayList<BatterySipper> sippers = new ArrayList<>(uidStats.size());
-
- for (int iu = 0, size = uidStats.size(); iu < size; iu++) {
- final Uid u = uidStats.valueAt(iu);
- sippers.add(new BatterySipper(DrainType.APP, u, 0));
- }
-
- for (int i = 0, size = mPowerCalculators.size(); i < size; i++) {
- final PowerCalculator calculator = mPowerCalculators.get(i);
- calculator.calculate(sippers, mStats, mRawRealtimeUs, mRawUptimeUs, mStatsType,
- asUsers);
- }
-
- for (int i = sippers.size() - 1; i >= 0; i--) {
- final BatterySipper sipper = sippers.get(i);
- final double totalPower = sipper.sumPower();
- if (DEBUG && totalPower != 0) {
- Log.d(TAG, String.format("UID %d: total power=%s", sipper.getUid(),
- PowerCalculator.formatCharge(totalPower)));
- }
-
- // Add the sipper to the list if it is consuming power.
- if (totalPower != 0 || sipper.getUid() == 0) {
- if (sipper.drainType == DrainType.APP) {
- sipper.computeMobilemspp();
- if (sipper.mobilemspp != 0) {
- mMobilemsppList.add(sipper);
- }
- }
-
- if (!sipper.isAggregated) {
- mUsageList.add(sipper);
- }
- }
- }
- }
-
- @UnsupportedAppUsage
- public List<BatterySipper> getUsageList() {
- return mUsageList;
- }
-
- public List<BatterySipper> getMobilemsppList() {
- return mMobilemsppList;
- }
-
- public long getStatsPeriod() {
- return mStatsPeriod;
- }
-
- public int getStatsType() {
- return mStatsType;
- }
-
- @UnsupportedAppUsage
- public double getMaxPower() {
- return mMaxPower;
- }
-
- public double getMaxRealPower() {
- return mMaxRealPower;
- }
-
- @UnsupportedAppUsage
- public double getTotalPower() {
- return mTotalPower;
- }
-
- public double getComputedPower() {
- return mComputedPower;
- }
-
- public double getMinDrainedPower() {
- return mMinDrainedPower;
- }
-
- public double getMaxDrainedPower() {
- return mMaxDrainedPower;
- }
-
- public static byte[] readFully(FileInputStream stream) throws java.io.IOException {
- return readFully(stream, stream.available());
- }
-
- public static byte[] readFully(FileInputStream stream, int avail) throws java.io.IOException {
- int pos = 0;
- byte[] data = new byte[avail];
- while (true) {
- int amt = stream.read(data, pos, data.length - pos);
- //Log.i("foo", "Read " + amt + " bytes at " + pos
- // + " of avail " + data.length);
- if (amt <= 0) {
- //Log.i("foo", "**** FINISHED READING: pos=" + pos
- // + " len=" + data.length);
- return data;
- }
- pos += amt;
- avail = stream.available();
- if (avail > data.length - pos) {
- byte[] newData = new byte[pos + avail];
- System.arraycopy(data, 0, newData, 0, pos);
- data = newData;
- }
- }
- }
-
- /**
- * Mark the {@link BatterySipper} that we should hide.
- *
- * @param sippers sipper list that need to check and remove
- * @return the total power of the hidden items of {@link BatterySipper}
- * for proportional smearing
- */
- public double removeHiddenBatterySippers(List<BatterySipper> sippers) {
- double proportionalSmearPowerMah = 0;
- for (int i = sippers.size() - 1; i >= 0; i--) {
- final BatterySipper sipper = sippers.get(i);
- sipper.shouldHide = shouldHideSipper(sipper);
- if (sipper.shouldHide) {
- if (sipper.drainType != DrainType.OVERCOUNTED
- && sipper.drainType != DrainType.SCREEN
- && sipper.drainType != DrainType.AMBIENT_DISPLAY
- && sipper.drainType != DrainType.UNACCOUNTED
- && sipper.drainType != DrainType.BLUETOOTH
- && sipper.drainType != DrainType.WIFI
- && sipper.drainType != DrainType.IDLE) {
- // Don't add it if it is overcounted, unaccounted or screen
- proportionalSmearPowerMah += sipper.totalPowerMah;
- }
- }
- }
- return proportionalSmearPowerMah;
- }
-
- /**
- * Check whether we should hide the battery sipper.
- */
- public boolean shouldHideSipper(BatterySipper sipper) {
- final DrainType drainType = sipper.drainType;
-
- return drainType == DrainType.IDLE
- || drainType == DrainType.CELL
- || drainType == DrainType.SCREEN
- || drainType == DrainType.AMBIENT_DISPLAY
- || drainType == DrainType.UNACCOUNTED
- || drainType == DrainType.OVERCOUNTED
- || isTypeService(sipper)
- || isTypeSystem(sipper);
- }
-
- /**
- * Check whether {@code sipper} is type service
- */
- public boolean isTypeService(BatterySipper sipper) {
- final String[] packages = mPackageManager.getPackagesForUid(sipper.getUid());
- if (packages == null) {
- return false;
- }
-
- for (String packageName : packages) {
- if (ArrayUtils.contains(mServicepackageArray, packageName)) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Check whether {@code sipper} is type system
- */
- public boolean isTypeSystem(BatterySipper sipper) {
- final int uid = sipper.uidObj == null ? -1 : sipper.getUid();
- sipper.mPackages = mPackageManager.getPackagesForUid(uid);
- // Classify all the sippers to type system if the range of uid is 0...FIRST_APPLICATION_UID
- if (uid >= Process.ROOT_UID && uid < Process.FIRST_APPLICATION_UID) {
- return true;
- } else if (sipper.mPackages != null) {
- for (final String packageName : sipper.mPackages) {
- if (ArrayUtils.contains(mSystemPackageArray, packageName)) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- public long convertUsToMs(long timeUs) {
- return timeUs / 1000;
- }
-
- public long convertMsToUs(long timeMs) {
- return timeMs * 1000;
- }
-
- @VisibleForTesting
- public void setPackageManager(PackageManager packageManager) {
- mPackageManager = packageManager;
- }
-
- @VisibleForTesting
- public void setSystemPackageArray(String[] array) {
- mSystemPackageArray = array;
- }
-
- @VisibleForTesting
- public void setServicePackageArray(String[] array) {
- mServicepackageArray = array;
- }
-
- @UnsupportedAppUsage
- private void load() {
- load(true);
- }
-
- private void load(boolean updateAll) {
- if (mBatteryInfo == null) {
- return;
- }
- mStats = getStats(mBatteryInfo, updateAll);
- if (mCollectBatteryBroadcast) {
- mBatteryBroadcast = mContext.registerReceiver(null,
- new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
- }
- }
-
- private static BatteryStatsImpl getStats(IBatteryStats service, boolean updateAll) {
- try {
- ParcelFileDescriptor pfd = service.getStatisticsStream(updateAll);
- if (pfd != null) {
- if (false) {
- Log.d(TAG, "selinux context: "
- + SELinux.getFileContext(pfd.getFileDescriptor()));
- }
- try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
- byte[] data = readFully(fis, MemoryFile.getSize(pfd.getFileDescriptor()));
- Parcel parcel = Parcel.obtain();
- parcel.unmarshall(data, 0, data.length);
- parcel.setDataPosition(0);
- BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR
- .createFromParcel(parcel);
- return stats;
- } catch (IOException e) {
- Log.w(TAG, "Unable to read statistics stream", e);
- }
- }
- } catch (RemoteException e) {
- Log.w(TAG, "RemoteException:", e);
- }
- return new BatteryStatsImpl();
- }
-}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 5ba45c9..70b9639 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -9211,7 +9211,7 @@
* Gets the minimum of the uid's foreground activity time and its PROCESS_STATE_TOP time
* since last marked. Also sets the mark time for both these timers.
*
- * @see BatteryStatsHelper#getProcessForegroundTimeMs
+ * @see CpuPowerCalculator
*
* @param doCalc if true, then calculate the minimum; else don't bother and return 0. Either
* way, the mark is set.
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index e4d5fb7..a1c1917 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -73,7 +73,7 @@
mPowerCalculators.add(new CpuPowerCalculator(mPowerProfile));
mPowerCalculators.add(new MemoryPowerCalculator(mPowerProfile));
mPowerCalculators.add(new WakelockPowerCalculator(mPowerProfile));
- if (!BatteryStatsHelper.checkWifiOnly(mContext)) {
+ if (!BatteryStats.checkWifiOnly(mContext)) {
mPowerCalculators.add(new MobileRadioPowerCalculator(mPowerProfile));
}
mPowerCalculators.add(new WifiPowerCalculator(mPowerProfile));
diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
index afa41a7..2ebf689 100644
--- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java
+++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
@@ -21,14 +21,11 @@
import android.os.BatteryStats.ControllerActivityCounter;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
-import android.os.Process;
import android.os.UidBatteryConsumer;
-import android.os.UserHandle;
import android.util.Log;
import android.util.SparseArray;
import java.util.Arrays;
-import java.util.List;
public class BluetoothPowerCalculator extends PowerCalculator {
private static final String TAG = "BluetoothPowerCalc";
@@ -106,7 +103,7 @@
powerAndDuration.durationMs - powerAndDuration.totalDurationMs);
if (DEBUG) {
Log.d(TAG, "Bluetooth active: time=" + (systemComponentDurationMs)
- + " power=" + formatCharge(powerAndDuration.powerMah));
+ + " power=" + BatteryStats.formatCharge(powerAndDuration.powerMah));
}
builder.getAggregateBatteryConsumerBuilder(
@@ -159,73 +156,6 @@
}
}
- @Override
- public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
- if (!mHasBluetoothPowerController || !batteryStats.hasBluetoothActivityReporting()) {
- return;
- }
-
- PowerAndDuration powerAndDuration = new PowerAndDuration();
-
- for (int i = sippers.size() - 1; i >= 0; i--) {
- final BatterySipper app = sippers.get(i);
- if (app.drainType == BatterySipper.DrainType.APP) {
- calculateApp(app, app.uidObj, statsType, powerAndDuration);
- }
- }
-
- BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0);
- final long measuredChargeUC = batteryStats.getBluetoothMeasuredBatteryConsumptionUC();
- final int powerModel = getPowerModel(measuredChargeUC);
- final ControllerActivityCounter activityCounter =
- batteryStats.getBluetoothControllerActivity();
- calculatePowerAndDuration(null, powerModel, measuredChargeUC, activityCounter, false,
- powerAndDuration);
-
- // Subtract what the apps used, but clamp to 0.
- final double powerMah = Math.max(0,
- powerAndDuration.powerMah - powerAndDuration.totalPowerMah);
- final long durationMs = Math.max(0,
- powerAndDuration.durationMs - powerAndDuration.totalDurationMs);
- if (DEBUG && powerMah != 0) {
- Log.d(TAG, "Bluetooth active: time=" + (durationMs)
- + " power=" + formatCharge(powerMah));
- }
-
- bs.bluetoothPowerMah = powerMah;
- bs.bluetoothRunningTimeMs = durationMs;
-
- for (int i = sippers.size() - 1; i >= 0; i--) {
- BatterySipper app = sippers.get(i);
- if (app.getUid() == Process.BLUETOOTH_UID) {
- if (DEBUG) Log.d(TAG, "Bluetooth adding sipper " + app + ": cpu=" + app.cpuTimeMs);
- app.isAggregated = true;
- bs.add(app);
- }
- }
- if (bs.sumPower() > 0) {
- sippers.add(bs);
- }
- }
-
- private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType,
- PowerAndDuration powerAndDuration) {
- final long measuredChargeUC = u.getBluetoothMeasuredBatteryConsumptionUC();
- final int powerModel = getPowerModel(measuredChargeUC);
- final ControllerActivityCounter activityCounter = u.getBluetoothControllerActivity();
- calculatePowerAndDuration(u, powerModel, measuredChargeUC, activityCounter,
- false, powerAndDuration);
-
- app.bluetoothRunningTimeMs = powerAndDuration.durationMs;
- app.bluetoothPowerMah = powerAndDuration.powerMah;
- app.btRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_BT_RX_DATA, statsType);
- app.btTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_BT_TX_DATA, statsType);
-
- powerAndDuration.totalDurationMs += powerAndDuration.durationMs;
- powerAndDuration.totalPowerMah += powerAndDuration.powerMah;
- }
-
/** Returns bluetooth power usage based on the best data available. */
private void calculatePowerAndDuration(@Nullable BatteryStats.Uid uid,
@BatteryConsumer.PowerModel int powerModel,
diff --git a/core/java/com/android/internal/os/CameraPowerCalculator.java b/core/java/com/android/internal/os/CameraPowerCalculator.java
index 7bccab5..d0749e0 100644
--- a/core/java/com/android/internal/os/CameraPowerCalculator.java
+++ b/core/java/com/android/internal/os/CameraPowerCalculator.java
@@ -69,14 +69,4 @@
app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA, durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA, powerMah);
}
-
- @Override
- protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
- long rawUptimeUs, int statsType) {
- final long durationMs = mPowerEstimator.calculateDuration(u.getCameraTurnedOnTimer(),
- rawRealtimeUs, statsType);
- final double powerMah = mPowerEstimator.calculatePower(durationMs);
- app.cameraTimeMs = durationMs;
- app.cameraPowerMah = powerMah;
- }
}
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index 9940a0a..1fc2baf 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -20,13 +20,11 @@
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.UidBatteryConsumer;
-import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
import java.util.Arrays;
-import java.util.List;
public class CpuPowerCalculator extends PowerCalculator {
private static final String TAG = "CpuPowerCalculator";
@@ -217,29 +215,6 @@
}
}
- @Override
- public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
- Result result = new Result();
- for (int i = sippers.size() - 1; i >= 0; i--) {
- final BatterySipper app = sippers.get(i);
- if (app.drainType == BatterySipper.DrainType.APP) {
- calculateApp(app, app.uidObj, statsType, result);
- }
- }
- }
-
- private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType, Result result) {
- final long consumptionUC = u.getCpuMeasuredBatteryConsumptionUC();
- final int powerModel = getPowerModel(consumptionUC);
- calculatePowerAndDuration(u, powerModel, consumptionUC, statsType, result);
-
- app.cpuPowerMah = result.powerMah;
- app.cpuTimeMs = result.durationMs;
- app.cpuFgTimeMs = result.durationFgMs;
- app.packageWithHighestDrain = result.packageWithHighestDrain;
- }
-
private void calculatePowerAndDuration(BatteryStats.Uid u,
@BatteryConsumer.PowerModel int powerModel, long consumptionUC, int statsType,
Result result) {
@@ -258,7 +233,7 @@
if (DEBUG && (durationMs != 0 || powerMah != 0)) {
Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + durationMs + " ms power="
- + formatCharge(powerMah));
+ + BatteryStats.formatCharge(powerMah));
}
// Keep track of the package with highest drain.
@@ -325,7 +300,7 @@
if (DEBUG) {
Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster
+ " clusterTimeMs=" + cpuClusterTimes[cluster]
- + " power=" + formatCharge(power));
+ + " power=" + BatteryStats.formatCharge(power));
}
}
} else {
diff --git a/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java b/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java
index 4cb7ef1..cbbb526 100644
--- a/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java
+++ b/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java
@@ -102,16 +102,6 @@
return newTotalPowerMah;
}
- @Override
- protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
- long rawUptimeUs, int statsType) {
- updateCustomMeasuredPowerMah(app, u.getCustomConsumerMeasuredBatteryConsumptionUC());
- }
-
- private void updateCustomMeasuredPowerMah(BatterySipper sipper, long[] measuredChargeUC) {
- sipper.customMeasuredPowerMah = calculateMeasuredEnergiesMah(measuredChargeUC);
- }
-
private double[] calculateMeasuredEnergiesMah(long[] measuredChargeUC) {
if (measuredChargeUC == null) {
return null;
diff --git a/core/java/com/android/internal/os/FlashlightPowerCalculator.java b/core/java/com/android/internal/os/FlashlightPowerCalculator.java
index 7d3f962..ce3e7b9 100644
--- a/core/java/com/android/internal/os/FlashlightPowerCalculator.java
+++ b/core/java/com/android/internal/os/FlashlightPowerCalculator.java
@@ -66,14 +66,4 @@
app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT, durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT, powerMah);
}
-
- @Override
- protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
- long rawUptimeUs, int statsType) {
- final long durationMs = mPowerEstimator.calculateDuration(u.getFlashlightTurnedOnTimer(),
- rawRealtimeUs, statsType);
- final double powerMah = mPowerEstimator.calculatePower(durationMs);
- app.flashlightTimeMs = durationMs;
- app.flashlightPowerMah = powerMah;
- }
}
diff --git a/core/java/com/android/internal/os/GnssPowerCalculator.java b/core/java/com/android/internal/os/GnssPowerCalculator.java
index a836ddb..0f78306 100644
--- a/core/java/com/android/internal/os/GnssPowerCalculator.java
+++ b/core/java/com/android/internal/os/GnssPowerCalculator.java
@@ -21,11 +21,8 @@
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.UidBatteryConsumer;
-import android.os.UserHandle;
import android.util.SparseArray;
-import java.util.List;
-
/**
* Estimates the amount of power consumed by the GNSS (e.g. GPS).
*/
@@ -100,41 +97,6 @@
return powerMah;
}
- @Override
- public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
- double averageGnssPowerMa = getAverageGnssPower(batteryStats, rawRealtimeUs, statsType);
- for (int i = sippers.size() - 1; i >= 0; i--) {
- final BatterySipper app = sippers.get(i);
- if (app.drainType == BatterySipper.DrainType.APP) {
- final long consumptionUC =
- app.uidObj.getGnssMeasuredBatteryConsumptionUC();
- final int powerModel = getPowerModel(consumptionUC);
- calculateApp(app, app.uidObj, powerModel, rawRealtimeUs, averageGnssPowerMa,
- consumptionUC);
- }
- }
- }
-
- private void calculateApp(BatterySipper app, BatteryStats.Uid u,
- @BatteryConsumer.PowerModel int powerModel, long rawRealtimeUs,
- double averageGnssPowerMa, long measuredChargeUC) {
- final long durationMs = computeDuration(u, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
-
- final double powerMah;
- switch (powerModel) {
- case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY:
- powerMah = uCtoMah(measuredChargeUC);
- break;
- case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
- default:
- powerMah = computePower(durationMs, averageGnssPowerMa);
- }
-
- app.gpsTimeMs = durationMs;
- app.gpsPowerMah = powerMah;
- }
-
private long computeDuration(BatteryStats.Uid u, long rawRealtimeUs, int statsType) {
final SparseArray<? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();
final BatteryStats.Uid.Sensor sensor = sensorStats.get(BatteryStats.Uid.Sensor.GPS);
diff --git a/core/java/com/android/internal/os/IdlePowerCalculator.java b/core/java/com/android/internal/os/IdlePowerCalculator.java
index 9491b3b..5b2052e 100644
--- a/core/java/com/android/internal/os/IdlePowerCalculator.java
+++ b/core/java/com/android/internal/os/IdlePowerCalculator.java
@@ -20,11 +20,7 @@
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
-import android.os.UserHandle;
import android.util.Log;
-import android.util.SparseArray;
-
-import java.util.List;
/**
* Estimates the amount of power consumed when the device is idle.
@@ -64,20 +60,6 @@
}
}
- @Override
- public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
- calculatePowerAndDuration(batteryStats, rawRealtimeUs, rawUptimeUs, statsType);
-
- if (mPowerMah != 0) {
- BatterySipper bs = new BatterySipper(BatterySipper.DrainType.IDLE, null, 0);
- bs.usagePowerMah = mPowerMah;
- bs.usageTimeMs = mDurationMs;
- bs.sumPower();
- sippers.add(bs);
- }
- }
-
/**
* Calculates the baseline power usage for the device when it is in suspend and idle.
* The device is drawing POWER_CPU_SUSPEND power at its lowest power state.
@@ -97,9 +79,9 @@
mPowerMah = suspendPowerMah + idlePowerMah;
if (DEBUG && mPowerMah != 0) {
Log.d(TAG, "Suspend: time=" + (batteryRealtimeUs / 1000)
- + " power=" + formatCharge(suspendPowerMah));
+ + " power=" + BatteryStats.formatCharge(suspendPowerMah));
Log.d(TAG, "Idle: time=" + (batteryUptimeUs / 1000)
- + " power=" + formatCharge(idlePowerMah));
+ + " power=" + BatteryStats.formatCharge(idlePowerMah));
}
mDurationMs = batteryRealtimeUs / 1000;
}
diff --git a/core/java/com/android/internal/os/MediaPowerCalculator.java b/core/java/com/android/internal/os/MediaPowerCalculator.java
deleted file mode 100644
index fff96da..0000000
--- a/core/java/com/android/internal/os/MediaPowerCalculator.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2018 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.internal.os;
-
-import android.os.BatteryConsumer;
-import android.os.BatteryStats;
-
-/**
- * A {@link PowerCalculator} to calculate power consumed by audio and video hardware.
- *
- * Also see {@link PowerProfile#POWER_AUDIO} and {@link PowerProfile#POWER_VIDEO}.
- */
-public class MediaPowerCalculator extends PowerCalculator {
- private static final int MS_IN_HR = 1000 * 60 * 60;
- private final double mAudioAveragePowerMa;
- private final double mVideoAveragePowerMa;
-
- public MediaPowerCalculator(PowerProfile profile) {
- mAudioAveragePowerMa = profile.getAveragePower(PowerProfile.POWER_AUDIO);
- mVideoAveragePowerMa = profile.getAveragePower(PowerProfile.POWER_VIDEO);
- }
-
- @Override
- public boolean isPowerComponentSupported(@BatteryConsumer.PowerComponent int powerComponent) {
- return powerComponent == BatteryConsumer.POWER_COMPONENT_VIDEO
- || powerComponent == BatteryConsumer.POWER_COMPONENT_AUDIO;
- }
-
- @Override
- protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
- long rawUptimeUs, int statsType) {
- // Calculate audio power usage, an estimate based on the average power routed to different
- // components like speaker, bluetooth, usb-c, earphone, etc.
- final BatteryStats.Timer audioTimer = u.getAudioTurnedOnTimer();
- if (audioTimer == null) {
- app.audioTimeMs = 0;
- app.audioPowerMah = 0;
- } else {
- final long totalTime = audioTimer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000;
- app.audioTimeMs = totalTime;
- app.audioPowerMah = (totalTime * mAudioAveragePowerMa) / MS_IN_HR;
- }
-
- // Calculate video power usage.
- final BatteryStats.Timer videoTimer = u.getVideoTurnedOnTimer();
- if (videoTimer == null) {
- app.videoTimeMs = 0;
- app.videoPowerMah = 0;
- } else {
- final long totalTime = videoTimer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000;
- app.videoTimeMs = totalTime;
- app.videoPowerMah = (totalTime * mVideoAveragePowerMa) / MS_IN_HR;
- }
- }
-}
diff --git a/core/java/com/android/internal/os/MemoryPowerCalculator.java b/core/java/com/android/internal/os/MemoryPowerCalculator.java
index 0440a58..0d3040c 100644
--- a/core/java/com/android/internal/os/MemoryPowerCalculator.java
+++ b/core/java/com/android/internal/os/MemoryPowerCalculator.java
@@ -4,11 +4,7 @@
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
-import android.os.UserHandle;
import android.util.LongSparseArray;
-import android.util.SparseArray;
-
-import java.util.List;
public class MemoryPowerCalculator extends PowerCalculator {
public static final String TAG = "MemoryPowerCalculator";
@@ -41,20 +37,6 @@
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_MEMORY, powerMah);
}
- @Override
- public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
- final long durationMs = calculateDuration(batteryStats, rawRealtimeUs, statsType);
- final double powerMah = calculatePower(batteryStats, rawRealtimeUs, statsType);
- BatterySipper memory = new BatterySipper(BatterySipper.DrainType.MEMORY, null, 0);
- memory.usageTimeMs = durationMs;
- memory.usagePowerMah = powerMah;
- memory.sumPower();
- if (memory.totalPowerMah > 0) {
- sippers.add(memory);
- }
- }
-
private long calculateDuration(BatteryStats batteryStats, long rawRealtimeUs, int statsType) {
long usageDurationMs = 0;
LongSparseArray<? extends BatteryStats.Timer> timers = batteryStats.getKernelMemoryStats();
diff --git a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
index 3a50b73..f4624de 100644
--- a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
+++ b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
@@ -20,13 +20,10 @@
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.UidBatteryConsumer;
-import android.os.UserHandle;
import android.telephony.CellSignalStrength;
import android.util.Log;
import android.util.SparseArray;
-import java.util.List;
-
public class MobileRadioPowerCalculator extends PowerCalculator {
private static final String TAG = "MobRadioPowerCalculator";
private static final boolean DEBUG = PowerCalculator.DEBUG;
@@ -170,65 +167,6 @@
}
}
- @Override
- public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
- PowerAndDuration total = new PowerAndDuration();
- for (int i = sippers.size() - 1; i >= 0; i--) {
- final BatterySipper app = sippers.get(i);
- if (app.drainType == BatterySipper.DrainType.APP) {
- final BatteryStats.Uid u = app.uidObj;
- calculateApp(app, u, statsType, total);
- }
- }
-
- BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0);
- final long consumptionUC = batteryStats.getMobileRadioMeasuredBatteryConsumptionUC();
- final int powerModel = getPowerModel(consumptionUC);
- calculateRemaining(total, powerModel, batteryStats, rawRealtimeUs, consumptionUC);
- if (total.remainingPowerMah != 0) {
- if (total.signalDurationMs != 0) {
- radio.noCoveragePercent =
- total.noCoverageDurationMs * 100.0 / total.signalDurationMs;
- }
- radio.mobileActive = total.durationMs;
- radio.mobileActiveCount = batteryStats.getMobileRadioActiveUnknownCount(statsType);
- radio.mobileRadioPowerMah = total.remainingPowerMah;
- radio.sumPower();
- }
- if (radio.totalPowerMah > 0) {
- sippers.add(radio);
- }
- }
-
- private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType,
- PowerAndDuration total) {
- app.mobileActive = calculateDuration(u, statsType);
-
- final long consumptionUC = u.getMobileRadioMeasuredBatteryConsumptionUC();
- final int powerModel = getPowerModel(consumptionUC);
- app.mobileRadioPowerMah = calculatePower(u, powerModel, app.mobileActive, consumptionUC);
- total.totalAppDurationMs += app.mobileActive;
-
- // Add cost of mobile traffic.
- app.mobileRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA,
- statsType);
- app.mobileTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA,
- statsType);
- app.mobileActiveCount = u.getMobileRadioActiveCount(statsType);
- app.mobileRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_RX_DATA,
- statsType);
- app.mobileTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_TX_DATA,
- statsType);
-
- if (DEBUG && app.mobileRadioPowerMah != 0) {
- Log.d(TAG, "UID " + u.getUid() + ": mobile packets "
- + (app.mobileRxPackets + app.mobileTxPackets)
- + " active time " + app.mobileActive
- + " power=" + formatCharge(app.mobileRadioPowerMah));
- }
- }
-
private long calculateDuration(BatteryStats.Uid u, int statsType) {
return u.getMobileRadioActiveTime(statsType) / 1000;
}
@@ -262,7 +200,7 @@
final double p = calcIdlePowerAtSignalStrengthMah(strengthTimeMs, i);
if (DEBUG && p != 0) {
Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power="
- + formatCharge(p));
+ + BatteryStats.formatCharge(p));
}
powerMah += p;
}
@@ -281,8 +219,8 @@
if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
final double p = calcScanTimePowerMah(scanningTimeMs);
if (DEBUG && p != 0) {
- Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs + " power=" + formatCharge(
- p));
+ Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs
+ + " power=" + BatteryStats.formatCharge(p));
}
powerMah += p;
diff --git a/core/java/com/android/internal/os/PhonePowerCalculator.java b/core/java/com/android/internal/os/PhonePowerCalculator.java
index 7310314..cb893de 100644
--- a/core/java/com/android/internal/os/PhonePowerCalculator.java
+++ b/core/java/com/android/internal/os/PhonePowerCalculator.java
@@ -20,10 +20,6 @@
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
-import android.os.UserHandle;
-import android.util.SparseArray;
-
-import java.util.List;
/**
* Estimates power consumed by telephony.
@@ -54,18 +50,4 @@
.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_PHONE, phoneOnTimeMs);
}
}
-
- @Override
- public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
- final long phoneOnTimeMs = batteryStats.getPhoneOnTime(rawRealtimeUs, statsType) / 1000;
- final double phoneOnPower = mPowerEstimator.calculatePower(phoneOnTimeMs);
- if (phoneOnPower != 0) {
- BatterySipper bs = new BatterySipper(BatterySipper.DrainType.PHONE, null, 0);
- bs.usagePowerMah = phoneOnPower;
- bs.usageTimeMs = phoneOnTimeMs;
- bs.sumPower();
- sippers.add(bs);
- }
- }
}
diff --git a/core/java/com/android/internal/os/PowerCalculator.java b/core/java/com/android/internal/os/PowerCalculator.java
index 28c0f5a..ec785b8 100644
--- a/core/java/com/android/internal/os/PowerCalculator.java
+++ b/core/java/com/android/internal/os/PowerCalculator.java
@@ -21,12 +21,9 @@
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.UidBatteryConsumer;
-import android.os.UserHandle;
import android.util.SparseArray;
import java.io.PrintWriter;
-import java.util.List;
-import java.util.Locale;
/**
* Calculates power use of a device subsystem for an app.
@@ -43,34 +40,6 @@
public abstract boolean isPowerComponentSupported(
@BatteryConsumer.PowerComponent int powerComponent);
-
- /**
- * Attributes the total amount of power used by this subsystem to various consumers such
- * as apps.
- *
- * @param sippers A list of battery sippers that contains battery attribution data.
- * The calculator may modify the list.
- * @param batteryStats The recorded battery stats.
- * @param rawRealtimeUs The raw system realtime in microseconds.
- * @param rawUptimeUs The raw system uptime in microseconds.
- * @param statsType The type of stats. As of {@link android.os.Build.VERSION_CODES#Q}, this
- * can only be {@link BatteryStats#STATS_SINCE_CHARGED}, since
- * {@link BatteryStats#STATS_CURRENT} and
- * {@link BatteryStats#STATS_SINCE_UNPLUGGED} are deprecated.
- * @param asUsers An array of users for which the attribution is requested. It may
- * contain {@link UserHandle#USER_ALL} to indicate that the attribution
- * should be performed for all users.
- */
- public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
- for (int i = sippers.size() - 1; i >= 0; i--) {
- final BatterySipper app = sippers.get(i);
- if (app.drainType == BatterySipper.DrainType.APP) {
- calculateApp(app, app.uidObj, rawRealtimeUs, rawUptimeUs, statsType);
- }
- }
- }
-
/**
* Attributes the total amount of power used by this subsystem to various consumers such
* as apps.
@@ -95,21 +64,6 @@
/**
* Calculate the amount of power an app used for this subsystem.
- * @param app The BatterySipper that represents the power use of an app.
- * @param u The recorded stats for the app.
- * @param rawRealtimeUs The raw system realtime in microseconds.
- * @param rawUptimeUs The raw system uptime in microseconds.
- * @param statsType The type of stats. As of {@link android.os.Build.VERSION_CODES#Q}, this can
- * only be {@link BatteryStats#STATS_SINCE_CHARGED}, since
- * {@link BatteryStats#STATS_CURRENT} and
- * {@link BatteryStats#STATS_SINCE_UNPLUGGED} are deprecated.
- */
- protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
- long rawUptimeUs, int statsType) {
- }
-
- /**
- * Calculate the amount of power an app used for this subsystem.
* @param app The UidBatteryConsumer.Builder that represents the power use of an app.
* @param u The recorded stats for the app.
* @param rawRealtimeUs The raw system realtime in microseconds.
@@ -145,38 +99,7 @@
* Prints formatted amount of power in milli-amp-hours.
*/
public static void printPowerMah(PrintWriter pw, double powerMah) {
- pw.print(formatCharge(powerMah));
- }
-
- /**
- * Converts charge in mAh to string.
- */
- public static String formatCharge(double power) {
- if (power == 0) return "0";
-
- final String format;
- if (power < .00001) {
- format = "%.8f";
- } else if (power < .0001) {
- format = "%.7f";
- } else if (power < .001) {
- format = "%.6f";
- } else if (power < .01) {
- format = "%.5f";
- } else if (power < .1) {
- format = "%.4f";
- } else if (power < 1) {
- format = "%.3f";
- } else if (power < 10) {
- format = "%.2f";
- } else if (power < 100) {
- format = "%.1f";
- } else {
- format = "%.0f";
- }
-
- // Use English locale because this is never used in UI (only in checkin and dump).
- return String.format(Locale.ENGLISH, format, power);
+ pw.print(BatteryStats.formatCharge(powerMah));
}
static double uCtoMah(long chargeUC) {
diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java
index 110b6b6..67d3d6e 100644
--- a/core/java/com/android/internal/os/ScreenPowerCalculator.java
+++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java
@@ -24,7 +24,6 @@
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.UidBatteryConsumer;
-import android.os.UserHandle;
import android.text.format.DateUtils;
import android.util.Slog;
import android.util.SparseArray;
@@ -32,8 +31,6 @@
import com.android.internal.annotations.VisibleForTesting;
-import java.util.List;
-
/**
* Estimates power consumed by the screen(s)
*/
@@ -125,48 +122,6 @@
}
/**
- * Screen power is the additional power the screen takes while the device is running.
- */
- @Override
- public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
- final PowerAndDuration totalPowerAndDuration = new PowerAndDuration();
- final long consumptionUC = batteryStats.getScreenOnMeasuredBatteryConsumptionUC();
- final int powerModel = getPowerModel(consumptionUC);
- calculateTotalDurationAndPower(totalPowerAndDuration, powerModel, batteryStats,
- rawRealtimeUs, statsType, consumptionUC);
- if (totalPowerAndDuration.powerMah == 0) {
- return;
- }
-
- // First deal with the SCREEN BatterySipper (since we need this for smearing over apps).
- final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.SCREEN, null, 0);
- bs.usagePowerMah = totalPowerAndDuration.powerMah;
- bs.usageTimeMs = totalPowerAndDuration.durationMs;
- bs.sumPower();
- sippers.add(bs);
-
- // Now deal with each app's BatterySipper. The results are stored in the screenPowerMah
- // field, which is considered smeared, but the method depends on the data source.
- switch (powerModel) {
- case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY:
- final PowerAndDuration appPowerAndDuration = new PowerAndDuration();
- for (int i = sippers.size() - 1; i >= 0; i--) {
- final BatterySipper app = sippers.get(i);
- if (app.drainType == BatterySipper.DrainType.APP) {
- calculateAppUsingMeasuredEnergy(appPowerAndDuration, app.uidObj,
- rawRealtimeUs);
- app.screenPowerMah = appPowerAndDuration.powerMah;
- }
- }
- break;
- case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
- default:
- smearScreenBatterySipper(sippers, bs, rawRealtimeUs);
- }
- }
-
- /**
* Stores duration and power information in totalPowerAndDuration.
*/
private void calculateTotalDurationAndPower(PowerAndDuration totalPowerAndDuration,
@@ -219,7 +174,7 @@
brightnessTime) * (bin + 0.5f) / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
if (DEBUG && binPowerMah != 0) {
Slog.d(TAG, "Screen bin #" + bin + ": time=" + brightnessTime
- + " power=" + formatCharge(binPowerMah));
+ + " power=" + BatteryStats.formatCharge(binPowerMah));
}
power += binPowerMah;
}
@@ -228,37 +183,8 @@
}
/**
- * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity
- * time, and store this in the {@link BatterySipper#screenPowerMah} field.
- */
- @VisibleForTesting
- public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper,
- long rawRealtimeUs) {
- long totalActivityTimeMs = 0;
- final SparseLongArray activityTimeArray = new SparseLongArray();
- for (int i = sippers.size() - 1; i >= 0; i--) {
- final BatteryStats.Uid uid = sippers.get(i).uidObj;
- if (uid != null) {
- final long timeMs = getProcessForegroundTimeMs(uid, rawRealtimeUs);
- activityTimeArray.put(uid.getUid(), timeMs);
- totalActivityTimeMs += timeMs;
- }
- }
-
- if (screenSipper != null && totalActivityTimeMs >= MIN_ACTIVE_TIME_FOR_SMEARING) {
- final double totalScreenPowerMah = screenSipper.totalPowerMah;
- for (int i = sippers.size() - 1; i >= 0; i--) {
- final BatterySipper sipper = sippers.get(i);
- sipper.screenPowerMah = totalScreenPowerMah
- * activityTimeArray.get(sipper.getUid(), 0)
- / totalActivityTimeMs;
- }
- }
- }
-
- /**
- * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity
- * time, and store this in the {@link BatterySipper#screenPowerMah} field.
+ * Smear the screen on power usage among {@code UidBatteryConsumers}, based on ratio of
+ * foreground activity time.
*/
private void smearScreenBatteryDrain(
SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders,
diff --git a/core/java/com/android/internal/os/SensorPowerCalculator.java b/core/java/com/android/internal/os/SensorPowerCalculator.java
index 495a6d9..4a9c91d 100644
--- a/core/java/com/android/internal/os/SensorPowerCalculator.java
+++ b/core/java/com/android/internal/os/SensorPowerCalculator.java
@@ -72,12 +72,6 @@
return powerMah;
}
- @Override
- protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
- long rawUptimeUs, int statsType) {
- app.sensorPowerMah = calculatePowerMah(u, rawRealtimeUs, statsType);
- }
-
private long calculateDuration(BatteryStats.Uid u, long rawRealtimeUs, int statsType) {
long durationMs = 0;
final SparseArray<? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();
diff --git a/core/java/com/android/internal/os/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
index d7872ba..3a3df87 100644
--- a/core/java/com/android/internal/os/SystemServicePowerCalculator.java
+++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
@@ -22,12 +22,9 @@
import android.os.BatteryUsageStatsQuery;
import android.os.Process;
import android.os.UidBatteryConsumer;
-import android.os.UserHandle;
import android.util.Log;
import android.util.SparseArray;
-import java.util.List;
-
/**
* Estimates the amount of power consumed by the System Server handling requests from
* a given app.
@@ -121,55 +118,6 @@
systemServicePowerMah);
}
- @Override
- public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, int statsType,
- SparseArray<UserHandle> asUsers) {
- final BatteryStats.Uid systemUid = batteryStats.getUidStats().get(Process.SYSTEM_UID);
- if (systemUid == null) {
- return;
- }
-
- final long consumptionUC = systemUid.getCpuMeasuredBatteryConsumptionUC();
- double systemServicePowerMah;
- if (getPowerModel(consumptionUC) == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
- systemServicePowerMah = calculatePowerUsingMeasuredConsumption(batteryStats,
- systemUid, consumptionUC);
- } else {
- systemServicePowerMah = calculatePowerUsingPowerProfile(batteryStats);
- }
-
- BatterySipper systemServerSipper = null;
- for (int i = sippers.size() - 1; i >= 0; i--) {
- final BatterySipper app = sippers.get(i);
- if (app.drainType == BatterySipper.DrainType.APP) {
- if (app.getUid() == Process.SYSTEM_UID) {
- systemServerSipper = app;
- break;
- }
- }
- }
-
- if (systemServerSipper != null) {
- systemServicePowerMah = Math.min(systemServicePowerMah, systemServerSipper.sumPower());
-
- // The system server power needs to be adjusted because part of it got
- // distributed to applications
- systemServerSipper.powerReattributedToOtherSippersMah = -systemServicePowerMah;
- }
-
- for (int i = sippers.size() - 1; i >= 0; i--) {
- final BatterySipper app = sippers.get(i);
- if (app.drainType == BatterySipper.DrainType.APP) {
- if (app != systemServerSipper) {
- final BatteryStats.Uid uid = app.uidObj;
- app.systemServiceCpuPowerMah =
- systemServicePowerMah * uid.getProportionalSystemServiceUsage();
- }
- }
- }
- }
-
private double calculatePowerUsingMeasuredConsumption(BatteryStats batteryStats,
BatteryStats.Uid systemUid, long consumptionUC) {
// Use the PowerProfile based model to estimate the ratio between the power consumed
diff --git a/core/java/com/android/internal/os/UserPowerCalculator.java b/core/java/com/android/internal/os/UserPowerCalculator.java
index b590bf7..22cff6e 100644
--- a/core/java/com/android/internal/os/UserPowerCalculator.java
+++ b/core/java/com/android/internal/os/UserPowerCalculator.java
@@ -27,8 +27,6 @@
import com.android.internal.util.ArrayUtils;
-import java.util.List;
-
/**
* Computes power consumed by Users
*/
@@ -65,40 +63,4 @@
}
}
}
-
- @Override
- public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
- final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
- if (forAllUsers) {
- return;
- }
-
- SparseArray<BatterySipper> userSippers = new SparseArray<>();
-
- for (int i = sippers.size() - 1; i >= 0; i--) {
- BatterySipper sipper = sippers.get(i);
- final int uid = sipper.getUid();
- final int userId = UserHandle.getUserId(uid);
- if (asUsers.get(userId) == null
- && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {
- // We are told to just report this user's apps as one accumulated entry.
- BatterySipper userSipper = userSippers.get(userId);
- if (userSipper == null) {
- userSipper = new BatterySipper(BatterySipper.DrainType.USER, null, 0);
- userSipper.userId = userId;
- userSippers.put(userId, userSipper);
- }
- userSipper.add(sipper);
- sipper.isAggregated = true;
- }
- }
-
- for (int i = 0; i < userSippers.size(); i++) {
- BatterySipper sipper = userSippers.valueAt(i);
- if (sipper.sumPower() > 0) {
- sippers.add(sipper);
- }
- }
- }
}
diff --git a/core/java/com/android/internal/os/WakelockPowerCalculator.java b/core/java/com/android/internal/os/WakelockPowerCalculator.java
index bceb209..0251e1c 100644
--- a/core/java/com/android/internal/os/WakelockPowerCalculator.java
+++ b/core/java/com/android/internal/os/WakelockPowerCalculator.java
@@ -21,13 +21,10 @@
import android.os.BatteryUsageStatsQuery;
import android.os.Process;
import android.os.UidBatteryConsumer;
-import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
-import java.util.List;
-
public class WakelockPowerCalculator extends PowerCalculator {
private static final String TAG = "WakelockPowerCalculator";
private static final boolean DEBUG = PowerCalculator.DEBUG;
@@ -105,42 +102,6 @@
appPowerMah);
}
- @Override
- public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
- final PowerAndDuration result = new PowerAndDuration();
- BatterySipper osSipper = null;
- double osPowerMah = 0;
- long osDurationMs = 0;
- long totalAppDurationMs = 0;
- for (int i = sippers.size() - 1; i >= 0; i--) {
- final BatterySipper app = sippers.get(i);
- if (app.drainType == BatterySipper.DrainType.APP) {
- calculateApp(result, app.uidObj, rawRealtimeUs, statsType);
- app.wakeLockTimeMs = result.durationMs;
- app.wakeLockPowerMah = result.powerMah;
- totalAppDurationMs += result.durationMs;
-
- if (app.getUid() == Process.ROOT_UID) {
- osSipper = app;
- osPowerMah = result.powerMah;
- osDurationMs = result.durationMs;
- }
- }
- }
-
- // The device has probably been awake for longer than the screen on
- // time and application wake lock time would account for. Assign
- // this remainder to the OS, if possible.
- if (osSipper != null) {
- calculateRemaining(result, batteryStats, rawRealtimeUs, rawUptimeUs, statsType,
- osPowerMah, osDurationMs, totalAppDurationMs);
- osSipper.wakeLockTimeMs = result.durationMs;
- osSipper.wakeLockPowerMah = result.powerMah;
- osSipper.sumPower();
- }
- }
-
private void calculateApp(PowerAndDuration result, BatteryStats.Uid u, long rawRealtimeUs,
int statsType) {
long wakeLockTimeUs = 0;
@@ -163,7 +124,7 @@
result.powerMah = mPowerEstimator.calculatePower(result.durationMs);
if (DEBUG && result.powerMah != 0) {
Log.d(TAG, "UID " + u.getUid() + ": wake " + result.durationMs
- + " power=" + formatCharge(result.powerMah));
+ + " power=" + BatteryStats.formatCharge(result.powerMah));
}
}
@@ -175,7 +136,8 @@
if (wakeTimeMillis > 0) {
final double power = mPowerEstimator.calculatePower(wakeTimeMillis);
if (DEBUG) {
- Log.d(TAG, "OS wakeLockTime " + wakeTimeMillis + " power " + formatCharge(power));
+ Log.d(TAG, "OS wakeLockTime " + wakeTimeMillis
+ + " power " + BatteryStats.formatCharge(power));
}
result.durationMs = osDurationMs + wakeTimeMillis;
result.powerMah = osPowerMah + power;
diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java
index ad291a4..8c3fb86 100644
--- a/core/java/com/android/internal/os/WifiPowerCalculator.java
+++ b/core/java/com/android/internal/os/WifiPowerCalculator.java
@@ -19,14 +19,11 @@
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
-import android.os.Process;
import android.os.UidBatteryConsumer;
-import android.os.UserHandle;
import android.util.Log;
import android.util.SparseArray;
import java.util.Arrays;
-import java.util.List;
/**
* WiFi power calculator for when BatteryStats supports energy reporting
@@ -156,62 +153,6 @@
totalAppPowerMah, powerModel);
}
- /**
- * We do per-app blaming of WiFi activity. If energy info is reported from the controller,
- * then only the WiFi process gets blamed here since we normalize power calculations and
- * assign all the power drain to apps. If energy info is not reported, we attribute the
- * difference between total running time of WiFi for all apps and the actual running time
- * of WiFi to the WiFi subsystem.
- */
- @Override
- public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
-
- final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0);
-
- long totalAppDurationMs = 0;
- double totalAppPowerMah = 0;
- final PowerDurationAndTraffic powerDurationAndTraffic = new PowerDurationAndTraffic();
- for (int i = sippers.size() - 1; i >= 0; i--) {
- final BatterySipper app = sippers.get(i);
- if (app.drainType == BatterySipper.DrainType.APP) {
- final long consumptionUC =
- app.uidObj.getWifiMeasuredBatteryConsumptionUC();
- final int powerModel = getPowerModel(consumptionUC);
- calculateApp(powerDurationAndTraffic, app.uidObj, powerModel, rawRealtimeUs,
- statsType, batteryStats.hasWifiActivityReporting(), consumptionUC);
-
- totalAppDurationMs += powerDurationAndTraffic.durationMs;
- totalAppPowerMah += powerDurationAndTraffic.powerMah;
-
- app.wifiPowerMah = powerDurationAndTraffic.powerMah;
- app.wifiRunningTimeMs = powerDurationAndTraffic.durationMs;
- app.wifiRxBytes = powerDurationAndTraffic.wifiRxBytes;
- app.wifiRxPackets = powerDurationAndTraffic.wifiRxPackets;
- app.wifiTxBytes = powerDurationAndTraffic.wifiTxBytes;
- app.wifiTxPackets = powerDurationAndTraffic.wifiTxPackets;
- if (app.getUid() == Process.WIFI_UID) {
- if (DEBUG) Log.d(TAG, "WiFi adding sipper " + app + ": cpu=" + app.cpuTimeMs);
- app.isAggregated = true;
- bs.add(app);
- }
- }
- }
-
- final long consumptionUC = batteryStats.getWifiMeasuredBatteryConsumptionUC();
- final int powerModel = getPowerModel(consumptionUC);
- calculateRemaining(powerDurationAndTraffic, powerModel, batteryStats, rawRealtimeUs,
- statsType, batteryStats.hasWifiActivityReporting(), totalAppDurationMs,
- totalAppPowerMah, consumptionUC);
-
- bs.wifiRunningTimeMs += powerDurationAndTraffic.durationMs;
- bs.wifiPowerMah += powerDurationAndTraffic.powerMah;
-
- if (bs.sumPower() > 0) {
- sippers.add(bs);
- }
- }
-
private void calculateApp(PowerDurationAndTraffic powerDurationAndTraffic,
BatteryStats.Uid u, @BatteryConsumer.PowerModel int powerModel,
long rawRealtimeUs, int statsType, boolean hasWifiActivityReporting,
@@ -251,7 +192,7 @@
if (DEBUG && powerDurationAndTraffic.powerMah != 0) {
Log.d(TAG, "UID " + u.getUid() + ": idle=" + idleTime + "ms rx=" + rxTime
- + "ms tx=" + txTime + "ms power=" + formatCharge(
+ + "ms tx=" + txTime + "ms power=" + BatteryStats.formatCharge(
powerDurationAndTraffic.powerMah));
}
@@ -306,7 +247,7 @@
}
if (DEBUG && powerDurationAndTraffic.powerMah != 0) {
- Log.d(TAG, "UID " + u.getUid() + ": power=" + formatCharge(
+ Log.d(TAG, "UID " + u.getUid() + ": power=" + BatteryStats.formatCharge(
powerDurationAndTraffic.powerMah));
}
}
@@ -353,7 +294,8 @@
powerDurationAndTraffic.powerMah = Math.max(0, totalPowerMah - totalAppPowerMah);
if (DEBUG) {
- Log.d(TAG, "left over WiFi power: " + formatCharge(powerDurationAndTraffic.powerMah));
+ Log.d(TAG, "left over WiFi power: " + BatteryStats.formatCharge(
+ powerDurationAndTraffic.powerMah));
}
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index f258f84..3d24aa2d 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -736,10 +736,13 @@
Zygote.applyInvokeWithSystemProperty(parsedArgs);
if (Zygote.nativeSupportsMemoryTagging()) {
- /* The system server has ASYNC MTE by default, in order to allow
- * system services to specify their own MTE level later, as you
- * can't re-enable MTE once it's disabled. */
- String mode = SystemProperties.get("arm64.memtag.process.system_server", "async");
+ String mode = SystemProperties.get("arm64.memtag.process.system_server", "");
+ if (mode.isEmpty()) {
+ /* The system server has ASYNC MTE by default, in order to allow
+ * system services to specify their own MTE level later, as you
+ * can't re-enable MTE once it's disabled. */
+ mode = SystemProperties.get("persist.arm64.memtag.default", "async");
+ }
if (mode.equals("async")) {
parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_ASYNC;
} else if (mode.equals("sync")) {
diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index e72afdd..8430c08 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -24,7 +24,9 @@
*/
oneway interface IInputMethodClient {
void onBindMethod(in InputBindResult res);
+ void onBindAccessibilityService(in InputBindResult res, int id);
void onUnbindMethod(int sequence, int unbindReason);
+ void onUnbindAccessibilityService(int sequence, int id);
void setActive(boolean active, boolean fullscreen, boolean reportToImeController);
void scheduleStartInputIfNecessary(boolean fullscreen);
void reportFullscreenMode(boolean fullscreen);
diff --git a/core/java/com/android/internal/view/IInputSessionWithIdCallback.aidl b/core/java/com/android/internal/view/IInputSessionWithIdCallback.aidl
new file mode 100644
index 0000000..8fbdefe
--- /dev/null
+++ b/core/java/com/android/internal/view/IInputSessionWithIdCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2013 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.internal.view;
+
+ import com.android.internal.view.IInputMethodSession;
+
+/**
+ * Helper interface for IInputMethod to allow the input method to notify the client when a new
+ * session has been created.
+ */
+oneway interface IInputSessionWithIdCallback {
+ void sessionCreated(IInputMethodSession session, int id);
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index c9dc6b6..1e11c6d 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -26,6 +26,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.PropertyInvalidatedCache;
import android.app.admin.DevicePolicyManager;
import android.app.admin.PasswordMetrics;
import android.app.trust.IStrongAuthTracker;
@@ -941,17 +942,53 @@
}
/**
+ * Retrieve the credential type of a user.
+ */
+ private final PropertyInvalidatedCache.QueryHandler<Integer, Integer> mCredentialTypeQuery =
+ new PropertyInvalidatedCache.QueryHandler<>() {
+ @Override
+ public Integer apply(Integer userHandle) {
+ try {
+ return getLockSettings().getCredentialType(userHandle);
+ } catch (RemoteException re) {
+ Log.e(TAG, "failed to get credential type", re);
+ return CREDENTIAL_TYPE_NONE;
+ }
+ }
+ @Override
+ public boolean shouldBypassCache(Integer userHandle) {
+ return userHandle == USER_FRP;
+ }
+ };
+
+ /**
+ * The API that is cached.
+ */
+ private final static String CREDENTIAL_TYPE_API = "getCredentialType";
+
+ /**
+ * Cache the credential type of a user.
+ */
+ private final PropertyInvalidatedCache<Integer, Integer> mCredentialTypeCache =
+ new PropertyInvalidatedCache<>(4, PropertyInvalidatedCache.MODULE_SYSTEM,
+ CREDENTIAL_TYPE_API, CREDENTIAL_TYPE_API, mCredentialTypeQuery);
+
+ /**
+ * Invalidate the credential cache
+ * @hide
+ */
+ public final static void invalidateCredentialTypeCache() {
+ PropertyInvalidatedCache.invalidateCache(PropertyInvalidatedCache.MODULE_SYSTEM,
+ CREDENTIAL_TYPE_API);
+ }
+
+ /**
* Returns the credential type of the user, can be one of {@link #CREDENTIAL_TYPE_NONE},
* {@link #CREDENTIAL_TYPE_PATTERN}, {@link #CREDENTIAL_TYPE_PIN} and
* {@link #CREDENTIAL_TYPE_PASSWORD}
*/
public @CredentialType int getCredentialTypeForUser(int userHandle) {
- try {
- return getLockSettings().getCredentialType(userHandle);
- } catch (RemoteException re) {
- Log.e(TAG, "failed to get credential type", re);
- return CREDENTIAL_TYPE_NONE;
- }
+ return mCredentialTypeCache.query(userHandle);
}
/**
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index 55f1369..4f13a9c 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -30,6 +30,11 @@
namespace android {
+static struct {
+ jclass clazz;
+ jmethodID ctor;
+} gTransactionClassInfo;
+
static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName) {
ScopedUtfChars name(env, jName);
sp<BLASTBufferQueue> queue = new BLASTBufferQueue(name.c_str());
@@ -86,6 +91,14 @@
return queue->isSameSurfaceControl(reinterpret_cast<SurfaceControl*>(surfaceControl));
}
+static jobject nativeGatherPendingTransactions(JNIEnv* env, jclass clazz, jlong ptr,
+ jlong frameNum) {
+ sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+ SurfaceComposerClient::Transaction* transaction = queue->gatherPendingTransactions(frameNum);
+ return env->NewObject(gTransactionClassInfo.clazz, gTransactionClassInfo.ctor,
+ reinterpret_cast<jlong>(transaction));
+}
+
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
// clang-format off
@@ -98,6 +111,7 @@
{"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum},
{"nativeApplyPendingTransactions", "(JJ)V", (void*)nativeApplyPendingTransactions},
{"nativeIsSameSurfaceControl", "(JJ)Z", (void*)nativeIsSameSurfaceControl},
+ {"nativeGatherPendingTransactions", "(JJ)Landroid/view/SurfaceControl$Transaction;", (void*)nativeGatherPendingTransactions}
// clang-format on
};
@@ -105,6 +119,11 @@
int res = jniRegisterNativeMethods(env, "android/graphics/BLASTBufferQueue",
gMethods, NELEM(gMethods));
LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
+
+ jclass transactionClazz = FindClassOrDie(env, "android/view/SurfaceControl$Transaction");
+ gTransactionClassInfo.clazz = MakeGlobalRefOrDie(env, transactionClazz);
+ gTransactionClassInfo.ctor =
+ GetMethodIDOrDie(env, gTransactionClassInfo.clazz, "<init>", "(J)V");
return 0;
}
diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp
index 3af55fe..d2d7213 100644
--- a/core/jni/android_os_HwRemoteBinder.cpp
+++ b/core/jni/android_os_HwRemoteBinder.cpp
@@ -81,27 +81,37 @@
void binderDied(const wp<hardware::IBinder>& who)
{
- if (mObject != NULL) {
- JNIEnv* env = javavm_to_jnienv(mVM);
+ JNIEnv* env = javavm_to_jnienv(mVM);
- env->CallStaticVoidMethod(gProxyOffsets.proxy_class, gProxyOffsets.sendDeathNotice, mObject, mCookie);
+ // Serialize with our containing HwBinderDeathRecipientList so that we can't
+ // delete the global ref on object while the list is being iterated.
+ sp<HwBinderDeathRecipientList> list = mList.promote();
+ if (list == nullptr) return;
+
+ jobject object;
+ {
+ AutoMutex _l(list->lock());
+
+ // this function now owns the global ref - to the rest of the code, it looks like
+ // this binder already died, but we won't actually delete the reference until
+ // the Java code has processed the death
+ object = mObject;
+
+ // Demote from strong ref to weak for after binderDied() has been delivered,
+ // to allow the DeathRecipient and BinderProxy to be GC'd if no longer needed.
+ mObjectWeak = env->NewWeakGlobalRef(mObject);
+ mObject = nullptr;
+ }
+
+ if (object != nullptr) {
+ env->CallStaticVoidMethod(gProxyOffsets.proxy_class, gProxyOffsets.sendDeathNotice,
+ object, mCookie);
if (env->ExceptionCheck()) {
ALOGE("Uncaught exception returned from death notification.");
env->ExceptionClear();
}
- // Serialize with our containing HwBinderDeathRecipientList so that we can't
- // delete the global ref on mObject while the list is being iterated.
- sp<HwBinderDeathRecipientList> list = mList.promote();
- if (list != NULL) {
- AutoMutex _l(list->lock());
-
- // Demote from strong ref to weak after binderDied() has been delivered,
- // to allow the DeathRecipient and BinderProxy to be GC'd if no longer needed.
- mObjectWeak = env->NewWeakGlobalRef(mObject);
- env->DeleteGlobalRef(mObject);
- mObject = NULL;
- }
+ env->DeleteGlobalRef(object);
}
}
@@ -115,7 +125,7 @@
}
}
- bool matches(jobject obj) {
+ bool matchesLocked(jobject obj) {
bool result;
JNIEnv* env = javavm_to_jnienv(mVM);
@@ -129,7 +139,7 @@
return result;
}
- void warnIfStillLive() {
+ void warnIfStillLiveLocked() {
if (mObject != NULL) {
// Okay, something is wrong -- we have a hard reference to a live death
// recipient on the VM side, but the list is being torn down.
@@ -176,7 +186,7 @@
AutoMutex _l(mLock);
for (const sp<HwBinderDeathRecipient>& deathRecipient : mList) {
- deathRecipient->warnIfStillLive();
+ deathRecipient->warnIfStillLiveLocked();
}
}
@@ -201,7 +211,7 @@
AutoMutex _l(mLock);
for(auto iter = mList.rbegin(); iter != mList.rend(); iter++) {
- if ((*iter)->matches(recipient)) {
+ if ((*iter)->matchesLocked(recipient)) {
return (*iter);
}
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 2736ba63..f29de56 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -199,6 +199,8 @@
<protected-broadcast
android:name="android.bluetooth.headsetclient.profile.action.LAST_VTAG" />
<protected-broadcast
+ android:name="android.bluetooth.headsetclient.profile.action.NETWORK_SERVICE_STATE_CHANGED" />
+ <protected-broadcast
android:name="android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
android:name="android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED" />
@@ -721,6 +723,7 @@
<protected-broadcast android:name="android.app.action.DEVICE_POLICY_RESOURCE_UPDATED" />
<protected-broadcast android:name="android.intent.action.SHOW_FOREGROUND_SERVICE_MANAGER" />
<protected-broadcast android:name="android.service.autofill.action.DELAYED_FILL" />
+ <protected-broadcast android:name="android.app.action.PROVISIONING_COMPLETED" />
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
@@ -1895,7 +1898,7 @@
android:label="@string/permlab_changeWifiState"
android:protectionLevel="normal" />
- <!-- @SystemApi @hide Allows applications to enable/disable wifi auto join. This permission
+ <!-- Allows applications to enable/disable wifi auto join. This permission
is used to let OEMs grant their trusted app access to a subset of privileged wifi APIs
to improve wifi performance.
<p>Not for use by third-party applications. -->
@@ -1933,7 +1936,7 @@
<permission android:name="android.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi @hide Allows an application to modify any wifi configuration, even if created
+ <!-- Allows an application to modify any wifi configuration, even if created
by another application. Once reconfigured the original creator cannot make any further
modifications.
<p>Not for use by third-party applications. -->
@@ -6616,14 +6619,6 @@
android:exported="false">
</activity>
- <activity android:name="com.android.server.logcat.LogAccessConfirmationActivity"
- android:theme="@style/Theme.Dialog.Confirmation"
- android:excludeFromRecents="true"
- android:process=":ui"
- android:label="@string/log_access_confirmation_title"
- android:exported="false">
- </activity>
-
<activity android:name="com.android.server.notification.NASLearnMoreActivity"
android:theme="@style/Theme.Dialog.Confirmation"
android:excludeFromRecents="true"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8df5871..090b1c52 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3910,6 +3910,8 @@
<!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_MULTI_FINGER_GESTURES}. -->
<flag name="flagRequestMultiFingerGestures" value="0x00001000" />
<flag name="flagSendMotionEvents" value="0x0004000" />
+ <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_INPUT_METHOD_EDITOR}. -->
+ <flag name="flagInputMethodEditor" value="0x0008000" />
</attr>
<!-- Component name of an activity that allows the user to modify
the settings for this service. This setting cannot be changed at runtime. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6a86b1c..775527d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4082,6 +4082,16 @@
-->
<string name="config_defaultAmbientContextDetectionService" translatable="false"></string>
+ <!-- Component name that accepts ACTION_SEND intents for requesting ambient context consent. -->
+ <string translatable="false" name="config_defaultAmbientContextConsentComponent"></string>
+
+ <!-- Intent extra key for the caller's package name while requesting ambient context consent.
+ -->
+ <string translatable="false" name="config_ambientContextPackageNameExtraKey"></string>
+
+ <!-- Intent extra key for the event code int array while requesting ambient context consent. -->
+ <string translatable="false" name="config_ambientContextEventArrayExtraKey"></string>
+
<!-- The component name for the system-wide captions service.
This service must be trusted, as it controls part of the UI of the volume bar.
Example: "com.android.captions/.SystemCaptionsService"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 70ca8fc..6297ed9 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5719,20 +5719,6 @@
<!-- Title for the harmful app warning dialog. [CHAR LIMIT=40] -->
<string name="harmful_app_warning_title">Harmful app detected</string>
- <!-- Title for the log access confirmation dialog. [CHAR LIMIT=40] -->
- <string name="log_access_confirmation_title">System log access request</string>
- <!-- Label for the allow button on the log access confirmation dialog. [CHAR LIMIT=20] -->
- <string name="log_access_confirmation_allow">Only this time</string>
- <!-- Label for the deny button on the log access confirmation dialog. [CHAR LIMIT=20] -->
- <string name="log_access_confirmation_deny">Don\u2019t allow</string>
-
- <!-- Content for the log access confirmation dialog. [CHAR LIMIT=NONE]-->
- <string name="log_access_confirmation_body"><xliff:g id="log_access_app_name" example="Example App">%s</xliff:g> requests system logs for functional debugging.
- These logs might contain information that apps and services on your device have written.</string>
-
- <!-- Privacy notice do not show [CHAR LIMIT=20] -->
- <string name="log_access_do_not_show_again">Don\u2019t show again</string>
-
<!-- Text describing a permission request for one app to show another app's
slices [CHAR LIMIT=NONE] -->
<string name="slices_permission_request"><xliff:g id="app" example="Example App">%1$s</xliff:g> wants to show <xliff:g id="app_2" example="Other Example App">%2$s</xliff:g> slices</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a79eec2..d731180 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3868,11 +3868,6 @@
<java-symbol type="string" name="harmful_app_warning_title" />
<java-symbol type="layout" name="harmful_app_warning_dialog" />
- <java-symbol type="string" name="log_access_confirmation_allow" />
- <java-symbol type="string" name="log_access_confirmation_deny" />
- <java-symbol type="string" name="log_access_confirmation_title" />
- <java-symbol type="string" name="log_access_confirmation_body" />
-
<java-symbol type="string" name="config_defaultAssistantAccessComponent" />
<java-symbol type="string" name="slices_permission_request" />
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
deleted file mode 100644
index 260b65a..0000000
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright (C) 2017 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.internal.os;
-
-import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.BatteryStats;
-import android.os.Process;
-import android.text.format.DateUtils;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import junit.framework.TestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class BatteryStatsHelperTest extends TestCase {
- private static final long TIME_FOREGROUND_ACTIVITY_ZERO = 0;
- private static final long TIME_FOREGROUND_ACTIVITY = 100 * DateUtils.MINUTE_IN_MILLIS * 1000;
- private static final long TIME_STATE_FOREGROUND_MS = 10 * DateUtils.MINUTE_IN_MILLIS;
- private static final long TIME_STATE_FOREGROUND_US = TIME_STATE_FOREGROUND_MS * 1000;
-
- private static final int UID = 123456;
- private static final double BATTERY_SCREEN_USAGE = 300;
- private static final double BATTERY_SYSTEM_USAGE = 600;
- private static final double BATTERY_WIFI_USAGE = 200;
- private static final double BATTERY_IDLE_USAGE = 600;
- private static final double BATTERY_BLUETOOTH_USAGE = 300;
- private static final double BATTERY_OVERACCOUNTED_USAGE = 500;
- private static final double BATTERY_UNACCOUNTED_USAGE = 700;
- private static final double BATTERY_APP_USAGE = 100;
- private static final double TOTAL_BATTERY_USAGE = 1000;
- private static final double PRECISION = 0.001;
-
- @Mock
- private BatteryStats.Uid mUid;
- @Mock
- private BatterySipper mWifiBatterySipper;
- @Mock
- private BatterySipper mBluetoothBatterySipper;
- @Mock
- private BatterySipper mIdleBatterySipper;
- @Mock
- private BatterySipper mNormalBatterySipper;
- @Mock
- private BatterySipper mScreenBatterySipper;
- @Mock
- private BatterySipper mOvercountedBatterySipper;
- @Mock
- private BatterySipper mUnaccountedBatterySipper;
- @Mock
- private BatterySipper mSystemBatterySipper;
- @Mock
- private BatterySipper mCellBatterySipper;
- @Mock
- private PackageManager mPackageManager;
-
- private BatteryStatsHelper mBatteryStatsHelper;
- private Context mContext;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
- mNormalBatterySipper.totalPowerMah = TOTAL_BATTERY_USAGE;
- when(mNormalBatterySipper.getUid()).thenReturn(UID);
- mNormalBatterySipper.uidObj = mUid;
-
-
- mScreenBatterySipper.drainType = BatterySipper.DrainType.SCREEN;
- mScreenBatterySipper.totalPowerMah = BATTERY_SCREEN_USAGE;
-
- mSystemBatterySipper.drainType = BatterySipper.DrainType.APP;
- mSystemBatterySipper.totalPowerMah = BATTERY_SYSTEM_USAGE;
- mSystemBatterySipper.uidObj = mUid;
- when(mSystemBatterySipper.getUid()).thenReturn(Process.SYSTEM_UID);
-
- mOvercountedBatterySipper.drainType = BatterySipper.DrainType.OVERCOUNTED;
- mOvercountedBatterySipper.totalPowerMah = BATTERY_OVERACCOUNTED_USAGE;
-
- mUnaccountedBatterySipper.drainType = BatterySipper.DrainType.UNACCOUNTED;
- mUnaccountedBatterySipper.totalPowerMah = BATTERY_UNACCOUNTED_USAGE;
-
- mWifiBatterySipper.drainType = BatterySipper.DrainType.WIFI;
- mWifiBatterySipper.totalPowerMah = BATTERY_WIFI_USAGE;
-
- mBluetoothBatterySipper.drainType = BatterySipper.DrainType.BLUETOOTH;
- mBluetoothBatterySipper.totalPowerMah = BATTERY_BLUETOOTH_USAGE;
-
- mIdleBatterySipper.drainType = BatterySipper.DrainType.IDLE;
- mIdleBatterySipper.totalPowerMah = BATTERY_IDLE_USAGE;
-
- mContext = InstrumentationRegistry.getContext();
- mBatteryStatsHelper = spy(new BatteryStatsHelper(mContext));
- mBatteryStatsHelper.setPackageManager(mPackageManager);
- }
-
- @Test
- public void testShouldHideSipper_TypeUnAccounted_ReturnTrue() {
- mNormalBatterySipper.drainType = BatterySipper.DrainType.UNACCOUNTED;
- assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue();
- }
-
- @Test
- public void testShouldHideSipper_TypeOverAccounted_ReturnTrue() {
- mNormalBatterySipper.drainType = BatterySipper.DrainType.OVERCOUNTED;
- assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue();
- }
-
- @Test
- public void testShouldHideSipper_TypeIdle_ReturnTrue() {
- mNormalBatterySipper.drainType = BatterySipper.DrainType.IDLE;
- assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue();
- }
-
- @Test
- public void testShouldHideSipper_TypeCell_ReturnTrue() {
- mNormalBatterySipper.drainType = BatterySipper.DrainType.CELL;
- assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue();
- }
-
- @Test
- public void testShouldHideSipper_TypeScreen_ReturnTrue() {
- mNormalBatterySipper.drainType = BatterySipper.DrainType.SCREEN;
- assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue();
- }
-
- @Test
- public void testShouldHideSipper_TypeSystem_ReturnTrue() {
- mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
- when(mNormalBatterySipper.getUid()).thenReturn(Process.ROOT_UID);
- assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isTrue();
- }
-
- @Test
- public void testShouldHideSipper_UidNormal_ReturnFalse() {
- mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
- assertThat(mBatteryStatsHelper.shouldHideSipper(mNormalBatterySipper)).isFalse();
- }
-
- @Test
- public void testRemoveHiddenBatterySippers_ContainsHiddenSippers_RemoveAndReturnValue() {
- final List<BatterySipper> sippers = new ArrayList<>();
- sippers.add(mNormalBatterySipper);
- sippers.add(mScreenBatterySipper);
- sippers.add(mSystemBatterySipper);
- sippers.add(mOvercountedBatterySipper);
- sippers.add(mUnaccountedBatterySipper);
- sippers.add(mWifiBatterySipper);
- sippers.add(mBluetoothBatterySipper);
- sippers.add(mIdleBatterySipper);
- doReturn(true).when(mBatteryStatsHelper).isTypeSystem(mSystemBatterySipper);
-
- final double totalUsage = mBatteryStatsHelper.removeHiddenBatterySippers(sippers);
-
- assertThat(mNormalBatterySipper.shouldHide).isFalse();
- assertThat(mScreenBatterySipper.shouldHide).isTrue();
- assertThat(mSystemBatterySipper.shouldHide).isTrue();
- assertThat(mOvercountedBatterySipper.shouldHide).isTrue();
- assertThat(mUnaccountedBatterySipper.shouldHide).isTrue();
- assertThat(totalUsage).isWithin(PRECISION).of(BATTERY_SYSTEM_USAGE);
- }
-
- @Test
- public void testSmearScreenBatterySipper() {
- final ScreenPowerCalculator spc = spy(ScreenPowerCalculator.class);
- final BatterySipper sipperNull = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY_ZERO,
- BATTERY_APP_USAGE, 0 /* uid */, true /* isUidNull */, spc);
- final BatterySipper sipperBg = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY_ZERO,
- BATTERY_APP_USAGE, 1 /* uid */, false /* isUidNull */, spc);
- final BatterySipper sipperFg = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY,
- BATTERY_APP_USAGE, 2 /* uid */, false /* isUidNull */, spc);
-
- final List<BatterySipper> sippers = new ArrayList<>();
- sippers.add(sipperNull);
- sippers.add(sipperBg);
- sippers.add(sipperFg);
-
- spc.smearScreenBatterySipper(sippers, mScreenBatterySipper, 0);
-
- assertThat(sipperNull.screenPowerMah).isWithin(PRECISION).of(0);
- assertThat(sipperBg.screenPowerMah).isWithin(PRECISION).of(0);
- assertThat(sipperFg.screenPowerMah).isWithin(PRECISION).of(BATTERY_SCREEN_USAGE);
- }
-
- @Test
- public void testIsTypeSystem_systemPackage_returnTrue() {
- final String[] systemPackages = {"com.android.system"};
- mBatteryStatsHelper.setSystemPackageArray(systemPackages);
- doReturn(UID).when(mNormalBatterySipper).getUid();
- doReturn(systemPackages).when(mPackageManager).getPackagesForUid(UID);
-
- assertThat(mBatteryStatsHelper.isTypeSystem(mNormalBatterySipper)).isTrue();
- }
-
- @Test
- public void testIsTypeService_servicePackage_returnTrue() {
- final String[] servicePackages = {"com.android.service"};
- mBatteryStatsHelper.setServicePackageArray(servicePackages);
- doReturn(UID).when(mNormalBatterySipper).getUid();
- doReturn(servicePackages).when(mPackageManager).getPackagesForUid(UID);
-
- assertThat(mBatteryStatsHelper.isTypeService(mNormalBatterySipper)).isTrue();
- }
-
- @Test
- public void testGetProcessForegroundTimeMs_largerActivityTime_returnMinTime() {
- final ScreenPowerCalculator spc = spy(ScreenPowerCalculator.class);
- doReturn(TIME_STATE_FOREGROUND_US + 500).when(spc)
- .getForegroundActivityTotalTimeUs(eq(mUid), anyLong());
- doReturn(TIME_STATE_FOREGROUND_US).when(mUid).getProcessStateTime(eq(PROCESS_STATE_TOP),
- anyLong(), anyInt());
-
- final long time = spc.getProcessForegroundTimeMs(mUid, 1000);
-
- assertThat(time).isEqualTo(TIME_STATE_FOREGROUND_MS);
- }
-
- private BatterySipper createTestSmearBatterySipper(long activityTime, double totalPowerMah,
- int uidCode, boolean isUidNull, ScreenPowerCalculator spc) {
- final BatterySipper sipper = mock(BatterySipper.class);
- sipper.drainType = BatterySipper.DrainType.APP;
- sipper.totalPowerMah = totalPowerMah;
- doReturn(uidCode).when(sipper).getUid();
- if (!isUidNull) {
- final BatteryStats.Uid uid = mock(BatteryStats.Uid.class, RETURNS_DEEP_STUBS);
- doReturn(activityTime).when(spc).getProcessForegroundTimeMs(eq(uid), anyLong());
- doReturn(uidCode).when(uid).getUid();
- sipper.uidObj = uid;
- }
-
- return sipper;
- }
-
-}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
index be8045d..d9b98a5 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
@@ -41,6 +41,7 @@
import android.bluetooth.UidTraffic;
import android.os.BatteryStats;
import android.os.BluetoothBatteryStats;
+import android.os.Parcel;
import android.os.WakeLockStats;
import android.os.WorkSource;
import android.util.SparseArray;
@@ -583,11 +584,42 @@
mBatteryStatsImpl.noteBluetoothScanStoppedFromSourceLocked(ws, true, 9000, 9000);
mBatteryStatsImpl.noteBluetoothScanResultsFromSourceLocked(ws, 42, 9000, 9000);
- BluetoothActivityEnergyInfo info = new BluetoothActivityEnergyInfo(1000,
- BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 9000, 8000, 12000, 0);
- info.setUidTraffic(ImmutableList.of(
- new UidTraffic(10042, 3000, 4000),
- new UidTraffic(10043, 5000, 8000)));
+
+
+ final Parcel uidTrafficParcel1 = Parcel.obtain();
+ final Parcel uidTrafficParcel2 = Parcel.obtain();
+
+ uidTrafficParcel1.writeInt(10042);
+ uidTrafficParcel1.writeLong(3000);
+ uidTrafficParcel1.writeLong(4000);
+ uidTrafficParcel1.setDataPosition(0);
+ uidTrafficParcel2.writeInt(10043);
+ uidTrafficParcel2.writeLong(5000);
+ uidTrafficParcel2.writeLong(8000);
+ uidTrafficParcel2.setDataPosition(0);
+
+ List<UidTraffic> uidTrafficList = ImmutableList.of(
+ UidTraffic.CREATOR.createFromParcel(uidTrafficParcel1),
+ UidTraffic.CREATOR.createFromParcel(uidTrafficParcel2));
+
+ final Parcel btActivityEnergyInfoParcel = Parcel.obtain();
+ btActivityEnergyInfoParcel.writeLong(1000);
+ btActivityEnergyInfoParcel.writeInt(
+ BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE);
+ btActivityEnergyInfoParcel.writeLong(9000);
+ btActivityEnergyInfoParcel.writeLong(8000);
+ btActivityEnergyInfoParcel.writeLong(12000);
+ btActivityEnergyInfoParcel.writeLong(0);
+ btActivityEnergyInfoParcel.writeTypedList(uidTrafficList);
+ btActivityEnergyInfoParcel.setDataPosition(0);
+
+ BluetoothActivityEnergyInfo info = BluetoothActivityEnergyInfo.CREATOR
+ .createFromParcel(btActivityEnergyInfoParcel);
+
+ uidTrafficParcel1.recycle();
+ uidTrafficParcel2.recycle();
+ btActivityEnergyInfoParcel.recycle();
+
mBatteryStatsImpl.updateBluetoothStateLocked(info, -1, 1000, 1000);
BluetoothBatteryStats stats =
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 92c2d43..ace39fb 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -30,7 +30,6 @@
BatteryStatsCounterTest.class,
BatteryStatsDualTimerTest.class,
BatteryStatsDurationTimerTest.class,
- BatteryStatsHelperTest.class,
BatteryStatsHistoryIteratorTest.class,
BatteryStatsHistoryTest.class,
BatteryStatsImplTest.class,
@@ -69,7 +68,6 @@
LongSamplingCounterTest.class,
LongSamplingCounterArrayTest.class,
MobileRadioPowerCalculatorTest.class,
- PowerCalculatorTest.class,
PowerProfileTest.class,
ScreenPowerCalculatorTest.class,
SensorPowerCalculatorTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
index ed035e5..448f666 100644
--- a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
@@ -24,6 +24,7 @@
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStatsQuery;
+import android.os.Parcel;
import android.os.Process;
import android.os.UidBatteryConsumer;
import android.os.WorkSource;
@@ -37,6 +38,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.List;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
@SuppressWarnings("GuardedBy")
@@ -90,11 +93,13 @@
uid.setProcessStateForTest(
BatteryStats.Uid.PROCESS_STATE_FOREGROUND, 1000);
- BluetoothActivityEnergyInfo info1 = new BluetoothActivityEnergyInfo(2000,
- BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 1000, 2000, 3000, 4000);
- info1.setUidTraffic(ImmutableList.of(
- new UidTraffic(Process.BLUETOOTH_UID, 1000, 2000),
- new UidTraffic(APP_UID, 3000, 4000)));
+
+ List<UidTraffic> trafficList1 = ImmutableList.of(
+ createUidTraffic(Process.BLUETOOTH_UID, 1000, 2000),
+ createUidTraffic(APP_UID, 3000, 4000));
+ BluetoothActivityEnergyInfo info1 = createBtEnergyInfo(2000,
+ BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 1000, 2000, 3000, 4000,
+ trafficList1);
batteryStats.updateBluetoothStateLocked(info1,
0/*1_000_000*/, 2000, 2000);
@@ -102,11 +107,14 @@
uid.setProcessStateForTest(
BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 3000);
- BluetoothActivityEnergyInfo info2 = new BluetoothActivityEnergyInfo(4000,
- BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 5000, 6000, 7000, 8000);
- info2.setUidTraffic(ImmutableList.of(
- new UidTraffic(Process.BLUETOOTH_UID, 5000, 6000),
- new UidTraffic(APP_UID, 7000, 8000)));
+
+ List<UidTraffic> trafficList2 = ImmutableList.of(
+ createUidTraffic(Process.BLUETOOTH_UID, 5000, 6000),
+ createUidTraffic(APP_UID, 7000, 8000));
+ BluetoothActivityEnergyInfo info2 = createBtEnergyInfo(4000,
+ BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 5000, 6000, 7000, 8000,
+ trafficList2);
+
batteryStats.updateBluetoothStateLocked(info2,
0 /*5_000_000 */, 4000, 4000);
@@ -202,11 +210,14 @@
uid.setProcessStateForTest(
BatteryStats.Uid.PROCESS_STATE_FOREGROUND, 1000);
- BluetoothActivityEnergyInfo info1 = new BluetoothActivityEnergyInfo(2000,
- BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 1000, 2000, 3000, 4000);
- info1.setUidTraffic(ImmutableList.of(
- new UidTraffic(Process.BLUETOOTH_UID, 1000, 2000),
- new UidTraffic(APP_UID, 3000, 4000)));
+
+ List<UidTraffic> trafficList1 = ImmutableList.of(
+ createUidTraffic(Process.BLUETOOTH_UID, 1000, 2000),
+ createUidTraffic(APP_UID, 3000, 4000));
+ BluetoothActivityEnergyInfo info1 = createBtEnergyInfo(2000,
+ BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 1000, 2000, 3000, 4000,
+ trafficList1);
+
batteryStats.updateBluetoothStateLocked(info1,
1_000_000, 2000, 2000);
@@ -214,11 +225,13 @@
uid.setProcessStateForTest(
BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 3000);
- BluetoothActivityEnergyInfo info2 = new BluetoothActivityEnergyInfo(4000,
- BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 5000, 6000, 7000, 8000);
- info2.setUidTraffic(ImmutableList.of(
- new UidTraffic(Process.BLUETOOTH_UID, 5000, 6000),
- new UidTraffic(APP_UID, 7000, 8000)));
+ List<UidTraffic> trafficList2 = ImmutableList.of(
+ createUidTraffic(Process.BLUETOOTH_UID, 5000, 6000),
+ createUidTraffic(APP_UID, 7000, 8000));
+ BluetoothActivityEnergyInfo info2 = createBtEnergyInfo(4000,
+ BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 5000, 6000, 7000, 8000,
+ trafficList2);
+
batteryStats.updateBluetoothStateLocked(info2,
5_000_000, 4000, 4000);
@@ -280,12 +293,15 @@
}
private void setupBluetoothEnergyInfo(long reportedEnergyUc, long consumedEnergyUc) {
- final BluetoothActivityEnergyInfo info = new BluetoothActivityEnergyInfo(1000,
+ List<UidTraffic> trafficList = ImmutableList.of(
+ createUidTraffic(Process.BLUETOOTH_UID, 1000, 2000),
+ createUidTraffic(APP_UID, 3000, 4000));
+
+
+ final BluetoothActivityEnergyInfo info = createBtEnergyInfo(1000,
BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 7000, 5000, 0,
- reportedEnergyUc);
- info.setUidTraffic(ImmutableList.of(
- new UidTraffic(Process.BLUETOOTH_UID, 1000, 2000),
- new UidTraffic(APP_UID, 3000, 4000)));
+ reportedEnergyUc, trafficList);
+
mStatsRule.getBatteryStats().updateBluetoothStateLocked(info,
consumedEnergyUc, 1000, 1000);
}
@@ -304,4 +320,34 @@
BatteryConsumer.POWER_COMPONENT_BLUETOOTH);
assertThat(usageDurationMillis).isEqualTo(durationMs);
}
+
+ private UidTraffic createUidTraffic(int uid, long traffic1, long traffic2) {
+ final Parcel uidTrafficParcel = Parcel.obtain();
+ uidTrafficParcel.writeInt(uid);
+ uidTrafficParcel.writeLong(traffic1);
+ uidTrafficParcel.writeLong(traffic2);
+ uidTrafficParcel.setDataPosition(0);
+
+ UidTraffic traffic = UidTraffic.CREATOR.createFromParcel(uidTrafficParcel);
+ uidTrafficParcel.recycle();
+ return traffic;
+ }
+
+ private BluetoothActivityEnergyInfo createBtEnergyInfo(long timestamp, int stackState,
+ long txTime, long rxTime, long idleTime, long energyUsed, List<UidTraffic> traffic) {
+ final Parcel btActivityEnergyInfoParcel = Parcel.obtain();
+ btActivityEnergyInfoParcel.writeLong(timestamp);
+ btActivityEnergyInfoParcel.writeInt(stackState);
+ btActivityEnergyInfoParcel.writeLong(txTime);
+ btActivityEnergyInfoParcel.writeLong(rxTime);
+ btActivityEnergyInfoParcel.writeLong(idleTime);
+ btActivityEnergyInfoParcel.writeLong(energyUsed);
+ btActivityEnergyInfoParcel.writeTypedList(traffic);
+ btActivityEnergyInfoParcel.setDataPosition(0);
+
+ BluetoothActivityEnergyInfo info = BluetoothActivityEnergyInfo.CREATOR
+ .createFromParcel(btActivityEnergyInfoParcel);
+ btActivityEnergyInfoParcel.recycle();
+ return info;
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java
deleted file mode 100644
index 4bd5724..0000000
--- a/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2018 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.internal.os;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.os.BatteryStats;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import junit.framework.TestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class PowerCalculatorTest extends TestCase {
- private static final long US_IN_HR = 1000L * 1000L * 60L * 60L;
-
- @Mock
- private PowerProfile mPowerProfile;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- }
-
- /** Test {@link MediaPowerCalculator#calculateApp} */
- @Test
- public void testMediaPowerCalculator() {
- when(mPowerProfile.getAveragePower(PowerProfile.POWER_AUDIO)).thenReturn(12.0);
- when(mPowerProfile.getAveragePower(PowerProfile.POWER_VIDEO)).thenReturn(25.0);
-
- BatteryStats.Uid u = mock(BatteryStats.Uid.class);
- BatteryStats.Timer audioTimer = mock(BatteryStats.Timer.class);
- when(u.getAudioTurnedOnTimer()).thenReturn(audioTimer);
- when(audioTimer.getTotalTimeLocked(2L * US_IN_HR, 0)).thenReturn(2L * US_IN_HR);
- BatteryStats.Timer videoTimer = mock(BatteryStats.Timer.class);
- when(u.getVideoTurnedOnTimer()).thenReturn(videoTimer);
- when(videoTimer.getTotalTimeLocked(2L * US_IN_HR, 0)).thenReturn(1L * US_IN_HR);
-
- MediaPowerCalculator mediaPowerCalculator = new MediaPowerCalculator(mPowerProfile);
- BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0);
-
- mediaPowerCalculator.calculate(List.of(app), null, 2L * US_IN_HR, 2L * US_IN_HR, 0, null);
- assertEquals(49.0, app.sumPower());
- }
-}
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index a9e730d..2678c79d 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -39,6 +39,8 @@
private static native long nativeGetLastAcquiredFrameNum(long ptr);
private static native void nativeApplyPendingTransactions(long ptr, long frameNumber);
private static native boolean nativeIsSameSurfaceControl(long ptr, long surfaceControlPtr);
+ private static native SurfaceControl.Transaction nativeGatherPendingTransactions(long ptr,
+ long frameNumber);
/** Create a new connection with the surface flinger. */
public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,
@@ -159,4 +161,17 @@
public boolean isSameSurfaceControl(SurfaceControl sc) {
return nativeIsSameSurfaceControl(mNativeObject, sc.mNativeObject);
}
+
+ /**
+ * Get any transactions that were passed to {@link #mergeWithNextTransaction} with the
+ * specified frameNumber. This is intended to ensure transactions don't get stuck as pending
+ * if the specified frameNumber is never drawn.
+ *
+ * @param frameNumber The frameNumber used to determine which transactions to apply.
+ * @return a Transaction that contains the merge of all the transactions that were sent to
+ * mergeWithNextTransaction
+ */
+ public SurfaceControl.Transaction gatherPendingTransactions(long frameNumber) {
+ return nativeGatherPendingTransactions(mNativeObject, frameNumber);
+ }
}
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index ee41148..ee0d647 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -290,7 +290,7 @@
* context at a time.
*
* @param texName The name of the OpenGL ES texture that will be created. This texture name
- * must be unusued in the OpenGL ES context that is current on the calling thread.
+ * must be unused in the OpenGL ES context that is current on the calling thread.
*/
public void attachToGLContext(int texName) {
int err = nativeAttachToGLContext(texName);
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 13c1498..7d57734 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -60,21 +60,21 @@
import java.util.concurrent.CopyOnWriteArrayList;
/**
- * MediaRouter allows applications to control the routing of media channels
+ * This API is not recommended for new applications. Use the
+ * <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/mediarouter/media/package-summary.html">Media Router
+ * Library</a> for consistent behavior across all devices.
+ *
+ * <p>MediaRouter allows applications to control the routing of media channels
* and streams from the current device to external speakers and destination devices.
*
* <p>A MediaRouter is retrieved through {@link Context#getSystemService(String)
* Context.getSystemService()} of a {@link Context#MEDIA_ROUTER_SERVICE
* Context.MEDIA_ROUTER_SERVICE}.
*
- * <p>The media router API is not thread-safe; all interactions with it must be
- * done from the main thread of the process.</p>
- *
- * <p>
- * We recommend using {@link android.media.MediaRouter2} APIs for new applications.
- * </p>
+ * <p>This API is not thread-safe; all interactions with it must be done from the main thread of the
+ * process.
*/
-//TODO: Link androidx.media2.MediaRouter when we are ready.
@SystemService(Context.MEDIA_ROUTER_SERVICE)
public class MediaRouter {
private static final String TAG = "MediaRouter";
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index f8c47e8..311476c 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -52,13 +52,13 @@
import java.util.stream.Collectors;
/**
- * This API is not generally intended for third party application developers.
- * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- <a href="{@docRoot}reference/androidx/mediarouter/media/package-summary.html">Media Router
+ * This API is not generally intended for third party application developers. Use the
+ * <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/mediarouter/media/package-summary.html">Media Router
* Library</a> for consistent behavior across all devices.
*
- * Media Router 2 allows applications to control the routing of media channels
- * and streams from the current device to remote speakers and devices.
+ * <p>MediaRouter2 allows applications to control the routing of media channels and streams from
+ * the current device to remote speakers and devices.
*/
// TODO(b/157873330): Add method names at the beginning of log messages. (e.g. selectRoute)
// Not only MediaRouter2, but also to service / manager / provider.
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 955ae3c..37050df 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -407,7 +407,7 @@
/**
* Get the session owner's package name.
*
- * @return The package name of of the session owner.
+ * @return The package name of the session owner.
*/
public String getPackageName() {
if (mPackageName == null) {
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 73e96a2..96809bd 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -1877,6 +1877,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
public int getClientPriority(@TvInputService.PriorityHintUseCaseType int useCase,
@Nullable String sessionId) {
return getClientPriorityInternal(useCase, sessionId);
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 4d6739e..31e1817 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -623,7 +623,7 @@
Image_setBufferItem(env, image, buffer);
env->SetLongField(image, gSurfaceImageClassInfo.mTimestamp,
static_cast<jlong>(buffer->mTimestamp));
- env->SetLongField(image, gSurfaceImageClassInfo.mDataSpace,
+ env->SetIntField(image, gSurfaceImageClassInfo.mDataSpace,
static_cast<jint>(buffer->mDataSpace));
auto transform = buffer->mTransform;
if (buffer->mTransformToDisplayInverse) {
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index 0f88afb..6c6fccb 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -901,7 +901,7 @@
env->SetIntField(thiz, gSurfaceImageClassInfo.mNativeFenceFd, reinterpret_cast<jint>(fenceFd));
- env->SetLongField(thiz, gSurfaceImageClassInfo.mDataSpace, dataSpace);
+ env->SetIntField(thiz, gSurfaceImageClassInfo.mDataSpace, dataSpace);
}
static void Image_unlockIfLocked(JNIEnv* env, jobject thiz) {
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index b7beb6e..3009a36 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -205,6 +205,7 @@
ASensorManager_destroyEventQueue;
ASensorManager_getDefaultSensor;
ASensorManager_getDefaultSensorEx; # introduced=21
+ ASensorManager_getDynamicSensorList; # introduced=33
ASensorManager_getInstance;
ASensorManager_getInstanceForPackage; # introduced=26
ASensorManager_getSensorList;
diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp
index 63082fd..968de34 100644
--- a/native/android/sensor.cpp
+++ b/native/android/sensor.cpp
@@ -83,6 +83,16 @@
return c;
}
+ssize_t ASensorManager_getDynamicSensorList(ASensorManager* manager, ASensorList* list) {
+ RETURN_IF_MANAGER_IS_NULL(android::BAD_VALUE);
+ Sensor const* const* l;
+ ssize_t c = static_cast<SensorManager*>(manager)->getDynamicSensorList(&l);
+ if (list) {
+ *list = reinterpret_cast<ASensorList>(l);
+ }
+ return c;
+}
+
ASensor const* ASensorManager_getDefaultSensor(ASensorManager* manager, int type) {
RETURN_IF_MANAGER_IS_NULL(nullptr);
return static_cast<SensorManager*>(manager)->getDefaultSensor(type);
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
index a423783..9cb0947 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
@@ -61,7 +61,7 @@
* Internet Protocol</a>
*/
@SystemService(Context.IPSEC_SERVICE)
-public final class IpSecManager {
+public class IpSecManager {
private static final String TAG = "IpSecManager";
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java b/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java
index 2e7cfcb..9a29f22 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java
@@ -51,6 +51,9 @@
private static final int[] MICROPHONE_OPS = new int[]{
AppOpsManager.OP_RECORD_AUDIO,
};
+ private static final int[] CAMERA_OPS = new int[]{
+ AppOpsManager.OP_CAMERA,
+ };
private static final String TAG = RecentAppOpsAccess.class.getSimpleName();
@@ -99,6 +102,13 @@
}
/**
+ * Creates an instance of {@link RecentAppOpsAccess} for camera access.
+ */
+ public static RecentAppOpsAccess createForCamera(Context context) {
+ return new RecentAppOpsAccess(context, CAMERA_OPS);
+ }
+
+ /**
* Fills a list of applications which queried for access recently within specified time.
* Apps are sorted by recency. Apps with more recent accesses are in the front.
*/
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
index dee6894..6a1cee3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
@@ -16,6 +16,7 @@
package com.android.settingslib.notification;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.AlertDialog;
@@ -42,8 +43,6 @@
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.policy.PhoneWindow;
import com.android.settingslib.R;
@@ -72,6 +71,9 @@
private static final int SECONDS_MS = 1000;
private static final int MINUTES_MS = 60 * SECONDS_MS;
+ @Nullable
+ private final ZenModeDialogMetricsLogger mMetricsLogger;
+
@VisibleForTesting
protected Uri mForeverId;
private int mBucketIndex = -1;
@@ -102,13 +104,16 @@
}
public EnableZenModeDialog(Context context, int themeResId) {
- this(context, themeResId, false /* cancelIsNeutral */);
+ this(context, themeResId, false /* cancelIsNeutral */,
+ new ZenModeDialogMetricsLogger(context));
}
- public EnableZenModeDialog(Context context, int themeResId, boolean cancelIsNeutral) {
+ public EnableZenModeDialog(Context context, int themeResId, boolean cancelIsNeutral,
+ ZenModeDialogMetricsLogger metricsLogger) {
mContext = context;
mThemeResId = themeResId;
mCancelIsNeutral = cancelIsNeutral;
+ mMetricsLogger = metricsLogger;
}
public AlertDialog createDialog() {
@@ -129,17 +134,11 @@
ConditionTag tag = getConditionTagAt(checkedId);
if (isForever(tag.condition)) {
- MetricsLogger.action(mContext,
- MetricsProto.MetricsEvent.
- NOTIFICATION_ZEN_MODE_TOGGLE_ON_FOREVER);
+ mMetricsLogger.logOnEnableZenModeForever();
} else if (isAlarm(tag.condition)) {
- MetricsLogger.action(mContext,
- MetricsProto.MetricsEvent.
- NOTIFICATION_ZEN_MODE_TOGGLE_ON_ALARM);
+ mMetricsLogger.logOnEnableZenModeUntilAlarm();
} else if (isCountdown(tag.condition)) {
- MetricsLogger.action(mContext,
- MetricsProto.MetricsEvent.
- NOTIFICATION_ZEN_MODE_TOGGLE_ON_COUNTDOWN);
+ mMetricsLogger.logOnEnableZenModeUntilCountdown();
} else {
Slog.d(TAG, "Invalid manual condition: " + tag.condition);
}
@@ -222,8 +221,7 @@
if (isChecked) {
tag.rb.setChecked(true);
if (DEBUG) Log.d(TAG, "onCheckedChanged " + conditionId);
- MetricsLogger.action(mContext,
- MetricsProto.MetricsEvent.QS_DND_CONDITION_SELECT);
+ mMetricsLogger.logOnConditionSelected();
updateAlarmWarningText(tag.condition);
}
}
@@ -435,7 +433,7 @@
}
private void onClickTimeButton(View row, ConditionTag tag, boolean up, int rowId) {
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.QS_DND_TIME, up);
+ mMetricsLogger.logOnClickTimeButton(up);
Condition newCondition = null;
final int N = MINUTE_BUCKETS.length;
if (mBucketIndex == -1) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/ZenModeDialogMetricsLogger.java b/packages/SettingsLib/src/com/android/settingslib/notification/ZenModeDialogMetricsLogger.java
new file mode 100644
index 0000000..088a37d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/ZenModeDialogMetricsLogger.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 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.settingslib.notification;
+
+import android.content.Context;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+
+/**
+ * Logs ui events for {@link EnableZenModeDialog}.
+ */
+public class ZenModeDialogMetricsLogger {
+ private final Context mContext;
+
+ public ZenModeDialogMetricsLogger(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * User enabled DND from the QS DND dialog to last until manually turned off
+ */
+ public void logOnEnableZenModeForever() {
+ MetricsLogger.action(
+ mContext,
+ MetricsProto.MetricsEvent.NOTIFICATION_ZEN_MODE_TOGGLE_ON_FOREVER);
+ }
+
+ /**
+ * User enabled DND from the QS DND dialog to last until the next alarm goes off
+ */
+ public void logOnEnableZenModeUntilAlarm() {
+ MetricsLogger.action(
+ mContext,
+ MetricsProto.MetricsEvent.NOTIFICATION_ZEN_MODE_TOGGLE_ON_ALARM);
+ }
+
+ /**
+ * User enabled DND from the QS DND dialog to last until countdown is done
+ */
+ public void logOnEnableZenModeUntilCountdown() {
+ MetricsLogger.action(
+ mContext,
+ MetricsProto.MetricsEvent.NOTIFICATION_ZEN_MODE_TOGGLE_ON_COUNTDOWN);
+ }
+
+ /**
+ * User selected an option on the DND dialog
+ */
+ public void logOnConditionSelected() {
+ MetricsLogger.action(
+ mContext,
+ MetricsProto.MetricsEvent.QS_DND_CONDITION_SELECT);
+ }
+
+ /**
+ * User increased or decreased countdown duration of DND from the DND dialog
+ */
+ public void logOnClickTimeButton(boolean up) {
+ MetricsLogger.action(mContext, MetricsProto.MetricsEvent.QS_DND_TIME, up);
+ }
+}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index a6bfc408b..716ee84 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -43,6 +43,7 @@
Settings.System.FONT_SCALE,
Settings.System.DIM_SCREEN,
Settings.System.SCREEN_OFF_TIMEOUT,
+ Settings.System.SCREEN_OFF_TIMEOUT_DOCKED,
Settings.System.SCREEN_BRIGHTNESS_MODE,
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
Settings.System.SCREEN_BRIGHTNESS_FOR_VR,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 06712cc..d430296 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -111,6 +111,7 @@
});
VALIDATORS.put(System.DISPLAY_COLOR_MODE_VENDOR_HINT, ANY_STRING_VALIDATOR);
VALIDATORS.put(System.SCREEN_OFF_TIMEOUT, NON_NEGATIVE_INTEGER_VALIDATOR);
+ VALIDATORS.put(System.SCREEN_OFF_TIMEOUT_DOCKED, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(System.SCREEN_BRIGHTNESS_FOR_VR, new InclusiveIntegerRangeValidator(0, 255));
VALIDATORS.put(System.SCREEN_BRIGHTNESS_MODE, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.ADAPTIVE_SLEEP, BOOLEAN_VALIDATOR);
diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
index ae557c4..e897f75 100644
--- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
@@ -20,4 +20,6 @@
<dimen name="controls_padding_horizontal">205dp</dimen>
<dimen name="split_shade_notifications_scrim_margin_bottom">16dp</dimen>
<dimen name="notification_panel_margin_bottom">56dp</dimen>
+
+ <dimen name="keyguard_split_shade_top_margin">72dp</dimen>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
index 54664f2..72b40d4 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
@@ -48,7 +48,7 @@
@Override
public void start() {
if (DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED, true)) {
+ DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED, false)) {
mClipboardManager = requireNonNull(mContext.getSystemService(ClipboardManager.class));
mClipboardManager.addPrimaryClipChangedListener(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 552f188..51101da 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -139,7 +139,7 @@
/***************************************/
// 900 - media
- public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, false);
+ public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, true);
public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, true);
public static final BooleanFlag MEDIA_SESSION_LAYOUT = new BooleanFlag(902, false);
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
index adae07b..2ed2f4f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
@@ -47,7 +47,7 @@
gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL)
type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY
flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- title = "Media Tap-To-Transfer Chip View"
+ title = WINDOW_TITLE
format = PixelFormat.TRANSLUCENT
setTrustedOverlay()
}
@@ -106,3 +106,7 @@
}
}
}
+
+// Used in CTS tests UpdateMediaTapToTransferSenderDisplayTest and
+// UpdateMediaTapToTransferReceiverDisplayTest
+private const val WINDOW_TITLE = "Media Transfer Chip View"
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt b/packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt
index cc5a771..26adf46 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt
@@ -94,15 +94,28 @@
override fun getId() = _id
}
+/**
+ * Events from the QS DND tile dialog. {@see QSZenModeDialogMetricsLogger}
+ * Other names for DND (Do Not Disturb) include "Zen" and "Priority mode".
+ */
enum class QSDndEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
- @UiEvent(doc = "TODO(beverlyt)")
+ @UiEvent(doc = "User selected an option on the DND dialog")
QS_DND_CONDITION_SELECT(420),
- @UiEvent(doc = "TODO(beverlyt)")
+ @UiEvent(doc = "User increased countdown duration of DND from the DND dialog")
QS_DND_TIME_UP(422),
- @UiEvent(doc = "TODO(beverlyt)")
- QS_DND_TIME_DOWN(423);
+ @UiEvent(doc = "User decreased countdown duration of DND from the DND dialog")
+ QS_DND_TIME_DOWN(423),
+
+ @UiEvent(doc = "User enabled DND from the QS DND dialog to last until manually turned off")
+ QS_DND_DIALOG_ENABLE_FOREVER(946),
+
+ @UiEvent(doc = "User enabled DND from the QS DND dialog to last until the next alarm goes off")
+ QS_DND_DIALOG_ENABLE_UNTIL_ALARM(947),
+
+ @UiEvent(doc = "User enabled DND from the QS DND dialog to last until countdown is done")
+ QS_DND_DIALOG_ENABLE_UNTIL_COUNTDOWN(948);
override fun getId() = _id
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index a06dc8b..a33650c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -65,6 +65,7 @@
import com.android.systemui.qs.SettingObserver;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.qs.tiles.dialog.QSZenModeDialogMetricsLogger;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.settings.SecureSettings;
@@ -86,6 +87,7 @@
private final SharedPreferences mSharedPreferences;
private final SettingObserver mSettingZenDuration;
private final DialogLaunchAnimator mDialogLaunchAnimator;
+ private final QSZenModeDialogMetricsLogger mQSZenDialogMetricsLogger;
private boolean mListening;
private boolean mShowingDetail;
@@ -119,6 +121,7 @@
refreshState();
}
};
+ mQSZenDialogMetricsLogger = new QSZenModeDialogMetricsLogger(mContext);
}
public static void setVisible(Context context, boolean visible) {
@@ -211,7 +214,8 @@
private Dialog makeZenModeDialog() {
AlertDialog dialog = new EnableZenModeDialog(mContext, R.style.Theme_SystemUI_Dialog,
- true /* cancelIsNeutral */).createDialog();
+ true /* cancelIsNeutral */,
+ mQSZenDialogMetricsLogger).createDialog();
SystemUIDialog.applyFlags(dialog);
SystemUIDialog.setShowForAllUsers(dialog, true);
SystemUIDialog.registerDismissListener(dialog);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/QSZenModeDialogMetricsLogger.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/QSZenModeDialogMetricsLogger.java
new file mode 100644
index 0000000..1b81a99
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/QSZenModeDialogMetricsLogger.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 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.systemui.qs.tiles.dialog;
+
+import android.content.Context;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.settingslib.notification.ZenModeDialogMetricsLogger;
+import com.android.systemui.qs.QSDndEvent;
+import com.android.systemui.qs.QSEvents;
+
+/**
+ * Logs ui events for the DND dialog that may appear from tapping the QS DND tile.
+ * To see the dialog from QS:
+ * Settings > Notifications > Do Not Disturb > Duration for Quick Settings > Ask every time
+ *
+ * Other names for DND (Do Not Disturb) include "Zen" and "Priority only".
+ */
+public class QSZenModeDialogMetricsLogger extends ZenModeDialogMetricsLogger {
+ private final UiEventLogger mUiEventLogger = QSEvents.INSTANCE.getQsUiEventsLogger();
+
+ public QSZenModeDialogMetricsLogger(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void logOnEnableZenModeForever() {
+ super.logOnEnableZenModeForever();
+ mUiEventLogger.log(QSDndEvent.QS_DND_DIALOG_ENABLE_FOREVER);
+ }
+
+ @Override
+ public void logOnEnableZenModeUntilAlarm() {
+ super.logOnEnableZenModeUntilAlarm();
+ mUiEventLogger.log(QSDndEvent.QS_DND_DIALOG_ENABLE_UNTIL_ALARM);
+ }
+
+ @Override
+ public void logOnEnableZenModeUntilCountdown() {
+ super.logOnEnableZenModeUntilCountdown();
+ mUiEventLogger.log(QSDndEvent.QS_DND_DIALOG_ENABLE_UNTIL_COUNTDOWN);
+ }
+
+ @Override
+ public void logOnConditionSelected() {
+ super.logOnConditionSelected();
+ mUiEventLogger.log(QSDndEvent.QS_DND_CONDITION_SELECT);
+ }
+
+ @Override
+ public void logOnClickTimeButton(boolean up) {
+ super.logOnClickTimeButton(up);
+ mUiEventLogger.log(up
+ ? QSDndEvent.QS_DND_TIME_UP : QSDndEvent.QS_DND_TIME_DOWN);
+ }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index eccf27e..0509a7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -73,6 +73,7 @@
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardIndication;
@@ -132,6 +133,7 @@
private final DevicePolicyManager mDevicePolicyManager;
private final UserManager mUserManager;
protected final @Main DelayableExecutor mExecutor;
+ protected final @Background DelayableExecutor mBackgroundExecutor;
private final LockPatternUtils mLockPatternUtils;
private final IActivityManager mIActivityManager;
private final FalsingManager mFalsingManager;
@@ -203,6 +205,7 @@
IBatteryStats iBatteryStats,
UserManager userManager,
@Main DelayableExecutor executor,
+ @Background DelayableExecutor bgExecutor,
FalsingManager falsingManager,
LockPatternUtils lockPatternUtils,
ScreenLifecycle screenLifecycle,
@@ -220,6 +223,7 @@
mBatteryInfo = iBatteryStats;
mUserManager = userManager;
mExecutor = executor;
+ mBackgroundExecutor = bgExecutor;
mLockPatternUtils = lockPatternUtils;
mIActivityManager = iActivityManager;
mFalsingManager = falsingManager;
@@ -328,15 +332,22 @@
private void updateDisclosure() {
if (mOrganizationOwnedDevice) {
- final CharSequence organizationName = getOrganizationOwnedDeviceOrganizationName();
- final CharSequence disclosure = getDisclosureText(organizationName);
- mRotateTextViewController.updateIndication(
- INDICATION_TYPE_DISCLOSURE,
- new KeyguardIndication.Builder()
- .setMessage(disclosure)
- .setTextColor(mInitialTextColorState)
- .build(),
- /* updateImmediately */ false);
+ mBackgroundExecutor.execute(() -> {
+ final CharSequence organizationName = getOrganizationOwnedDeviceOrganizationName();
+ final CharSequence disclosure = getDisclosureText(organizationName);
+
+ mExecutor.execute(() -> {
+ if (mKeyguardStateController.isShowing()) {
+ mRotateTextViewController.updateIndication(
+ INDICATION_TYPE_DISCLOSURE,
+ new KeyguardIndication.Builder()
+ .setMessage(disclosure)
+ .setTextColor(mInitialTextColorState)
+ .build(),
+ /* updateImmediately */ false);
+ }
+ });
+ });
} else {
mRotateTextViewController.hideIndication(INDICATION_TYPE_DISCLOSURE);
}
@@ -364,26 +375,35 @@
}
private void updateOwnerInfo() {
- String info = mLockPatternUtils.getDeviceOwnerInfo();
- if (info == null) {
- // Use the current user owner information if enabled.
- final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled(
- KeyguardUpdateMonitor.getCurrentUser());
- if (ownerInfoEnabled) {
- info = mLockPatternUtils.getOwnerInfo(KeyguardUpdateMonitor.getCurrentUser());
+ // Check device owner info on a bg thread.
+ // It makes multiple IPCs that could block the thread it's run on.
+ mBackgroundExecutor.execute(() -> {
+ String info = mLockPatternUtils.getDeviceOwnerInfo();
+ if (info == null) {
+ // Use the current user owner information if enabled.
+ final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled(
+ KeyguardUpdateMonitor.getCurrentUser());
+ if (ownerInfoEnabled) {
+ info = mLockPatternUtils.getOwnerInfo(KeyguardUpdateMonitor.getCurrentUser());
+ }
}
- }
- if (!TextUtils.isEmpty(info)) {
- mRotateTextViewController.updateIndication(
- INDICATION_TYPE_OWNER_INFO,
- new KeyguardIndication.Builder()
- .setMessage(info)
- .setTextColor(mInitialTextColorState)
- .build(),
- false);
- } else {
- mRotateTextViewController.hideIndication(INDICATION_TYPE_OWNER_INFO);
- }
+
+ // Update the UI on the main thread.
+ final String finalInfo = info;
+ mExecutor.execute(() -> {
+ if (!TextUtils.isEmpty(finalInfo) && mKeyguardStateController.isShowing()) {
+ mRotateTextViewController.updateIndication(
+ INDICATION_TYPE_OWNER_INFO,
+ new KeyguardIndication.Builder()
+ .setMessage(finalInfo)
+ .setTextColor(mInitialTextColorState)
+ .build(),
+ false);
+ } else {
+ mRotateTextViewController.hideIndication(INDICATION_TYPE_OWNER_INFO);
+ }
+ });
+ });
}
private void updateBattery(boolean animate) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index ee12cc5..7811401 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -336,6 +336,9 @@
}
private void setDozeAmountInternal(float dozeAmount) {
+ if (Float.compare(dozeAmount, mDozeAmount) == 0) {
+ return;
+ }
mDozeAmount = dozeAmount;
float interpolatedAmount = mDozeInterpolator.getInterpolation(dozeAmount);
synchronized (mListeners) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index e1116f8..d1c63e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -290,11 +290,11 @@
@Override
public void onThemeChanged() {
- updateShowEmptyShadeView();
mView.updateCornerRadius();
mView.updateBgColor();
mView.updateDecorViews();
mView.reinflateViews();
+ updateShowEmptyShadeView();
updateFooter();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index e24cd3e..3a3f581 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -376,6 +376,11 @@
final float stackHeight = ambientState.getStackHeight() - shelfHeight - scrimPadding;
final float stackEndHeight = ambientState.getStackEndHeight() - shelfHeight - scrimPadding;
+ if (stackEndHeight == 0f) {
+ // This should not happen, since even when the shade is empty we show EmptyShadeView
+ // but check just in case, so we don't return infinity or NaN.
+ return 0f;
+ }
return stackHeight / stackEndHeight;
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 0cd4fb9..7bb987c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -56,12 +56,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Prefs;
import com.android.systemui.R;
-import com.android.systemui.qs.QSDndEvent;
-import com.android.systemui.qs.QSEvents;
import com.android.systemui.statusbar.policy.ZenModeController;
import java.io.FileDescriptor;
@@ -106,7 +103,6 @@
private final TransitionHelper mTransitionHelper = new TransitionHelper();
private final Uri mForeverId;
private final ConfigurableTexts mConfigurableTexts;
- private final UiEventLogger mUiEventLogger = QSEvents.INSTANCE.getQsUiEventsLogger();
private String mTag = TAG + "/" + Integer.toHexString(System.identityHashCode(this));
@@ -666,7 +662,6 @@
tag.rb.setChecked(true);
if (DEBUG) Log.d(mTag, "onCheckedChanged " + conditionId);
MetricsLogger.action(mContext, MetricsEvent.QS_DND_CONDITION_SELECT);
- mUiEventLogger.log(QSDndEvent.QS_DND_CONDITION_SELECT);
select(tag.condition);
announceConditionSelection(tag);
}
@@ -772,7 +767,6 @@
private void onClickTimeButton(View row, ConditionTag tag, boolean up, int rowId) {
MetricsLogger.action(mContext, MetricsEvent.QS_DND_TIME, up);
- mUiEventLogger.log(up ? QSDndEvent.QS_DND_TIME_UP : QSDndEvent.QS_DND_TIME_DOWN);
Condition newCondition = null;
final int N = MINUTE_BUCKETS.length;
if (mBucketIndex == -1) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 98e2857..5924329 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -229,8 +229,8 @@
mController = new KeyguardIndicationController(mContext, mWakeLockBuilder,
mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor,
mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats,
- mUserManager, mExecutor, mFalsingManager, mLockPatternUtils, mScreenLifecycle,
- mIActivityManager, mKeyguardBypassController);
+ mUserManager, mExecutor, mExecutor, mFalsingManager, mLockPatternUtils,
+ mScreenLifecycle, mIActivityManager, mKeyguardBypassController);
mController.init();
mController.setIndicationArea(mIndicationArea);
verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
@@ -331,6 +331,7 @@
when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false);
when(mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()).thenReturn(false);
sendUpdateDisclosureBroadcast();
+ mExecutor.runAllReady();
verifyHideIndication(INDICATION_TYPE_DISCLOSURE);
}
@@ -341,6 +342,7 @@
when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null);
sendUpdateDisclosureBroadcast();
+ mExecutor.runAllReady();
verifyIndicationMessage(INDICATION_TYPE_DISCLOSURE, mDisclosureGeneric);
}
@@ -353,6 +355,7 @@
new UserInfo(10, /* name */ null, /* flags */ FLAG_MANAGED_PROFILE)));
when(mDevicePolicyManager.getOrganizationNameForUser(eq(10))).thenReturn(null);
sendUpdateDisclosureBroadcast();
+ mExecutor.runAllReady();
verifyIndicationMessage(INDICATION_TYPE_DISCLOSURE, mDisclosureGeneric);
}
@@ -363,6 +366,7 @@
when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(ORGANIZATION_NAME);
sendUpdateDisclosureBroadcast();
+ mExecutor.runAllReady();
verifyIndicationMessage(INDICATION_TYPE_DISCLOSURE, mDisclosureWithOrganization);
}
@@ -375,6 +379,7 @@
new UserInfo(10, /* name */ null, FLAG_MANAGED_PROFILE)));
when(mDevicePolicyManager.getOrganizationNameForUser(eq(10))).thenReturn(ORGANIZATION_NAME);
sendUpdateDisclosureBroadcast();
+ mExecutor.runAllReady();
verifyIndicationMessage(INDICATION_TYPE_DISCLOSURE, mDisclosureWithOrganization);
}
@@ -387,6 +392,7 @@
when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null);
sendUpdateDisclosureBroadcast();
+ mExecutor.runAllReady();
verifyIndicationMessage(INDICATION_TYPE_DISCLOSURE, mDisclosureGeneric);
reset(mRotateTextViewController);
@@ -394,12 +400,14 @@
when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(ORGANIZATION_NAME);
sendUpdateDisclosureBroadcast();
+ mExecutor.runAllReady();
verifyIndicationMessage(INDICATION_TYPE_DISCLOSURE, mDisclosureWithOrganization);
reset(mRotateTextViewController);
when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false);
sendUpdateDisclosureBroadcast();
+ mExecutor.runAllReady();
verifyHideIndication(INDICATION_TYPE_DISCLOSURE);
}
@@ -413,6 +421,7 @@
when(mDevicePolicyManager.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
.thenReturn(DEVICE_OWNER_TYPE_FINANCED);
sendUpdateDisclosureBroadcast();
+ mExecutor.runAllReady();
verifyIndicationMessage(INDICATION_TYPE_DISCLOSURE, mFinancedDisclosureWithOrganization);
}
@@ -734,6 +743,7 @@
// WHEN asked to update the indication area
mController.setVisible(true);
+ mExecutor.runAllReady();
// THEN the owner info should be hidden
verifyHideIndication(INDICATION_TYPE_OWNER_INFO);
@@ -763,6 +773,7 @@
// WHEN keyguard showing changed called
mKeyguardStateControllerCallback.onKeyguardShowingChanged();
+ mExecutor.runAllReady();
// THEN persistent messages are updated (in this case, most messages are hidden since
// no info is provided) - verify that this happens
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index a5ea897..6abdea5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -23,14 +23,18 @@
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
@@ -73,4 +77,14 @@
assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_SHADE.id, ids[1])
assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_SHADE_LOCKED.id, ids[2])
}
+
+ @Test
+ fun testSetDozeAmountInternal_onlySetsOnce() {
+ val listener = mock(StatusBarStateController.StateListener::class.java)
+ controller.addCallback(listener)
+
+ controller.setDozeAmount(0.5f, false /* animated */)
+ controller.setDozeAmount(0.5f, false /* animated */)
+ verify(listener).onDozeAmountChanged(eq(0.5f), anyFloat())
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 8b62a64..7f10314 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -25,6 +25,7 @@
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CONNECTION;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.accessibility.AccessibilityInteractionClient.CALL_STACK;
import static android.view.accessibility.AccessibilityInteractionClient.IGNORE_CALL_STACK;
@@ -66,6 +67,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.Trace;
import android.provider.Settings;
import android.util.Slog;
import android.util.SparseArray;
@@ -80,15 +82,21 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputBinding;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.view.IInputContext;
+import com.android.internal.view.IInputMethodSession;
+import com.android.internal.view.IInputSessionWithIdCallback;
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
import com.android.server.accessibility.magnification.MagnificationProcessor;
+import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -180,6 +188,8 @@
boolean mLastAccessibilityButtonCallbackState;
+ boolean mRequestImeApis;
+
int mFetchFlags;
long mNotificationTimeout;
@@ -271,6 +281,9 @@
void onDoubleTapAndHold(int displayId);
+ void requestImeLocked(AccessibilityServiceConnection connection);
+
+ void unbindImeLocked(AccessibilityServiceConnection connection);
}
public AbstractAccessibilityServiceConnection(Context context, ComponentName componentName,
@@ -374,6 +387,9 @@
& AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES) != 0;
mRequestAccessibilityButton = (info.flags
& AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
+ // TODO(b/218193835): request ime when ime flag is set and clean up when ime flag is unset
+ mRequestImeApis = (info.flags
+ & AccessibilityServiceInfo.FLAG_INPUT_METHOD_EDITOR) != 0;
}
protected boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) {
@@ -1610,6 +1626,27 @@
mInvocationHandler.notifyAccessibilityButtonAvailabilityChangedLocked(available);
}
+ public void createImeSessionLocked() {
+ mInvocationHandler.createImeSessionLocked();
+ }
+
+ public void setImeSessionEnabledLocked(IInputMethodSession session, boolean enabled) {
+ mInvocationHandler.setImeSessionEnabledLocked(session, enabled);
+ }
+
+ public void bindInputLocked(InputBinding binding) {
+ mInvocationHandler.bindInputLocked(binding);
+ }
+
+ public void unbindInputLocked() {
+ mInvocationHandler.unbindInputLocked();
+ }
+
+ public void startInputLocked(IBinder startInputToken, IInputContext inputContext,
+ EditorInfo editorInfo, boolean restarting) {
+ mInvocationHandler.startInputLocked(startInputToken, inputContext, editorInfo, restarting);
+ }
+
/**
* Called by the invocation handler to notify the service that the
* state of magnification has changed.
@@ -1732,6 +1769,84 @@
}
}
+ private void createImeSessionInternal() {
+ final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
+ if (listener != null) {
+ try {
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("createImeSession", "");
+ }
+ AccessibilityCallback callback = new AccessibilityCallback();
+ listener.createImeSession(callback);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG,
+ "Error requesting IME session from " + mService, re);
+ }
+ }
+ }
+
+ private void setImeSessionEnabledInternal(IInputMethodSession session, boolean enabled) {
+ final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
+ if (listener != null && session != null) {
+ try {
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("createImeSession", "");
+ }
+ listener.setImeSessionEnabled(session, enabled);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG,
+ "Error requesting IME session from " + mService, re);
+ }
+ }
+ }
+
+ private void bindInputInternal(InputBinding binding) {
+ final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
+ if (listener != null) {
+ try {
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("bindInput", binding.toString());
+ }
+ listener.bindInput(binding);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG,
+ "Error binding input to " + mService, re);
+ }
+ }
+ }
+
+ private void unbindInputInternal() {
+ final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
+ if (listener != null) {
+ try {
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("unbindInput", "");
+ }
+ listener.unbindInput();
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG,
+ "Error unbinding input to " + mService, re);
+ }
+ }
+ }
+
+ private void startInputInternal(IBinder startInputToken, IInputContext inputContext,
+ EditorInfo editorInfo, boolean restarting) {
+ final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
+ if (listener != null) {
+ try {
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("startInput", startInputToken + " "
+ + inputContext + " " + editorInfo + restarting);
+ }
+ listener.startInput(startInputToken, inputContext, editorInfo, restarting);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG,
+ "Error starting input to " + mService, re);
+ }
+ }
+ }
+
protected IAccessibilityServiceClient getServiceInterfaceSafely() {
synchronized (mLock) {
return mServiceInterface;
@@ -1925,6 +2040,11 @@
private static final int MSG_ON_ACCESSIBILITY_BUTTON_CLICKED = 7;
private static final int MSG_ON_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED = 8;
private static final int MSG_ON_SYSTEM_ACTIONS_CHANGED = 9;
+ private static final int MSG_CREATE_IME_SESSION = 10;
+ private static final int MSG_SET_IME_SESSION_ENABLED = 11;
+ private static final int MSG_BIND_INPUT = 12;
+ private static final int MSG_UNBIND_INPUT = 13;
+ private static final int MSG_START_INPUT = 14;
/** List of magnification callback states, mapping from displayId -> Boolean */
@GuardedBy("mlock")
@@ -1974,6 +2094,29 @@
notifySystemActionsChangedInternal();
break;
}
+ case MSG_CREATE_IME_SESSION:
+ createImeSessionInternal();
+ break;
+ case MSG_SET_IME_SESSION_ENABLED:
+ final boolean enabled = (message.arg1 != 0);
+ final IInputMethodSession session = (IInputMethodSession) message.obj;
+ setImeSessionEnabledInternal(session, enabled);
+ break;
+ case MSG_BIND_INPUT:
+ final InputBinding binding = (InputBinding) message.obj;
+ bindInputInternal(binding);
+ break;
+ case MSG_UNBIND_INPUT:
+ unbindInputInternal();
+ break;
+ case MSG_START_INPUT:
+ final boolean restarting = (message.arg1 != 0);
+ final SomeArgs args = (SomeArgs) message.obj;
+ final IBinder startInputToken = (IBinder) args.arg1;
+ final IInputContext inputContext = (IInputContext) args.arg2;
+ final EditorInfo editorInfo = (EditorInfo) args.arg3;
+ startInputInternal(startInputToken, inputContext, editorInfo, restarting);
+ break;
default: {
throw new IllegalArgumentException("Unknown message: " + type);
}
@@ -2036,6 +2179,37 @@
(available ? 1 : 0), 0);
msg.sendToTarget();
}
+
+ public void createImeSessionLocked() {
+ final Message msg = obtainMessage(MSG_CREATE_IME_SESSION);
+ msg.sendToTarget();
+ }
+
+ public void setImeSessionEnabledLocked(IInputMethodSession session, boolean enabled) {
+ final Message msg = obtainMessage(MSG_SET_IME_SESSION_ENABLED, (enabled ? 1 : 0),
+ 0, session);
+ msg.sendToTarget();
+ }
+
+ public void bindInputLocked(InputBinding binding) {
+ final Message msg = obtainMessage(MSG_BIND_INPUT, binding);
+ msg.sendToTarget();
+ }
+
+ public void unbindInputLocked() {
+ final Message msg = obtainMessage(MSG_UNBIND_INPUT);
+ msg.sendToTarget();
+ }
+
+ public void startInputLocked(IBinder startInputToken, IInputContext inputContext,
+ EditorInfo editorInfo, boolean restarting) {
+ final SomeArgs args = SomeArgs.obtain();
+ args.arg1 = startInputToken;
+ args.arg2 = inputContext;
+ args.arg3 = editorInfo;
+ final Message msg = obtainMessage(MSG_START_INPUT, restarting ? 1 : 0, 0, args);
+ msg.sendToTarget();
+ }
}
public boolean isServiceHandlesDoubleTapEnabled() {
@@ -2185,4 +2359,18 @@
Binder.restoreCallingIdentity(identity);
}
}
+
+ private static final class AccessibilityCallback extends IInputSessionWithIdCallback.Stub {
+ @Override
+ public void sessionCreated(IInputMethodSession session, int id) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.sessionCreated");
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ InputMethodManagerInternal.get().onSessionForAccessibilityCreated(id, session);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+ }
}
\ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 5b580d9..62da981 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -115,6 +115,8 @@
import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
import android.view.accessibility.IWindowMagnificationConnection;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputBinding;
import com.android.internal.R;
import com.android.internal.accessibility.AccessibilityShortcutController;
@@ -128,12 +130,16 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IntPair;
+import com.android.internal.view.IInputContext;
+import com.android.internal.view.IInputMethodSession;
+import com.android.server.AccessibilityManagerInternal;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.MagnificationProcessor;
import com.android.server.accessibility.magnification.MagnificationScaleProvider;
import com.android.server.accessibility.magnification.WindowMagnificationManager;
+import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -231,7 +237,7 @@
private final MainHandler mMainHandler;
- // Lazily initialized - access through getSystemActionPerfomer()
+ // Lazily initialized - access through getSystemActionPerformer()
private SystemActionPerformer mSystemActionPerformer;
private InteractionBridge mInteractionBridge;
@@ -273,6 +279,13 @@
private Point mTempPoint = new Point();
private boolean mIsAccessibilityButtonShown;
+ private InputBinding mInputBinding;
+ IBinder mStartInputToken;
+ IInputContext mInputContext;
+ EditorInfo mEditorInfo;
+ boolean mRestarting;
+ boolean mInputSessionRequested;
+
private AccessibilityUserState getCurrentUserStateLocked() {
return getUserStateLocked(mCurrentUserId);
}
@@ -298,6 +311,42 @@
}
}
+ private static final class LocalServiceImpl extends AccessibilityManagerInternal {
+ @NonNull
+ private final AccessibilityManagerService mService;
+
+ LocalServiceImpl(@NonNull AccessibilityManagerService service) {
+ mService = service;
+ }
+
+ @Override
+ public void setImeSessionEnabled(SparseArray<IInputMethodSession> sessions,
+ boolean enabled) {
+ mService.setImeSessionEnabled(sessions, enabled);
+ }
+
+ @Override
+ public void unbindInput() {
+ mService.unbindInput();
+ }
+
+ @Override
+ public void bindInput(InputBinding binding) {
+ mService.bindInput(binding);
+ }
+
+ @Override
+ public void createImeSession(ArraySet<Integer> ignoreSet) {
+ mService.createImeSession(ignoreSet);
+ }
+
+ @Override
+ public void startInput(IBinder startInputToken, IInputContext inputContext,
+ EditorInfo editorInfo, boolean restarting) {
+ mService.startInput(startInputToken, inputContext, editorInfo, restarting);
+ }
+ }
+
public static final class Lifecycle extends SystemService {
private final AccessibilityManagerService mService;
@@ -308,6 +357,8 @@
@Override
public void onStart() {
+ LocalServices.addService(AccessibilityManagerInternal.class,
+ new LocalServiceImpl(mService));
publishBinderService(Context.ACCESSIBILITY_SERVICE, mService);
}
@@ -3463,27 +3514,27 @@
@Override
@RequiresPermission(Manifest.permission.SET_SYSTEM_AUDIO_CAPTION)
- public void setSystemAudioCaptioningRequested(boolean isEnabled, int userId) {
+ public void setSystemAudioCaptioningEnabled(boolean isEnabled, int userId) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.SET_SYSTEM_AUDIO_CAPTION,
- "setSystemAudioCaptioningRequested");
+ "setSystemAudioCaptioningEnabled");
- mCaptioningManagerImpl.setSystemAudioCaptioningRequested(isEnabled, userId);
+ mCaptioningManagerImpl.setSystemAudioCaptioningEnabled(isEnabled, userId);
}
@Override
- public boolean isSystemAudioCaptioningUiRequested(int userId) {
- return mCaptioningManagerImpl.isSystemAudioCaptioningUiRequested(userId);
+ public boolean isSystemAudioCaptioningUiEnabled(int userId) {
+ return mCaptioningManagerImpl.isSystemAudioCaptioningUiEnabled(userId);
}
@Override
@RequiresPermission(Manifest.permission.SET_SYSTEM_AUDIO_CAPTION)
- public void setSystemAudioCaptioningUiRequested(boolean isEnabled, int userId) {
+ public void setSystemAudioCaptioningUiEnabled(boolean isEnabled, int userId) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.SET_SYSTEM_AUDIO_CAPTION,
- "setSystemAudioCaptioningUiRequested");
+ "setSystemAudioCaptioningUiEnabled");
- mCaptioningManagerImpl.setSystemAudioCaptioningUiRequested(isEnabled, userId);
+ mCaptioningManagerImpl.setSystemAudioCaptioningUiEnabled(isEnabled, userId);
}
@Override
@@ -4240,6 +4291,45 @@
this, displayId));
}
+ @Override
+ public void requestImeLocked(AccessibilityServiceConnection connection) {
+ mMainHandler.sendMessage(obtainMessage(
+ AccessibilityManagerService::createSessionForConnection, this, connection));
+ mMainHandler.sendMessage(obtainMessage(
+ AccessibilityManagerService::bindAndStartInputForConnection, this, connection));
+ }
+
+ @Override
+ public void unbindImeLocked(AccessibilityServiceConnection connection) {
+ mMainHandler.sendMessage(obtainMessage(
+ AccessibilityManagerService::unbindInputForConnection, this, connection));
+ }
+
+ private void createSessionForConnection(AccessibilityServiceConnection connection) {
+ synchronized (mLock) {
+ if (mInputSessionRequested) {
+ connection.createImeSessionLocked();
+ }
+ }
+ }
+
+ private void bindAndStartInputForConnection(AccessibilityServiceConnection connection) {
+ synchronized (mLock) {
+ if (mInputBinding != null) {
+ connection.bindInputLocked(mInputBinding);
+ connection.startInputLocked(mStartInputToken, mInputContext, mEditorInfo,
+ mRestarting);
+ }
+ }
+ }
+
+ private void unbindInputForConnection(AccessibilityServiceConnection connection) {
+ InputMethodManagerInternal.get().unbindAccessibilityFromCurrentClient(connection.mId);
+ synchronized (mLock) {
+ connection.unbindInputLocked();
+ }
+ }
+
private void onDoubleTapAndHoldInternal(int displayId) {
synchronized (mLock) {
if (mHasInputFilter && mInputFilter != null) {
@@ -4268,4 +4358,100 @@
public AccessibilityTraceManager getTraceManager() {
return mTraceManager;
}
+
+ /**
+ * Bind input for accessibility services which request ime capabilities.
+ *
+ * @param binding Information given to an accessibility service about a client connecting to it.
+ */
+ public void bindInput(InputBinding binding) {
+ AccessibilityUserState userState;
+ synchronized (mLock) {
+ // Keep records of these in case new Accessibility Services are enabled.
+ mInputBinding = binding;
+ userState = getCurrentUserStateLocked();
+ for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
+ final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
+ if (service.requestImeApis()) {
+ service.bindInputLocked(binding);
+ }
+ }
+ }
+ }
+
+ /**
+ * Unbind input for accessibility services which request ime capabilities.
+ */
+ public void unbindInput() {
+ AccessibilityUserState userState;
+ // TODO(b/218182733): Resolve the Imf lock and mLock possible deadlock
+ synchronized (mLock) {
+ userState = getCurrentUserStateLocked();
+ for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
+ final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
+ if (service.requestImeApis()) {
+ service.unbindInputLocked();
+ }
+ }
+ }
+ }
+
+ /**
+ * Start input for accessibility services which request ime capabilities.
+ */
+ public void startInput(IBinder startInputToken, IInputContext inputContext,
+ EditorInfo editorInfo, boolean restarting) {
+ AccessibilityUserState userState;
+ synchronized (mLock) {
+ // Keep records of these in case new Accessibility Services are enabled.
+ mStartInputToken = startInputToken;
+ mInputContext = inputContext;
+ mEditorInfo = editorInfo;
+ mRestarting = restarting;
+ userState = getCurrentUserStateLocked();
+ for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
+ final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
+ if (service.requestImeApis()) {
+ service.startInputLocked(startInputToken, inputContext, editorInfo, restarting);
+ }
+ }
+ }
+ }
+
+ /**
+ * Request input sessions from all accessibility services which request ime capabilities and
+ * whose id is not in the ignoreSet
+ */
+ public void createImeSession(ArraySet<Integer> ignoreSet) {
+ AccessibilityUserState userState;
+ synchronized (mLock) {
+ mInputSessionRequested = true;
+ userState = getCurrentUserStateLocked();
+ for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
+ final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
+ if ((!ignoreSet.contains(service.mId)) && service.requestImeApis()) {
+ service.createImeSessionLocked();
+ }
+ }
+ }
+ }
+
+ /**
+ * Enable or disable the sessions.
+ *
+ * @param sessions Sessions to enable or disable.
+ * @param enabled True if enable the sessions or false if disable the sessions.
+ */
+ public void setImeSessionEnabled(SparseArray<IInputMethodSession> sessions, boolean enabled) {
+ AccessibilityUserState userState;
+ synchronized (mLock) {
+ userState = getCurrentUserStateLocked();
+ for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
+ final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
+ if (sessions.contains(service.mId) && service.requestImeApis()) {
+ service.setImeSessionEnabledLocked(sessions.get(service.mId), enabled);
+ }
+ }
+ }
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 8f7260f..0631028 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -127,6 +127,9 @@
}
public void unbindLocked() {
+ if (requestImeApis()) {
+ mSystemSupport.unbindImeLocked(this);
+ }
mContext.unbindService(this);
AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState == null) return;
@@ -188,6 +191,9 @@
// the new configuration (for example, initializing the input filter).
mMainHandler.sendMessage(obtainMessage(
AccessibilityServiceConnection::initializeService, this));
+ if (requestImeApis()) {
+ mSystemSupport.requestImeLocked(this);
+ }
}
}
@@ -371,6 +377,9 @@
if (!isConnectedLocked()) {
return;
}
+ if (requestImeApis()) {
+ mSystemSupport.unbindImeLocked(this);
+ }
mAccessibilityServiceInfo.crashed = true;
AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState != null) {
@@ -512,6 +521,10 @@
mMainHandler.sendMessage(msg);
}
+ public boolean requestImeApis() {
+ return mRequestImeApis;
+ }
+
private void notifyMotionEventInternal(MotionEvent event) {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
diff --git a/services/accessibility/java/com/android/server/accessibility/CaptioningManagerImpl.java b/services/accessibility/java/com/android/server/accessibility/CaptioningManagerImpl.java
index 39780d2..0fc6c8d 100644
--- a/services/accessibility/java/com/android/server/accessibility/CaptioningManagerImpl.java
+++ b/services/accessibility/java/com/android/server/accessibility/CaptioningManagerImpl.java
@@ -40,7 +40,7 @@
* @param userId The user Id.
*/
@Override
- public void setSystemAudioCaptioningRequested(boolean isEnabled, int userId) {
+ public void setSystemAudioCaptioningEnabled(boolean isEnabled, int userId) {
final long identity = Binder.clearCallingIdentity();
try {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
@@ -57,7 +57,7 @@
* @return the system audio caption UI enabled state.
*/
@Override
- public boolean isSystemAudioCaptioningUiRequested(int userId) {
+ public boolean isSystemAudioCaptioningUiEnabled(int userId) {
final long identity = Binder.clearCallingIdentity();
try {
return Settings.Secure.getIntForUser(mContext.getContentResolver(),
@@ -75,7 +75,7 @@
* @param userId The user Id.
*/
@Override
- public void setSystemAudioCaptioningUiRequested(boolean isEnabled, int userId) {
+ public void setSystemAudioCaptioningUiEnabled(boolean isEnabled, int userId) {
final long identity = Binder.clearCallingIdentity();
try {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
diff --git a/services/api/current.txt b/services/api/current.txt
index dcf7e64..e46c972 100644
--- a/services/api/current.txt
+++ b/services/api/current.txt
@@ -38,8 +38,8 @@
package com.android.server.am {
public interface ActivityManagerLocal {
+ method public boolean bindSupplementalProcessService(@NonNull android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull String, int) throws android.os.RemoteException;
method public boolean canStartForegroundService(int, int, @NonNull String);
- method public boolean startAndBindSupplementalProcessService(@NonNull android.content.Intent, @NonNull android.content.ServiceConnection, int) throws android.os.TransactionTooLargeException;
}
}
diff --git a/services/core/java/com/android/server/AccessibilityManagerInternal.java b/services/core/java/com/android/server/AccessibilityManagerInternal.java
new file mode 100644
index 0000000..28f6db1
--- /dev/null
+++ b/services/core/java/com/android/server/AccessibilityManagerInternal.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 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;
+
+import android.annotation.NonNull;
+import android.os.IBinder;
+import android.util.ArraySet;
+import android.util.SparseArray;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputBinding;
+
+import com.android.internal.view.IInputContext;
+import com.android.internal.view.IInputMethodSession;
+
+/**
+ * Accessibility manager local system service interface.
+ */
+public abstract class AccessibilityManagerInternal {
+ /** Enable or disable the sessions. */
+ public abstract void setImeSessionEnabled(SparseArray<IInputMethodSession> sessions,
+ boolean enabled);
+
+ /** Unbind input for all accessibility services which require ime capabilities. */
+ public abstract void unbindInput();
+
+ /** Bind input for all accessibility services which require ime capabilities. */
+ public abstract void bindInput(InputBinding binding);
+
+ /**
+ * Request input session from all accessibility services which require ime capabilities and
+ * whose id is not in the ignoreSet.
+ */
+ public abstract void createImeSession(ArraySet<Integer> ignoreSet);
+
+ /** Start input for all accessibility services which require ime capabilities. */
+ public abstract void startInput(IBinder startInputToken, IInputContext inputContext,
+ EditorInfo editorInfo, boolean restarting);
+
+ private static final AccessibilityManagerInternal NOP = new AccessibilityManagerInternal() {
+ @Override
+ public void setImeSessionEnabled(SparseArray<IInputMethodSession> sessions,
+ boolean enabled) {
+ }
+
+ @Override
+ public void unbindInput() {
+ }
+
+ @Override
+ public void bindInput(InputBinding binding) {
+ }
+
+ @Override
+ public void createImeSession(ArraySet<Integer> ignoreSet) {
+ }
+
+ @Override
+ public void startInput(IBinder startInputToken, IInputContext inputContext,
+ EditorInfo editorInfo, boolean restarting) {
+ }
+ };
+
+ /**
+ * @return Global instance if exists. Otherwise, a fallback no-op instance.
+ */
+ @NonNull
+ public static AccessibilityManagerInternal get() {
+ final AccessibilityManagerInternal instance =
+ LocalServices.getService(AccessibilityManagerInternal.class);
+ return instance != null ? instance : NOP;
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 9353dd8..dc64d80 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3411,9 +3411,11 @@
final Intent.FilterComparison filter
= new Intent.FilterComparison(service.cloneFilter());
final ServiceRestarter res = new ServiceRestarter();
+ String supplementalProcessName = isSupplementalProcessService ? instanceName
+ : null;
r = new ServiceRecord(mAm, className, name, definingPackageName,
definingUid, filter, sInfo, callingFromFg, res,
- isSupplementalProcessService);
+ supplementalProcessName);
res.setService(r);
smap.mServicesByInstanceName.put(name, r);
smap.mServicesByIntent.put(filter, r);
diff --git a/services/core/java/com/android/server/am/ActivityManagerLocal.java b/services/core/java/com/android/server/am/ActivityManagerLocal.java
index d9ee7d9..535340b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerLocal.java
+++ b/services/core/java/com/android/server/am/ActivityManagerLocal.java
@@ -17,10 +17,12 @@
package com.android.server.am;
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.os.TransactionTooLargeException;
+import android.os.RemoteException;
/**
* Interface for in-process calls into
@@ -63,22 +65,28 @@
void tempAllowWhileInUsePermissionInFgs(int uid, long durationMs);
/**
- * Starts a supplemental process service and binds to it. You can through the arguments here
- * have the system bring up multiple concurrent processes hosting their own instance of that
- * service. The <var>userAppUid</var> you provide here identifies the different instances - each
- * unique uid is attributed to a supplemental process.
+ * Binds to a supplemental process service, creating it if needed. You can through the arguments
+ * here have the system bring up multiple concurrent processes hosting their own instance of
+ * that service. The {@code processName} you provide here identifies the different instances.
*
* @param service Identifies the supplemental process service to connect to. The Intent must
* specify an explicit component name. This value cannot be null.
* @param conn Receives information as the service is started and stopped.
* This must be a valid ServiceConnection object; it must not be null.
* @param userAppUid Uid of the app for which the supplemental process needs to be spawned.
+ * @param processName Unique identifier for the service instance. Each unique name here will
+ * result in a different service instance being created. Identifiers must only contain
+ * ASCII letters, digits, underscores, and periods.
+ * @param flags Operation options provided by Context class for the binding.
* @return {@code true} if the system is in the process of bringing up a
* service that your client has permission to bind to; {@code false}
* if the system couldn't find the service or if your client doesn't
* have permission to bind to it.
+ * @throws RemoteException If the service could not be brought up.
+ * @see Context#bindService(Intent, ServiceConnection, int)
*/
- boolean startAndBindSupplementalProcessService(@NonNull Intent service,
- @NonNull ServiceConnection conn, int userAppUid) throws TransactionTooLargeException;
-
+ @SuppressLint("RethrowRemoteException")
+ boolean bindSupplementalProcessService(@NonNull Intent service, @NonNull ServiceConnection conn,
+ int userAppUid, @NonNull String processName, @Context.BindServiceFlags int flags)
+ throws RemoteException;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5cc163d..7f64c00 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -458,7 +458,7 @@
* broadcasts
*/
private static final boolean ENFORCE_DYNAMIC_RECEIVER_EXPLICIT_EXPORT =
- SystemProperties.getBoolean("fw.enforce_dynamic_receiver_explicit_export", false);
+ SystemProperties.getBoolean("fw.enforce_dynamic_receiver_explicit_export", true);
static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityManagerService" : TAG_AM;
static final String TAG_BACKUP = TAG + POSTFIX_BACKUP;
@@ -15951,14 +15951,17 @@
}
@Override
- public boolean startAndBindSupplementalProcessService(Intent service,
- ServiceConnection conn, int userAppUid) throws TransactionTooLargeException {
+ public boolean bindSupplementalProcessService(Intent service, ServiceConnection conn,
+ int userAppUid, String processName, int flags) throws RemoteException {
if (service == null) {
throw new IllegalArgumentException("intent is null");
}
if (conn == null) {
throw new IllegalArgumentException("connection is null");
}
+ if (processName == null) {
+ throw new IllegalArgumentException("processName is null");
+ }
if (service.getComponent() == null) {
throw new IllegalArgumentException("service must specify explicit component");
}
@@ -15967,15 +15970,14 @@
}
Handler handler = mContext.getMainThreadHandler();
- int flags = Context.BIND_AUTO_CREATE;
final IServiceConnection sd = mContext.getServiceDispatcher(conn, handler, flags);
service.prepareToLeaveProcess(mContext);
return ActivityManagerService.this.bindServiceInstance(
- mContext.getIApplicationThread(), mContext.getActivityToken(), service,
- service.resolveTypeIfNeeded(mContext.getContentResolver()), sd, flags,
- Integer.toString(userAppUid), /*isSupplementalProcessService*/ true,
- mContext.getOpPackageName(), UserHandle.getUserId(userAppUid)) != 0;
+ mContext.getIApplicationThread(), mContext.getActivityToken(), service,
+ service.resolveTypeIfNeeded(mContext.getContentResolver()), sd, flags,
+ processName, /*isSupplementalProcessService*/ true, mContext.getOpPackageName(),
+ UserHandle.getUserId(userAppUid)) != 0;
}
@Override
@@ -16119,23 +16121,6 @@
}
/**
- * Returns package name by pid.
- */
- @Override
- @Nullable
- public String getPackageNameByPid(int pid) {
- synchronized (mPidsSelfLocked) {
- final ProcessRecord app = mPidsSelfLocked.get(pid);
-
- if (app != null && app.info != null) {
- return app.info.packageName;
- }
-
- return null;
- }
- }
-
- /**
* Sets if the given pid has an overlay UI or not.
*
* @param pid The pid we are setting overlay UI for.
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 2f7249e..91822ac 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -79,7 +79,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
-import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.BatteryUsageStatsProvider;
import com.android.internal.os.BatteryUsageStatsStore;
@@ -2488,7 +2487,7 @@
final long ident = Binder.clearCallingIdentity();
try {
- if (BatteryStatsHelper.checkWifiOnly(mContext)) {
+ if (BatteryStats.checkWifiOnly(mContext)) {
flags |= BatteryStats.DUMP_DEVICE_WIFI_ONLY;
}
awaitCompletion();
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index bdfd02e..6c9187a 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2491,6 +2491,7 @@
mCachedAppOptimizer.onOomAdjustChanged(state.getSetAdj(), state.getCurAdj(), app);
} else if (mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE
&& state.getSetAdj() < ProcessList.FOREGROUND_APP_ADJ
+ && !state.isRunningRemoteAnimation()
// Because these can fire independent of oom_adj/procstate changes, we need
// to throttle the actual dispatch of these requests in addition to the
// processing of the requests. As a result, there is throttling both here
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index da78e2d..d3b5752 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -571,13 +571,13 @@
Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg,
Runnable restarter) {
this(ams, name, instanceName, definingPackageName, definingUid, intent, sInfo, callerIsFg,
- restarter, false);
+ restarter, null);
}
ServiceRecord(ActivityManagerService ams, ComponentName name,
ComponentName instanceName, String definingPackageName, int definingUid,
Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg,
- Runnable restarter, boolean isSupplementalProcessService) {
+ Runnable restarter, String supplementalProcessName) {
this.ams = ams;
this.name = name;
this.instanceName = instanceName;
@@ -588,9 +588,10 @@
serviceInfo = sInfo;
appInfo = sInfo.applicationInfo;
packageName = sInfo.applicationInfo.packageName;
- if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0
- || isSupplementalProcessService) {
+ if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) {
processName = sInfo.processName + ":" + instanceName.getClassName();
+ } else if (supplementalProcessName != null) {
+ processName = supplementalProcessName;
} else {
processName = sInfo.processName;
}
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
index 6982513..ab4d856 100644
--- a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
@@ -20,22 +20,30 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
import android.app.AppGlobals;
import android.app.BroadcastOptions;
import android.app.PendingIntent;
import android.app.ambientcontext.AmbientContextEvent;
import android.app.ambientcontext.AmbientContextEventRequest;
-import android.app.ambientcontext.AmbientContextEventResponse;
import android.app.ambientcontext.AmbientContextManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
import android.content.pm.ServiceInfo;
import android.os.Binder;
+import android.os.Bundle;
import android.os.RemoteCallback;
import android.os.RemoteException;
-import android.service.ambientcontext.AmbientContextDetectionService;
+import android.provider.Settings;
+import android.service.ambientcontext.AmbientContextDetectionResult;
+import android.service.ambientcontext.AmbientContextDetectionServiceStatus;
+import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -44,7 +52,8 @@
import com.android.server.infra.AbstractPerUserSystemService;
import java.io.PrintWriter;
-import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Set;
/**
@@ -60,14 +69,12 @@
RemoteAmbientContextDetectionService mRemoteService;
private ComponentName mComponentName;
- private Context mContext;
private Set<PendingIntent> mExistingPendingIntents;
AmbientContextManagerPerUserService(
@NonNull AmbientContextManagerService master, Object lock, @UserIdInt int userId) {
super(master, lock, userId);
- mContext = master.getContext();
- mExistingPendingIntents = new HashSet<>();
+ mExistingPendingIntents = new ArraySet<>();
}
void destroyLocked() {
@@ -154,12 +161,13 @@
* package. A new registration from the same package will overwrite the previous registration.
*/
public void onRegisterObserver(AmbientContextEventRequest request,
- PendingIntent pendingIntent) {
+ PendingIntent pendingIntent, RemoteCallback clientStatusCallback) {
synchronized (mLock) {
if (!setUpServiceIfNeeded()) {
- Slog.w(TAG, "Service is not available at this moment.");
- sendStatusUpdateIntent(
- pendingIntent, AmbientContextEventResponse.STATUS_SERVICE_UNAVAILABLE);
+ Slog.w(TAG, "Detection service is not available at this moment.");
+ sendStatusCallback(
+ clientStatusCallback,
+ AmbientContextManager.STATUS_SERVICE_UNAVAILABLE);
return;
}
@@ -173,29 +181,58 @@
}
// Register new package and add request to mExistingRequests
- startDetection(request, callingPackage, createRemoteCallback());
+ startDetection(request, callingPackage, createDetectionResultRemoteCallback(),
+ getServerStatusCallback(clientStatusCallback));
mExistingPendingIntents.add(pendingIntent);
}
}
+ /**
+ * Returns a RemoteCallback that handles the status from the detection service, and
+ * sends results to the client callback.
+ */
+ private RemoteCallback getServerStatusCallback(RemoteCallback clientStatusCallback) {
+ return new RemoteCallback(result -> {
+ AmbientContextDetectionServiceStatus serviceStatus =
+ (AmbientContextDetectionServiceStatus) result.get(
+ AmbientContextDetectionServiceStatus.STATUS_RESPONSE_BUNDLE_KEY);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ String packageName = serviceStatus.getPackageName();
+ Bundle bundle = new Bundle();
+ bundle.putInt(
+ AmbientContextManager.STATUS_RESPONSE_BUNDLE_KEY,
+ serviceStatus.getStatusCode());
+ clientStatusCallback.sendResult(bundle);
+ int statusCode = serviceStatus.getStatusCode();
+ Slog.i(TAG, "Got detection status of " + statusCode
+ + " for " + packageName);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ });
+ }
+
@VisibleForTesting
void startDetection(AmbientContextEventRequest request, String callingPackage,
- RemoteCallback callback) {
+ RemoteCallback detectionResultCallback, RemoteCallback statusCallback) {
Slog.d(TAG, "Requested detection of " + request.getEventTypes());
synchronized (mLock) {
ensureRemoteServiceInitiated();
- mRemoteService.startDetection(request, callingPackage, callback);
+ mRemoteService.startDetection(request, callingPackage, detectionResultCallback,
+ statusCallback);
}
}
/**
* Sends an intent with a status code and empty events.
*/
- void sendStatusUpdateIntent(PendingIntent pendingIntent, int statusCode) {
- AmbientContextEventResponse response = new AmbientContextEventResponse.Builder()
- .setStatusCode(statusCode)
- .build();
- sendResponseIntent(pendingIntent, response);
+ void sendStatusCallback(RemoteCallback statusCallback, int statusCode) {
+ Bundle bundle = new Bundle();
+ bundle.putInt(
+ AmbientContextManager.STATUS_RESPONSE_BUNDLE_KEY,
+ statusCode);
+ statusCallback.sendResult(bundle);
}
/**
@@ -217,6 +254,113 @@
}
}
+ public void onQueryServiceStatus(int[] eventTypes, String callingPackage,
+ RemoteCallback statusCallback) {
+ Slog.d(TAG, "Query event status of " + Arrays.toString(eventTypes)
+ + " for " + callingPackage);
+ synchronized (mLock) {
+ if (!setUpServiceIfNeeded()) {
+ Slog.w(TAG, "Detection service is not available at this moment.");
+ sendStatusToCallback(statusCallback,
+ AmbientContextManager.STATUS_SERVICE_UNAVAILABLE);
+ return;
+ }
+ ensureRemoteServiceInitiated();
+ mRemoteService.queryServiceStatus(
+ eventTypes,
+ callingPackage,
+ getServerStatusCallback(statusCallback));
+ }
+ }
+
+ public void onStartConsentActivity(int[] eventTypes, String callingPackage) {
+ Slog.d(TAG, "Opening consent activity of " + Arrays.toString(eventTypes)
+ + " for " + callingPackage);
+
+ // Look up the recent task from the callingPackage
+ ActivityManager.RecentTaskInfo task;
+ ParceledListSlice<ActivityManager.RecentTaskInfo> recentTasks;
+ int userId = getUserId();
+ try {
+ recentTasks = ActivityTaskManager.getService().getRecentTasks(/*maxNum*/1,
+ /*flags*/ 0, userId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to query recent tasks!");
+ return;
+ }
+
+ if ((recentTasks != null) || recentTasks.getList().isEmpty()) {
+ Slog.e(TAG, "Recent task list is empty!");
+ return;
+ }
+
+ task = recentTasks.getList().get(0);
+ if (!callingPackage.equals(task.topActivityInfo.packageName)) {
+ Slog.e(TAG, "Recent task package name: " + task.topActivityInfo.packageName
+ + " doesn't match with camera client package name: " + callingPackage);
+ return;
+ }
+
+ // Start activity as the same task from the callingPackage
+ Intent intent = new Intent();
+ ComponentName consentComponent = getConsentComponent();
+ if (consentComponent != null) {
+ Slog.e(TAG, "Starting consent activity for " + callingPackage);
+ Context context = getContext();
+ String packageNameExtraKey = Settings.Secure.getStringForUser(
+ context.getContentResolver(),
+ Settings.Secure.AMBIENT_CONTEXT_PACKAGE_NAME_EXTRA_KEY,
+ userId);
+ String eventArrayExtraKey = Settings.Secure.getStringForUser(
+ context.getContentResolver(),
+ Settings.Secure.AMBIENT_CONTEXT_EVENT_ARRAY_EXTRA_KEY,
+ userId);
+
+ // Create consent activity intent with the calling package name and requested events.
+ intent.setComponent(consentComponent);
+ if (packageNameExtraKey != null) {
+ intent.putExtra(packageNameExtraKey, callingPackage);
+ }
+ if (eventArrayExtraKey != null) {
+ intent.putExtra(eventArrayExtraKey, eventTypes);
+ }
+
+ // Set parent to the calling app's task
+ ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchTaskId(task.taskId);
+ context.startActivity(intent, options.toBundle());
+ } else {
+ Slog.e(TAG, "Consent component not found!");
+ }
+ }
+
+ /**
+ * Returns the consent activity component from config lookup.
+ */
+ private ComponentName getConsentComponent() {
+ Context context = getContext();
+ String consentComponent = Settings.Secure.getStringForUser(
+ context.getContentResolver(),
+ Settings.Secure.AMBIENT_CONTEXT_CONSENT_COMPONENT,
+ getUserId());
+ if (TextUtils.isEmpty(consentComponent)) {
+ return null;
+ }
+ return ComponentName.unflattenFromString(consentComponent);
+ }
+
+ /**
+ * Sends the result response with the specified status to the callback.
+ */
+ void sendStatusToCallback(RemoteCallback callback,
+ @AmbientContextManager.StatusCode int status) {
+ Bundle bundle = new Bundle();
+ bundle.putInt(
+ AmbientContextManager.STATUS_RESPONSE_BUNDLE_KEY,
+ status);
+ callback.sendResult(bundle);
+ }
+
@VisibleForTesting
void stopDetection(String packageName) {
Slog.d(TAG, "Stop detection for " + packageName);
@@ -240,12 +384,13 @@
* Sends out the Intent to the client after the event is detected.
*
* @param pendingIntent Client's PendingIntent for callback
- * @param response Response with status code and detection result
+ * @param result result from the detection service
*/
- private void sendResponseIntent(PendingIntent pendingIntent,
- AmbientContextEventResponse response) {
+ private void sendDetectionResultIntent(PendingIntent pendingIntent,
+ AmbientContextDetectionResult result) {
Intent intent = new Intent();
- intent.putExtra(AmbientContextManager.EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE, response);
+ intent.putExtra(AmbientContextManager.EXTRA_AMBIENT_CONTEXT_EVENTS,
+ new ArrayList(result.getEvents()));
// Explicitly disallow the receiver from starting activities, to prevent apps from utilizing
// the PendingIntent as a backdoor to do this.
BroadcastOptions options = BroadcastOptions.makeBasic();
@@ -254,38 +399,29 @@
pendingIntent.send(getContext(), 0, intent, null, null, null,
options.toBundle());
Slog.i(TAG, "Sending PendingIntent to " + pendingIntent.getCreatorPackage() + ": "
- + response);
+ + result);
} catch (PendingIntent.CanceledException e) {
Slog.w(TAG, "Couldn't deliver pendingIntent:" + pendingIntent);
}
}
@NonNull
- private RemoteCallback createRemoteCallback() {
+ private RemoteCallback createDetectionResultRemoteCallback() {
return new RemoteCallback(result -> {
- AmbientContextEventResponse response = (AmbientContextEventResponse) result.get(
- AmbientContextDetectionService.RESPONSE_BUNDLE_KEY);
+ AmbientContextDetectionResult detectionResult =
+ (AmbientContextDetectionResult) result.get(
+ AmbientContextDetectionResult.RESULT_RESPONSE_BUNDLE_KEY);
final long token = Binder.clearCallingIdentity();
try {
- Set<PendingIntent> pendingIntentForFailedRequests = new HashSet<>();
for (PendingIntent pendingIntent : mExistingPendingIntents) {
- // Send PendingIntent if a requesting package matches the response packages.
- if (response.getPackageName().equals(pendingIntent.getCreatorPackage())) {
- sendResponseIntent(pendingIntent, response);
-
- int statusCode = response.getStatusCode();
- if (statusCode != AmbientContextEventResponse.STATUS_SUCCESS) {
- pendingIntentForFailedRequests.add(pendingIntent);
- }
- Slog.i(TAG, "Got response of " + response.getEvents() + " for "
- + pendingIntent.getCreatorPackage() + ". Status: " + statusCode);
+ // Send PendingIntent to requesting packages
+ String creatorPackage = pendingIntent.getCreatorPackage();
+ if (detectionResult.getPackageName().equals(creatorPackage)) {
+ sendDetectionResultIntent(pendingIntent, detectionResult);
+ Slog.i(TAG, "Got detection result of " + detectionResult.getEvents()
+ + " for " + creatorPackage);
}
}
-
- // Removes the failed requests from the existing requests.
- for (PendingIntent pendingIntent : pendingIntentForFailedRequests) {
- mExistingPendingIntents.remove(pendingIntent);
- }
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
index 33905f2..2cdf7e7 100644
--- a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
@@ -24,8 +24,8 @@
import android.app.PendingIntent;
import android.app.ambientcontext.AmbientContextEvent;
import android.app.ambientcontext.AmbientContextEventRequest;
-import android.app.ambientcontext.AmbientContextEventResponse;
-import android.app.ambientcontext.IAmbientContextEventObserver;
+import android.app.ambientcontext.AmbientContextManager;
+import android.app.ambientcontext.IAmbientContextManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
@@ -76,7 +76,7 @@
@Override
public void onStart() {
- publishBinderService(Context.AMBIENT_CONTEXT_SERVICE, new AmbientContextEventObserver());
+ publishBinderService(Context.AMBIENT_CONTEXT_SERVICE, new AmbientContextManagerInternal());
}
@Override
@@ -128,14 +128,16 @@
* specified events. Intended for use by shell command for testing.
* Requires ACCESS_AMBIENT_CONTEXT_EVENT permission.
*/
- void startAmbientContextEvent(@UserIdInt int userId, AmbientContextEventRequest request,
- String packageName, RemoteCallback callback) {
+ void startDetection(@UserIdInt int userId, AmbientContextEventRequest request,
+ String packageName, RemoteCallback detectionResultCallback,
+ RemoteCallback statusCallback) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
synchronized (mLock) {
final AmbientContextManagerPerUserService service = getServiceForUserLocked(userId);
if (service != null) {
- service.startDetection(request, packageName, callback);
+ service.startDetection(request, packageName, detectionResultCallback,
+ statusCallback);
} else {
Slog.i(TAG, "service not available for user_id: " + userId);
}
@@ -161,6 +163,25 @@
}
/**
+ * Send request to the remote AmbientContextDetectionService impl to query the status of the
+ * specified events. Intended for use by shell command for testing.
+ * Requires ACCESS_AMBIENT_CONTEXT_EVENT permission.
+ */
+ void queryServiceStatus(@UserIdInt int userId, String packageName,
+ int[] eventTypes, RemoteCallback callback) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
+ synchronized (mLock) {
+ final AmbientContextManagerPerUserService service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.onQueryServiceStatus(eventTypes, packageName, callback);
+ } else {
+ Slog.i(TAG, "query service not available for user_id: " + userId);
+ }
+ }
+ }
+
+ /**
* Returns the AmbientContextManagerPerUserService component for this user.
*/
public ComponentName getComponentName(@UserIdInt int userId) {
@@ -173,34 +194,65 @@
return null;
}
- private final class AmbientContextEventObserver extends IAmbientContextEventObserver.Stub {
+ private final class AmbientContextManagerInternal extends IAmbientContextManager.Stub {
final AmbientContextManagerPerUserService mService = getServiceForUserLocked(
UserHandle.getCallingUserId());
@Override
public void registerObserver(
- AmbientContextEventRequest request, PendingIntent pendingIntent) {
+ AmbientContextEventRequest request, PendingIntent resultPendingIntent,
+ RemoteCallback statusCallback) {
Objects.requireNonNull(request);
- Objects.requireNonNull(pendingIntent);
+ Objects.requireNonNull(resultPendingIntent);
+ Objects.requireNonNull(statusCallback);
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
+ assertCalledByPackageOwner(resultPendingIntent.getCreatorPackage());
if (!mIsServiceEnabled) {
Slog.w(TAG, "Service not available.");
- mService.sendStatusUpdateIntent(pendingIntent,
- AmbientContextEventResponse.STATUS_SERVICE_UNAVAILABLE);
+ mService.sendStatusCallback(statusCallback,
+ AmbientContextManager.STATUS_SERVICE_UNAVAILABLE);
return;
}
- mService.onRegisterObserver(request, pendingIntent);
+ mService.onRegisterObserver(request, resultPendingIntent, statusCallback);
}
@Override
public void unregisterObserver(String callingPackage) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
+ assertCalledByPackageOwner(callingPackage);
mService.onUnregisterObserver(callingPackage);
}
@Override
+ public void queryServiceStatus(int[] eventTypes, String callingPackage,
+ RemoteCallback statusCallback) {
+ Objects.requireNonNull(eventTypes);
+ Objects.requireNonNull(callingPackage);
+ Objects.requireNonNull(statusCallback);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
+ assertCalledByPackageOwner(callingPackage);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Detection service not available.");
+ mService.sendStatusToCallback(statusCallback,
+ AmbientContextManager.STATUS_SERVICE_UNAVAILABLE);
+ return;
+ }
+ mService.onQueryServiceStatus(eventTypes, callingPackage,
+ statusCallback);
+ }
+
+ @Override
+ public void startConsentActivity(int[] eventTypes, String callingPackage) {
+ Objects.requireNonNull(eventTypes);
+ Objects.requireNonNull(callingPackage);
+ assertCalledByPackageOwner(callingPackage);
+ mService.onStartConsentActivity(eventTypes, callingPackage);
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) {
return;
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java b/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java
index b5cd985..010bf1b 100644
--- a/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java
@@ -21,12 +21,12 @@
import android.annotation.NonNull;
import android.app.ambientcontext.AmbientContextEvent;
import android.app.ambientcontext.AmbientContextEventRequest;
-import android.app.ambientcontext.AmbientContextEventResponse;
import android.content.ComponentName;
import android.os.Binder;
import android.os.RemoteCallback;
import android.os.ShellCommand;
-import android.service.ambientcontext.AmbientContextDetectionService;
+import android.service.ambientcontext.AmbientContextDetectionResult;
+import android.service.ambientcontext.AmbientContextDetectionServiceStatus;
import java.io.PrintWriter;
@@ -35,6 +35,12 @@
*/
final class AmbientContextShellCommand extends ShellCommand {
+ private static final AmbientContextEventRequest REQUEST =
+ new AmbientContextEventRequest.Builder()
+ .addEventType(AmbientContextEvent.EVENT_COUGH)
+ .addEventType(AmbientContextEvent.EVENT_SNORE)
+ .build();
+
@NonNull
private final AmbientContextManagerService mService;
@@ -44,22 +50,43 @@
/** Callbacks for AmbientContextEventService results used internally for testing. */
static class TestableCallbackInternal {
- private AmbientContextEventResponse mLastResponse;
+ private AmbientContextDetectionResult mLastResult;
+ private AmbientContextDetectionServiceStatus mLastStatus;
- public AmbientContextEventResponse getLastResponse() {
- return mLastResponse;
+ public AmbientContextDetectionResult getLastResult() {
+ return mLastResult;
+ }
+
+ public AmbientContextDetectionServiceStatus getLastStatus() {
+ return mLastStatus;
}
@NonNull
- private RemoteCallback createRemoteCallback() {
+ private RemoteCallback createRemoteDetectionResultCallback() {
return new RemoteCallback(result -> {
- AmbientContextEventResponse response =
- (AmbientContextEventResponse) result.get(
- AmbientContextDetectionService.RESPONSE_BUNDLE_KEY);
+ AmbientContextDetectionResult detectionResult =
+ (AmbientContextDetectionResult) result.get(
+ AmbientContextDetectionResult.RESULT_RESPONSE_BUNDLE_KEY);
final long token = Binder.clearCallingIdentity();
try {
- mLastResponse = response;
- out.println("Response available: " + response);
+ mLastResult = detectionResult;
+ out.println("Detection result available: " + detectionResult);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ });
+ }
+
+ @NonNull
+ private RemoteCallback createRemoteStatusCallback() {
+ return new RemoteCallback(result -> {
+ AmbientContextDetectionServiceStatus status =
+ (AmbientContextDetectionServiceStatus) result.get(
+ AmbientContextDetectionServiceStatus.STATUS_RESPONSE_BUNDLE_KEY);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mLastStatus = status;
+ out.println("Status available: " + status);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -83,6 +110,8 @@
return runStopDetection();
case "get-last-status-code":
return getLastStatusCode();
+ case "query-service-status":
+ return runQueryServiceStatus();
case "get-bound-package":
return getBoundPackageName();
case "set-temporary-service":
@@ -95,13 +124,9 @@
private int runStartDetection() {
final int userId = Integer.parseInt(getNextArgRequired());
final String packageName = getNextArgRequired();
- AmbientContextEventRequest request = new AmbientContextEventRequest.Builder()
- .addEventType(AmbientContextEvent.EVENT_COUGH)
- .addEventType(AmbientContextEvent.EVENT_SNORE)
- .build();
-
- mService.startAmbientContextEvent(userId, request, packageName,
- sTestableCallbackInternal.createRemoteCallback());
+ mService.startDetection(userId, REQUEST, packageName,
+ sTestableCallbackInternal.createRemoteDetectionResultCallback(),
+ sTestableCallbackInternal.createRemoteStatusCallback());
return 0;
}
@@ -112,8 +137,20 @@
return 0;
}
+ private int runQueryServiceStatus() {
+ final int userId = Integer.parseInt(getNextArgRequired());
+ final String packageName = getNextArgRequired();
+ int[] types = new int[] {
+ AmbientContextEvent.EVENT_COUGH,
+ AmbientContextEvent.EVENT_SNORE};
+ mService.queryServiceStatus(userId, packageName, types,
+ sTestableCallbackInternal.createRemoteStatusCallback());
+ return 0;
+ }
+
private int getLastStatusCode() {
- AmbientContextEventResponse lastResponse = sTestableCallbackInternal.getLastResponse();
+ AmbientContextDetectionServiceStatus lastResponse =
+ sTestableCallbackInternal.getLastStatus();
if (lastResponse == null) {
return -1;
}
@@ -130,6 +167,7 @@
pw.println(" start-detection USER_ID PACKAGE_NAME: Starts AmbientContextEvent detection.");
pw.println(" stop-detection USER_ID: Stops AmbientContextEvent detection.");
pw.println(" get-last-status-code: Prints the latest request status code.");
+ pw.println(" query-event-status USER_ID PACKAGE_NAME: Prints the event status code.");
pw.println(" get-bound-package USER_ID:"
+ " Print the bound package that implements the service.");
pw.println(" set-temporary-service USER_ID [COMPONENT_NAME DURATION]");
diff --git a/services/core/java/com/android/server/ambientcontext/RemoteAmbientContextDetectionService.java b/services/core/java/com/android/server/ambientcontext/RemoteAmbientContextDetectionService.java
index 5cc29b3..f42080d 100644
--- a/services/core/java/com/android/server/ambientcontext/RemoteAmbientContextDetectionService.java
+++ b/services/core/java/com/android/server/ambientcontext/RemoteAmbientContextDetectionService.java
@@ -20,6 +20,7 @@
import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
import android.annotation.NonNull;
+import android.app.ambientcontext.AmbientContextEvent;
import android.app.ambientcontext.AmbientContextEventRequest;
import android.content.ComponentName;
import android.content.Context;
@@ -53,13 +54,15 @@
*
* @param request The request with events to detect, and optional detection options.
* @param packageName The app package that requested the detection
- * @param callback callback for detection results
+ * @param detectionResultCallback callback for detection results
+ * @param statusCallback callback for service status
*/
public void startDetection(
@NonNull AmbientContextEventRequest request, String packageName,
- RemoteCallback callback) {
+ RemoteCallback detectionResultCallback, RemoteCallback statusCallback) {
Slog.i(TAG, "Start detection for " + request.getEventTypes());
- post(service -> service.startDetection(request, packageName, callback));
+ post(service -> service.startDetection(request, packageName, detectionResultCallback,
+ statusCallback));
}
/**
@@ -71,4 +74,15 @@
Slog.i(TAG, "Stop detection for " + packageName);
post(service -> service.stopDetection(packageName));
}
+
+ /**
+ * Asks the implementation to return the event status for the package.
+ */
+ public void queryServiceStatus(
+ @AmbientContextEvent.EventCode int[] eventTypes,
+ String packageName,
+ RemoteCallback callback) {
+ Slog.i(TAG, "Query status for " + packageName);
+ post(service -> service.queryServiceStatus(eventTypes, packageName, callback));
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index b814782..b2f500a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -422,7 +422,7 @@
addFreshWindowToken();
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
- null, null, mCurId, mCurSeq, false);
+ null, null, null, mCurId, mCurSeq, false);
}
Slog.w(InputMethodManagerService.TAG,
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index 80c83e9..29dcdfa 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -25,6 +25,7 @@
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
+import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.InlineSuggestionsRequestInfo;
import com.android.server.LocalServices;
@@ -149,6 +150,24 @@
public abstract void updateImeWindowStatus(boolean disableImeIcon);
/**
+ * Callback when the IInputMethodSession from the accessibility service with the specified
+ * accessibilityConnectionId is created.
+ *
+ * @param accessibilityConnectionId The connection id of the accessibility service.
+ * @param session The session passed back from the accessibility service.
+ */
+ public abstract void onSessionForAccessibilityCreated(int accessibilityConnectionId,
+ IInputMethodSession session);
+
+ /**
+ * Unbind the accessibility service with the specified accessibilityConnectionId from current
+ * client.
+ *
+ * @param accessibilityConnectionId The connection id of the accessibility service.
+ */
+ public abstract void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId);
+
+ /**
* Fake implementation of {@link InputMethodManagerInternal}. All the methods do nothing.
*/
private static final InputMethodManagerInternal NOP =
@@ -211,6 +230,15 @@
@Override
public void updateImeWindowStatus(boolean disableImeIcon) {
}
+
+ @Override
+ public void onSessionForAccessibilityCreated(int accessibilityConnectionId,
+ IInputMethodSession session) {
+ }
+
+ @Override
+ public void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId) {
+ }
};
/**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index ba15a04..7068ed1 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -94,6 +94,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.DeadObjectException;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
@@ -120,6 +121,7 @@
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import android.view.IWindowManager;
import android.view.InputChannel;
@@ -171,6 +173,7 @@
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.IInputSessionCallback;
import com.android.internal.view.InlineSuggestionsRequestInfo;
+import com.android.server.AccessibilityManagerInternal;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
@@ -228,7 +231,9 @@
private static final int MSG_START_HANDWRITING = 1100;
private static final int MSG_UNBIND_CLIENT = 3000;
+ private static final int MSG_UNBIND_ACCESSIBILITY_SERVICE = 3001;
private static final int MSG_BIND_CLIENT = 3010;
+ private static final int MSG_BIND_ACCESSIBILITY_SERVICE = 3011;
private static final int MSG_SET_ACTIVE = 3020;
private static final int MSG_SET_INTERACTIVE = 3030;
private static final int MSG_REPORT_FULLSCREEN_MODE = 3045;
@@ -351,6 +356,33 @@
}
}
+ /**
+ * Record session state for an accessibility service.
+ */
+ private static class AccessibilitySessionState {
+ final ClientState mClient;
+ // Id of the accessibility service.
+ final int mId;
+
+ public IInputMethodSession mSession;
+
+ @Override
+ public String toString() {
+ return "AccessibilitySessionState{uid " + mClient.uid + " pid " + mClient.pid
+ + " id " + Integer.toHexString(mId)
+ + " session " + Integer.toHexString(
+ System.identityHashCode(mSession))
+ + "}";
+ }
+
+ AccessibilitySessionState(ClientState client, int id,
+ IInputMethodSession session) {
+ mClient = client;
+ mId = id;
+ mSession = session;
+ }
+ }
+
private static final class ClientDeathRecipient implements IBinder.DeathRecipient {
private final InputMethodManagerService mImms;
private final IInputMethodClient mClient;
@@ -376,7 +408,9 @@
final ClientDeathRecipient clientDeathRecipient;
boolean sessionRequested;
+ boolean mSessionRequestedForAccessibility;
SessionState curSession;
+ SparseArray<AccessibilitySessionState> mAccessibilitySessions = new SparseArray<>();
@Override
public String toString() {
@@ -637,10 +671,16 @@
boolean mBoundToMethod;
/**
+ * Have we called bindInput() for accessibility services?
+ */
+ boolean mBoundToAccessibility;
+
+ /**
* Currently enabled session.
*/
@GuardedBy("ImfLock.class")
SessionState mEnabledSession;
+ SparseArray<AccessibilitySessionState> mEnabledAccessibilitySessions = new SparseArray<>();
/**
* True if the device is currently interactive with user. The value is true initially.
@@ -2188,6 +2228,7 @@
if (cs != null) {
client.asBinder().unlinkToDeath(cs.clientDeathRecipient, 0);
clearClientSessionLocked(cs);
+ clearClientSessionForAccessibilityLocked(cs);
if (mCurClient == cs) {
hideCurrentInputLocked(
mCurFocusedWindow, 0, null, SoftInputShowHideReason.HIDE_REMOVE_CLIENT);
@@ -2195,9 +2236,13 @@
mBoundToMethod = false;
IInputMethodInvoker curMethod = getCurMethodLocked();
if (curMethod != null) {
+ // When we unbind input, we are unbinding the client, so we always
+ // unbind ime and a11y together.
curMethod.unbindInput();
+ AccessibilityManagerInternal.get().unbindInput();
}
}
+ mBoundToAccessibility = false;
mCurClient = null;
}
if (mCurFocusedWindowClient == cs) {
@@ -2216,6 +2261,24 @@
}
@NonNull
+ private Message obtainMessageOOO(int what, Object arg1, Object arg2, Object arg3) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = arg1;
+ args.arg2 = arg2;
+ args.arg3 = arg3;
+ return mHandler.obtainMessage(what, 0, 0, args);
+ }
+
+ @NonNull
+ private Message obtainMessageIIOO(int what, int arg1, int arg2,
+ Object arg3, Object arg4) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = arg3;
+ args.arg2 = arg4;
+ return mHandler.obtainMessage(what, arg1, arg2, args);
+ }
+
+ @NonNull
private Message obtainMessageIIIO(int what, int argi1, int argi2, int argi3, Object arg1) {
final SomeArgs args = SomeArgs.obtain();
args.arg1 = arg1;
@@ -2252,13 +2315,18 @@
curMethod.unbindInput();
}
}
+ mBoundToAccessibility = false;
+ // Since we set active false to current client and set mCurClient to null, let's unbind
+ // all accessibility too. That means, when input method get disconnected (including
+ // switching ime), we also unbind accessibility
scheduleSetActiveToClient(mCurClient, false /* active */, false /* fullscreen */,
false /* reportToImeController */);
executeOrSendMessage(mCurClient.client, mHandler.obtainMessage(
MSG_UNBIND_CLIENT, getSequenceNumberLocked(), unbindClientReason,
mCurClient.client));
mCurClient.sessionRequested = false;
+ mCurClient.mSessionRequestedForAccessibility = false;
mCurClient = null;
mMenuController.hideInputMethodMenuLocked();
@@ -2340,11 +2408,63 @@
final InputMethodInfo curInputMethodInfo = mMethodMap.get(curId);
final boolean suppressesSpellChecker =
curInputMethodInfo != null && curInputMethodInfo.suppressesSpellChecker();
+ final SparseArray<IInputMethodSession> accessibilityInputMethodSessions =
+ createAccessibilityInputMethodSessions(mCurClient.mAccessibilitySessions);
return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
- session.session, (session.channel != null ? session.channel.dup() : null),
+ session.session, accessibilityInputMethodSessions,
+ (session.channel != null ? session.channel.dup() : null),
curId, getSequenceNumberLocked(), suppressesSpellChecker);
}
+ @GuardedBy("ImfLock.class")
+ @Nullable
+ InputBindResult attachNewAccessibilityLocked(@StartInputReason int startInputReason,
+ boolean initial, int id) {
+ if (!mBoundToAccessibility) {
+ AccessibilityManagerInternal.get().bindInput(mCurClient.binding);
+ mBoundToAccessibility = true;
+ }
+
+ // TODO(b/187453053): grantImplicitAccess to accessibility services access? if so, need to
+ // record accessibility services uid.
+
+ final AccessibilitySessionState accessibilitySession =
+ mCurClient.mAccessibilitySessions.get(id);
+ // We don't start input when session for a11y is created. We start input when
+ // input method start input, a11y manager service is always on.
+ if (startInputReason != StartInputReason.SESSION_CREATED_BY_ACCESSIBILITY) {
+ final Binder startInputToken = new Binder();
+ setEnabledSessionForAccessibilityLocked(mCurClient.mAccessibilitySessions);
+ AccessibilityManagerInternal.get().startInput(startInputToken, mCurInputContext,
+ mCurAttribute, !initial /* restarting */);
+ }
+
+ if (accessibilitySession != null) {
+ final SessionState session = mCurClient.curSession;
+ IInputMethodSession imeSession = session == null ? null : session.session;
+ final SparseArray<IInputMethodSession> accessibilityInputMethodSessions =
+ createAccessibilityInputMethodSessions(mCurClient.mAccessibilitySessions);
+ return new InputBindResult(
+ InputBindResult.ResultCode.SUCCESS_WITH_ACCESSIBILITY_SESSION,
+ imeSession, accessibilityInputMethodSessions, null,
+ getCurIdLocked(), getSequenceNumberLocked(), false);
+ }
+ return null;
+ }
+
+ private SparseArray<IInputMethodSession> createAccessibilityInputMethodSessions(
+ SparseArray<AccessibilitySessionState> accessibilitySessions) {
+ final SparseArray<IInputMethodSession> accessibilityInputMethodSessions =
+ new SparseArray<>();
+ if (accessibilitySessions != null) {
+ for (int i = 0; i < accessibilitySessions.size(); i++) {
+ accessibilityInputMethodSessions.append(accessibilitySessions.keyAt(i),
+ accessibilitySessions.valueAt(i).mSession);
+ }
+ }
+ return accessibilityInputMethodSessions;
+ }
+
/**
* Called by {@link #startInputOrWindowGainedFocusInternalLocked} to bind/unbind/attach the
* selected InputMethod to the given focused IME client.
@@ -2370,7 +2490,7 @@
// party code.
return new InputBindResult(
InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
- null, null, selectedMethodId, getSequenceNumberLocked(), false);
+ null, null, null, selectedMethodId, getSequenceNumberLocked(), false);
}
if (!InputMethodUtils.checkIfPackageBelongsToUid(mAppOpsManager, cs.uid,
@@ -2422,6 +2542,17 @@
if (cs.curSession != null) {
// Fast case: if we are already connected to the input method,
// then just return it.
+ // This doesn't mean a11y sessions are there. When a11y service is
+ // enabled while this client is switched out, this client doesn't have the session.
+ // A11yManagerService will only request missing sessions (will not request existing
+ // sessions again). Note when an a11y service is disabled, it will clear its
+ // session from all clients, so we don't need to worry about disabled a11y services.
+ cs.mSessionRequestedForAccessibility = false;
+ requestClientSessionForAccessibilityLocked(cs);
+ // we can always attach to accessibility because AccessibilityManagerService is
+ // always on.
+ attachNewAccessibilityLocked(startInputReason,
+ (startInputFlags & StartInputFlags.INITIAL_CONNECTION) != 0, -1);
return attachNewInputLocked(startInputReason,
(startInputFlags & StartInputFlags.INITIAL_CONNECTION) != 0);
}
@@ -2464,9 +2595,10 @@
// Return to client, and we will get back with it when
// we have had a session made for it.
requestClientSessionLocked(cs);
+ requestClientSessionForAccessibilityLocked(cs);
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
- null, null, getCurIdLocked(), getSequenceNumberLocked(), false);
+ null, null, null, getCurIdLocked(), getSequenceNumberLocked(), false);
} else {
long bindingDuration = SystemClock.uptimeMillis() - getLastBindTimeLocked();
if (bindingDuration < TIME_TO_RECONNECT) {
@@ -2479,7 +2611,7 @@
// to see if we can get back in touch with the service.
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
- null, null, getCurIdLocked(), getSequenceNumberLocked(), false);
+ null, null, null, getCurIdLocked(), getSequenceNumberLocked(), false);
} else {
EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
getSelectedMethodIdLocked(), bindingDuration, 0);
@@ -2565,6 +2697,8 @@
method, session, channel);
InputBindResult res = attachNewInputLocked(
StartInputReason.SESSION_CREATED_BY_IME, true);
+ attachNewAccessibilityLocked(StartInputReason.SESSION_CREATED_BY_IME,
+ true, -1);
if (res.method != null) {
executeOrSendMessage(mCurClient.client, obtainMessageOO(
MSG_BIND_CLIENT, mCurClient.client, res));
@@ -2602,7 +2736,9 @@
void reRequestCurrentClientSessionLocked() {
if (mCurClient != null) {
clearClientSessionLocked(mCurClient);
+ clearClientSessionForAccessibilityLocked(mCurClient);
requestClientSessionLocked(mCurClient);
+ requestClientSessionForAccessibilityLocked(mCurClient);
}
}
@@ -2646,6 +2782,19 @@
}
@GuardedBy("ImfLock.class")
+ void requestClientSessionForAccessibilityLocked(ClientState cs) {
+ if (!cs.mSessionRequestedForAccessibility) {
+ if (DEBUG) Slog.v(TAG, "Creating new accessibility sessions for client " + cs);
+ cs.mSessionRequestedForAccessibility = true;
+ ArraySet<Integer> ignoreSet = new ArraySet<>();
+ for (int i = 0; i < cs.mAccessibilitySessions.size(); i++) {
+ ignoreSet.add(cs.mAccessibilitySessions.keyAt(i));
+ }
+ AccessibilityManagerInternal.get().createImeSession(ignoreSet);
+ }
+ }
+
+ @GuardedBy("ImfLock.class")
void clearClientSessionLocked(ClientState cs) {
finishSessionLocked(cs.curSession);
cs.curSession = null;
@@ -2653,6 +2802,24 @@
}
@GuardedBy("ImfLock.class")
+ void clearClientSessionForAccessibilityLocked(ClientState cs) {
+ for (int i = 0; i < cs.mAccessibilitySessions.size(); i++) {
+ finishSessionForAccessibilityLocked(cs.mAccessibilitySessions.valueAt(i));
+ }
+ cs.mAccessibilitySessions.clear();
+ cs.mSessionRequestedForAccessibility = false;
+ }
+
+ @GuardedBy("ImfLock.class")
+ void clearClientSessionForAccessibilityLocked(ClientState cs, int id) {
+ AccessibilitySessionState session = cs.mAccessibilitySessions.get(id);
+ if (session != null) {
+ finishSessionForAccessibilityLocked(session);
+ cs.mAccessibilitySessions.remove(id);
+ }
+ }
+
+ @GuardedBy("ImfLock.class")
private void finishSessionLocked(SessionState sessionState) {
if (sessionState != null) {
if (sessionState.session != null) {
@@ -2672,15 +2839,34 @@
}
@GuardedBy("ImfLock.class")
+ private void finishSessionForAccessibilityLocked(AccessibilitySessionState sessionState) {
+ if (sessionState != null) {
+ if (sessionState.mSession != null) {
+ try {
+ sessionState.mSession.finishSession();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Session failed to close due to remote exception", e);
+ }
+ sessionState.mSession = null;
+ }
+ }
+ }
+
+ @GuardedBy("ImfLock.class")
void clearClientSessionsLocked() {
if (getCurMethodLocked() != null) {
final int numClients = mClients.size();
for (int i = 0; i < numClients; ++i) {
clearClientSessionLocked(mClients.valueAt(i));
+ clearClientSessionForAccessibilityLocked(mClients.valueAt(i));
}
finishSessionLocked(mEnabledSession);
+ for (int i = 0; i < mEnabledAccessibilitySessions.size(); i++) {
+ finishSessionForAccessibilityLocked(mEnabledAccessibilitySessions.valueAt(i));
+ }
mEnabledSession = null;
+ mEnabledAccessibilitySessions.clear();
scheduleNotifyImeUidToAudioService(Process.INVALID_UID);
}
hideStatusBarIconLocked();
@@ -3455,7 +3641,7 @@
}
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
- null, null, null, -1, false);
+ null, null, null, null, -1, false);
}
mCurFocusedWindow = windowToken;
@@ -4250,6 +4436,41 @@
}
}
+ @GuardedBy("ImfLock.class")
+ void setEnabledSessionForAccessibilityLocked(
+ SparseArray<AccessibilitySessionState> accessibilitySessions) {
+ // mEnabledAccessibilitySessions could the same object as accessibilitySessions.
+ SparseArray<IInputMethodSession> disabledSessions = new SparseArray<>();
+ for (int i = 0; i < mEnabledAccessibilitySessions.size(); i++) {
+ if (!accessibilitySessions.contains(mEnabledAccessibilitySessions.keyAt(i))) {
+ AccessibilitySessionState sessionState = mEnabledAccessibilitySessions.valueAt(i);
+ if (sessionState != null) {
+ disabledSessions.append(mEnabledAccessibilitySessions.keyAt(i),
+ sessionState.mSession);
+ }
+ }
+ }
+ if (disabledSessions.size() > 0) {
+ AccessibilityManagerInternal.get().setImeSessionEnabled(disabledSessions,
+ false);
+ }
+ SparseArray<IInputMethodSession> enabledSessions = new SparseArray<>();
+ for (int i = 0; i < accessibilitySessions.size(); i++) {
+ if (!mEnabledAccessibilitySessions.contains(accessibilitySessions.keyAt(i))) {
+ AccessibilitySessionState sessionState = accessibilitySessions.valueAt(i);
+ if (sessionState != null) {
+ enabledSessions.append(accessibilitySessions.keyAt(i), sessionState.mSession);
+ }
+ }
+ }
+ if (enabledSessions.size() > 0) {
+ AccessibilityManagerInternal.get().setImeSessionEnabled(enabledSessions,
+ true);
+ }
+ mEnabledAccessibilitySessions = accessibilitySessions;
+ }
+
+ @SuppressWarnings("unchecked")
@UiThread
@Override
public boolean handleMessage(Message msg) {
@@ -4319,13 +4540,34 @@
// ---------------------------------------------------------
- case MSG_UNBIND_CLIENT:
+ case MSG_UNBIND_CLIENT: {
try {
- ((IInputMethodClient)msg.obj).onUnbindMethod(msg.arg1, msg.arg2);
+ // This unbinds all accessibility services too.
+ ((IInputMethodClient) msg.obj).onUnbindMethod(msg.arg1, msg.arg2);
} catch (RemoteException e) {
// There is nothing interesting about the last client dying.
+ if (!(e instanceof DeadObjectException)) {
+ Slog.w(TAG, "RemoteException when unbinding input method service or"
+ + "accessibility services");
+ }
}
return true;
+ }
+ case MSG_UNBIND_ACCESSIBILITY_SERVICE: {
+ args = (SomeArgs) msg.obj;
+ IInputMethodClient client = (IInputMethodClient) args.arg1;
+ int id = (int) args.arg2;
+ try {
+ client.onUnbindAccessibilityService(msg.arg1, id);
+ } catch (RemoteException e) {
+ // There is nothing interesting about the last client dying.
+ if (!(e instanceof DeadObjectException)) {
+ Slog.w(TAG, "RemoteException when unbinding accessibility services");
+ }
+ }
+ args.recycle();
+ return true;
+ }
case MSG_BIND_CLIENT: {
args = (SomeArgs)msg.obj;
IInputMethodClient client = (IInputMethodClient)args.arg1;
@@ -4344,6 +4586,25 @@
args.recycle();
return true;
}
+ case MSG_BIND_ACCESSIBILITY_SERVICE: {
+ args = (SomeArgs) msg.obj;
+ IInputMethodClient client = (IInputMethodClient) args.arg1;
+ InputBindResult res = (InputBindResult) args.arg2;
+ int id = (int) args.arg3;
+ try {
+ client.onBindAccessibilityService(res, id);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Client died receiving input method " + args.arg2);
+ } finally {
+ // Dispose the channel if the accessibility service is not local to this process
+ // because the remote proxy will get its own copy when unparceled.
+ if (res.channel != null && Binder.isProxy(client)) {
+ res.channel.dispose();
+ }
+ }
+ args.recycle();
+ return true;
+ }
case MSG_SET_ACTIVE: {
args = (SomeArgs) msg.obj;
final ClientState clientState = (ClientState) args.arg1;
@@ -5025,6 +5286,59 @@
mHandler.obtainMessage(MSG_UPDATE_IME_WINDOW_STATUS, disableImeIcon ? 1 : 0, 0)
.sendToTarget();
}
+
+ @Override
+ public void onSessionForAccessibilityCreated(int accessibilityConnectionId,
+ IInputMethodSession session) {
+ synchronized (ImfLock.class) {
+ if (mCurClient != null) {
+ clearClientSessionForAccessibilityLocked(mCurClient, accessibilityConnectionId);
+ mCurClient.mAccessibilitySessions.put(accessibilityConnectionId,
+ new AccessibilitySessionState(mCurClient, accessibilityConnectionId,
+ session));
+ InputBindResult res = attachNewAccessibilityLocked(
+ StartInputReason.SESSION_CREATED_BY_ACCESSIBILITY, true,
+ accessibilityConnectionId);
+ executeOrSendMessage(mCurClient.client, obtainMessageOOO(
+ MSG_BIND_ACCESSIBILITY_SERVICE, mCurClient.client, res,
+ accessibilityConnectionId));
+ }
+ }
+ }
+
+ @Override
+ public void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId) {
+ synchronized (ImfLock.class) {
+ if (mCurClient != null) {
+ if (DEBUG) {
+ Slog.v(TAG, "unbindAccessibilityFromCurrentClientLocked: client="
+ + mCurClient.client.asBinder());
+ }
+ // A11yManagerService unbinds the disabled accessibility service. We don't need
+ // to do it here.
+ @UnbindReason int unbindClientReason =
+ UnbindReason.ACCESSIBILITY_SERVICE_DISABLED;
+ executeOrSendMessage(mCurClient.client, obtainMessageIIOO(
+ MSG_UNBIND_ACCESSIBILITY_SERVICE, getSequenceNumberLocked(),
+ unbindClientReason, mCurClient.client, accessibilityConnectionId));
+ }
+ // We only have sessions when we bound to an input method. Remove this session
+ // from all clients.
+ if (getCurMethodLocked() != null) {
+ final int numClients = mClients.size();
+ for (int i = 0; i < numClients; ++i) {
+ clearClientSessionForAccessibilityLocked(mClients.valueAt(i),
+ accessibilityConnectionId);
+ }
+ AccessibilitySessionState session = mEnabledAccessibilitySessions.get(
+ accessibilityConnectionId);
+ if (session != null) {
+ finishSessionForAccessibilityLocked(session);
+ mEnabledAccessibilitySessions.remove(accessibilityConnectionId);
+ }
+ }
+ }
+ }
}
@BinderThread
@@ -5184,6 +5498,8 @@
p.println(" client=" + ci.client);
p.println(" inputContext=" + ci.inputContext);
p.println(" sessionRequested=" + ci.sessionRequested);
+ p.println(" sessionRequestedForAccessibility="
+ + ci.mSessionRequestedForAccessibility);
p.println(" curSession=" + ci.curSession);
}
p.println(" mCurMethodId=" + getSelectedMethodIdLocked());
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 3abe5e2..4d3e438 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -148,6 +148,8 @@
import com.android.server.locksettings.SyntheticPasswordManager.TokenType;
import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
import com.android.server.pm.UserManagerInternal;
+import com.android.server.utils.Watchable;
+import com.android.server.utils.Watcher;
import com.android.server.wm.WindowManagerInternal;
import libcore.util.HexEncoding;
@@ -230,6 +232,7 @@
protected final Handler mHandler;
@VisibleForTesting
protected final LockSettingsStorage mStorage;
+ private final Watcher mStorageWatcher;
private final LockSettingsStrongAuth mStrongAuth;
private final SynchronizedStrongAuthTracker mStrongAuthTracker;
private final BiometricDeferredQueue mBiometricDeferredQueue;
@@ -573,6 +576,12 @@
}
}
+ private class StorageWatcher extends Watcher {
+ public void onChange(Watchable what) {
+ LockSettingsService.this.onChange();
+ }
+ }
+
public LockSettingsService(Context context) {
this(new Injector(context));
}
@@ -614,6 +623,16 @@
mStorage);
LocalServices.addService(LockSettingsInternal.class, new LocalService());
+
+ mStorageWatcher = new StorageWatcher();
+ mStorage.registerObserver(mStorageWatcher);
+ }
+
+ /**
+ * Invalidate caches if the storage has changed.
+ */
+ private void onChange() {
+ LockPatternUtils.invalidateCredentialTypeCache();
}
/**
@@ -1278,6 +1297,11 @@
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userId);
}
+ /**
+ * This API is cached; whenever the result would change,
+ * {@link com.android.internal.widget.LockPatternUtils#invalidateCredentialTypeCache}
+ * must be called.
+ */
@Override
public int getCredentialType(int userId) {
checkPasswordHavePermission(userId);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index f69a3bd..f9db5cf 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -47,6 +47,7 @@
import com.android.internal.widget.LockPatternUtils.CredentialType;
import com.android.server.LocalServices;
import com.android.server.PersistentDataBlockManagerInternal;
+import com.android.server.utils.WatchableImpl;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -66,7 +67,7 @@
/**
* Storage for the lock settings service.
*/
-class LockSettingsStorage {
+class LockSettingsStorage extends WatchableImpl {
private static final String TAG = "LockSettingsStorage";
private static final String TABLE = "locksettings";
@@ -173,7 +174,7 @@
} finally {
db.endTransaction();
}
-
+ dispatchChange(this);
}
@VisibleForTesting
@@ -221,7 +222,7 @@
} finally {
db.endTransaction();
}
-
+ dispatchChange(this);
}
public void prefetchUser(int userId) {
@@ -412,6 +413,7 @@
}
}
mCache.putFile(name, hash);
+ dispatchChange(this);
}
}
@@ -423,6 +425,7 @@
file.delete();
mCache.putFile(name, null);
}
+ dispatchChange(this);
}
}
@@ -500,6 +503,7 @@
Slog.w(TAG, "Failed to zeroize " + path, e);
} finally {
file.delete();
+ dispatchChange(this);
}
mCache.putFile(path, null);
}
@@ -587,6 +591,7 @@
} finally {
db.endTransaction();
}
+ dispatchChange(this);
}
private void deleteFilesAndRemoveCache(String... names) {
@@ -595,6 +600,7 @@
if (file.exists()) {
file.delete();
mCache.putFile(name, null);
+ dispatchChange(this);
}
}
}
@@ -675,6 +681,7 @@
}
persistentDataBlock.setFrpCredentialHandle(PersistentData.toBytes(
persistentType, userId, qualityForUi, payload));
+ dispatchChange(this);
}
public PersistentData readPersistentDataBlock() {
diff --git a/services/core/java/com/android/server/logcat/LogAccessConfirmationActivity.java b/services/core/java/com/android/server/logcat/LogAccessConfirmationActivity.java
deleted file mode 100644
index 6b442a6..0000000
--- a/services/core/java/com/android/server/logcat/LogAccessConfirmationActivity.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2022 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.logcat;
-
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.os.Bundle;
-import android.os.ServiceManager;
-import android.os.logcat.ILogcatManagerService;
-import android.util.Slog;
-import android.view.View;
-import android.widget.TextView;
-
-import com.android.internal.R;
-import com.android.internal.app.AlertActivity;
-import com.android.internal.app.AlertController;
-
-
-/**
- * This dialog is shown to the user before an activity in a harmful app is launched.
- *
- * See {@code PackageManager.setLogcatAppInfo} for more info.
- */
-public class LogAccessConfirmationActivity extends AlertActivity implements
- DialogInterface.OnClickListener {
- private static final String TAG = LogAccessConfirmationActivity.class.getSimpleName();
-
- private String mPackageName;
- private IntentSender mTarget;
- private final ILogcatManagerService mLogcatManagerService =
- ILogcatManagerService.Stub.asInterface(ServiceManager.getService("logcat"));
-
- private int mUid;
- private int mGid;
- private int mPid;
- private int mFd;
-
- private static final String EXTRA_UID = "uid";
- private static final String EXTRA_GID = "gid";
- private static final String EXTRA_PID = "pid";
- private static final String EXTRA_FD = "fd";
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- final Intent intent = getIntent();
- mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
- mUid = intent.getIntExtra("uid", 0);
- mGid = intent.getIntExtra("gid", 0);
- mPid = intent.getIntExtra("pid", 0);
- mFd = intent.getIntExtra("fd", 0);
-
- final AlertController.AlertParams p = mAlertParams;
- p.mTitle = getString(R.string.log_access_confirmation_title);
- p.mView = createView();
-
- p.mPositiveButtonText = getString(R.string.log_access_confirmation_allow);
- p.mPositiveButtonListener = this;
- p.mNegativeButtonText = getString(R.string.log_access_confirmation_deny);
- p.mNegativeButtonListener = this;
-
- mAlert.installContent(mAlertParams);
- }
-
- private View createView() {
- final View view = getLayoutInflater().inflate(R.layout.harmful_app_warning_dialog,
- null /*root*/);
- ((TextView) view.findViewById(R.id.app_name_text))
- .setText(mPackageName);
- ((TextView) view.findViewById(R.id.message))
- .setText(getIntent().getExtras().getString("body"));
- return view;
- }
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- switch (which) {
- case DialogInterface.BUTTON_POSITIVE:
- try {
- mLogcatManagerService.approve(mUid, mGid, mPid, mFd);
- } catch (Throwable t) {
- Slog.e(TAG, "Could not start the LogcatManagerService.", t);
- }
- finish();
- break;
- case DialogInterface.BUTTON_NEGATIVE:
- try {
- mLogcatManagerService.decline(mUid, mGid, mPid, mFd);
- } catch (Throwable t) {
- Slog.e(TAG, "Could not start the LogcatManagerService.", t);
- }
- finish();
- break;
- }
- }
-
- /**
- * Create the Intent for a LogAccessConfirmationActivity.
- */
- public static Intent createIntent(Context context, String targetPackageName,
- IntentSender target, int uid, int gid, int pid, int fd) {
- final Intent intent = new Intent();
- intent.setClass(context, LogAccessConfirmationActivity.class);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, targetPackageName);
- intent.putExtra(EXTRA_UID, uid);
- intent.putExtra(EXTRA_GID, gid);
- intent.putExtra(EXTRA_PID, pid);
- intent.putExtra(EXTRA_FD, fd);
-
- return intent;
- }
-
-}
diff --git a/services/core/java/com/android/server/logcat/LogcatManagerService.java b/services/core/java/com/android/server/logcat/LogcatManagerService.java
index 7832296..ff6372ae 100644
--- a/services/core/java/com/android/server/logcat/LogcatManagerService.java
+++ b/services/core/java/com/android/server/logcat/LogcatManagerService.java
@@ -16,36 +16,20 @@
package com.android.server.logcat;
-import android.annotation.NonNull;
-import android.app.ActivityManager;
-import android.app.ActivityManager.RunningAppProcessInfo;
-import android.app.ActivityManagerInternal;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.os.ILogd;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.UserHandle;
import android.os.logcat.ILogcatManagerService;
import android.util.Slog;
-import com.android.internal.R;
-import com.android.internal.notification.SystemNotificationChannels;
-import com.android.internal.util.ArrayUtils;
-import com.android.server.LocalServices;
import com.android.server.SystemService;
-import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
- * Service responsible for managing the access to Logcat.
+ * Service responsible for manage the access to Logcat.
*/
public final class LogcatManagerService extends SystemService {
@@ -54,43 +38,6 @@
private final BinderService mBinderService;
private final ExecutorService mThreadExecutor;
private ILogd mLogdService;
- private NotificationManager mNotificationManager;
- private @NonNull ActivityManager mActivityManager;
- private ActivityManagerInternal mActivityManagerInternal;
- private static final int MAX_UID_IMPORTANCE_COUNT_LISTENER = 2;
- private static int sUidImportanceListenerCount = 0;
- private static final int AID_APP_UID = 10000;
-
- // TODO This allowlist is just a temporary workaround for the tests:
- // FrameworksServicesTests
- // PlatformRuleTests
- // After adapting the test suites, the allowlist will be removed in
- // the upcoming bug fix patches.
- private static final String[] ALLOWABLE_TESTING_PACKAGES = {
- "android.platform.test.rule.tests",
- "com.android.frameworks.servicestests"
- };
-
- // TODO Same as the above ALLOWABLE_TESTING_PACKAGES.
- private boolean isAllowableTestingPackage(int uid) {
- PackageManager pm = mContext.getPackageManager();
-
- String[] packageNames = pm.getPackagesForUid(uid);
-
- if (ArrayUtils.isEmpty(packageNames)) {
- return false;
- }
-
- for (String name : packageNames) {
- Slog.e(TAG, "isAllowableTestingPackage: " + name);
-
- if (Arrays.asList(ALLOWABLE_TESTING_PACKAGES).contains(name)) {
- return true;
- }
- }
-
- return false;
- };
private final class BinderService extends ILogcatManagerService.Stub {
@Override
@@ -104,197 +51,6 @@
// the logd data access is finished.
mThreadExecutor.execute(new LogdMonitor(uid, gid, pid, fd, false));
}
-
- @Override
- public void approve(int uid, int gid, int pid, int fd) {
- try {
- getLogdService().approve(uid, gid, pid, fd);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
-
- @Override
- public void decline(int uid, int gid, int pid, int fd) {
- try {
- getLogdService().decline(uid, gid, pid, fd);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- }
-
- private ILogd getLogdService() {
- synchronized (LogcatManagerService.this) {
- if (mLogdService == null) {
- LogcatManagerService.this.addLogdService();
- }
- return mLogdService;
- }
- }
-
- private String getBodyString(Context context, String callingPackage, int uid) {
- PackageManager pm = context.getPackageManager();
- try {
- return context.getString(
- com.android.internal.R.string.log_access_confirmation_body,
- pm.getApplicationInfoAsUser(callingPackage, PackageManager.MATCH_DIRECT_BOOT_AUTO,
- UserHandle.getUserId(uid)).loadLabel(pm));
- } catch (NameNotFoundException e) {
- // App name is unknown.
- return null;
- }
- }
-
- private void sendNotification(int notificationId, String clientInfo, int uid, int gid, int pid,
- int fd) {
-
- final ActivityManagerInternal activityManagerInternal =
- LocalServices.getService(ActivityManagerInternal.class);
-
- PackageManager pm = mContext.getPackageManager();
- String packageName = activityManagerInternal.getPackageNameByPid(pid);
- if (packageName != null) {
- String notificationBody = getBodyString(mContext, packageName, uid);
-
- final Intent mIntent = LogAccessConfirmationActivity.createIntent(mContext,
- packageName, null, uid, gid, pid, fd);
-
- if (notificationBody == null) {
- // Decline the logd access if the nofitication body is unknown
- Slog.e(TAG, "Unknown notification body, declining the logd access");
- declineLogdAccess(uid, gid, pid, fd);
- return;
- }
-
- // TODO Next version will replace notification with dialogue
- // per UX guidance.
- generateNotificationWithBodyContent(notificationId, clientInfo, notificationBody,
- mIntent);
- return;
-
- }
-
- String[] packageNames = pm.getPackagesForUid(uid);
-
- if (ArrayUtils.isEmpty(packageNames)) {
- // Decline the logd access if the app name is unknown
- Slog.e(TAG, "Unknown calling package name, declining the logd access");
- declineLogdAccess(uid, gid, pid, fd);
- return;
- }
-
- String firstPackageName = packageNames[0];
-
- if (firstPackageName == null || firstPackageName.length() == 0) {
- // Decline the logd access if the package name from uid is unknown
- Slog.e(TAG, "Unknown calling package name, declining the logd access");
- declineLogdAccess(uid, gid, pid, fd);
- return;
- }
-
- String notificationBody = getBodyString(mContext, firstPackageName, uid);
-
- final Intent mIntent = LogAccessConfirmationActivity.createIntent(mContext,
- firstPackageName, null, uid, gid, pid, fd);
-
- if (notificationBody == null) {
- Slog.e(TAG, "Unknown notification body, declining the logd access");
- declineLogdAccess(uid, gid, pid, fd);
- return;
- }
-
- // TODO Next version will replace notification with dialogue
- // per UX guidance.
- generateNotificationWithBodyContent(notificationId, clientInfo,
- notificationBody, mIntent);
- }
-
- private void declineLogdAccess(int uid, int gid, int pid, int fd) {
- try {
- getLogdService().decline(uid, gid, pid, fd);
- } catch (RemoteException ex) {
- Slog.e(TAG, "Fails to call remote functions ", ex);
- }
- }
-
- private void generateNotificationWithBodyContent(int notificationId, String clientInfo,
- String notificationBody, Intent intent) {
- final Notification.Builder notificationBuilder = new Notification.Builder(
- mContext,
- SystemNotificationChannels.ACCESSIBILITY_SECURITY_POLICY);
- intent.setFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- intent.setIdentifier(String.valueOf(notificationId) + clientInfo);
- intent.putExtra("body", notificationBody);
-
- notificationBuilder
- .setSmallIcon(R.drawable.ic_info)
- .setContentTitle(
- mContext.getString(R.string.log_access_confirmation_title))
- .setContentText(notificationBody)
- .setContentIntent(
- PendingIntent.getActivity(mContext, 0, intent,
- PendingIntent.FLAG_IMMUTABLE))
- .setTicker(mContext.getString(R.string.log_access_confirmation_title))
- .setOnlyAlertOnce(true)
- .setAutoCancel(true);
- mNotificationManager.notify(notificationId, notificationBuilder.build());
- }
-
- /**
- * A class which watches an uid for background access and notifies the logdMonitor when
- * the package status becomes foreground (importance change)
- */
- private class UidImportanceListener implements ActivityManager.OnUidImportanceListener {
- private final int mExpectedUid;
- private final int mExpectedGid;
- private final int mExpectedPid;
- private final int mExpectedFd;
- private int mExpectedImportance;
- private int mCurrentImportance = RunningAppProcessInfo.IMPORTANCE_GONE;
-
- UidImportanceListener(int uid, int gid, int pid, int fd, int importance) {
- mExpectedUid = uid;
- mExpectedGid = gid;
- mExpectedPid = pid;
- mExpectedFd = fd;
- mExpectedImportance = importance;
- }
-
- @Override
- public void onUidImportance(int uid, int importance) {
- if (uid == mExpectedUid) {
- mCurrentImportance = importance;
-
- /**
- * 1) If the process status changes to foreground, send a notification
- * for user consent.
- * 2) If the process status remains background, we decline logd access request.
- **/
- if (importance <= RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE) {
- String clientInfo = getClientInfo(uid, mExpectedGid, mExpectedPid, mExpectedFd);
- sendNotification(0, clientInfo, uid, mExpectedGid, mExpectedPid,
- mExpectedFd);
- mActivityManager.removeOnUidImportanceListener(this);
-
- synchronized (LogcatManagerService.this) {
- sUidImportanceListenerCount--;
- }
- } else {
- try {
- getLogdService().decline(uid, mExpectedGid, mExpectedPid, mExpectedFd);
- } catch (RemoteException ex) {
- Slog.e(TAG, "Fails to call remote functions ", ex);
- }
- }
- }
- }
- }
-
- private static String getClientInfo(int uid, int gid, int pid, int fd) {
- return "UID=" + Integer.toString(uid) + " GID=" + Integer.toString(gid) + " PID="
- + Integer.toString(pid) + " FD=" + Integer.toString(fd);
}
private class LogdMonitor implements Runnable {
@@ -318,7 +74,9 @@
}
/**
- * LogdMonitor generates a prompt for users.
+ * The current version grant the permission by default.
+ * And track the logd access.
+ * The next version will generate a prompt for users.
* The users decide whether the logd access is allowed.
*/
@Override
@@ -328,63 +86,10 @@
}
if (mStart) {
-
- // TODO See the comments of ALLOWABLE_TESTING_PACKAGES.
- if (isAllowableTestingPackage(mUid)) {
- try {
- getLogdService().approve(mUid, mGid, mPid, mFd);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- return;
- }
-
- // If the access request is coming from native apps, approve the logd access
- // TODO: This is needed to make tooling to work. However,
- // we intend to be stricter with respect to native processes in a follow-up CL
- if (mUid < AID_APP_UID) {
- try {
- getLogdService().approve(mUid, mGid, mPid, mFd);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- return;
- }
-
- final int procState = LocalServices.getService(ActivityManagerInternal.class)
- .getUidProcessState(mUid);
- // If the process is foreground, send a notification for user consent
- if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
- String clientInfo = getClientInfo(mUid, mGid, mPid, mFd);
- sendNotification(0, clientInfo, mUid, mGid, mPid, mFd);
- } else {
- /**
- * If the process is background, add a background process change listener and
- * monitor if the process status changes.
- * To avoid clients registering multiple listeners, we limit the number of
- * maximum listeners to MAX_UID_IMPORTANCE_COUNT_LISTENER.
- **/
- if (mActivityManager == null) {
- return;
- }
-
- synchronized (LogcatManagerService.this) {
- if (sUidImportanceListenerCount < MAX_UID_IMPORTANCE_COUNT_LISTENER) {
- // Trigger addOnUidImportanceListener when there is an update from
- // the importance of the process
- mActivityManager.addOnUidImportanceListener(new UidImportanceListener(
- mUid, mGid, mPid, mFd,
- RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE),
- RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
- sUidImportanceListenerCount++;
- } else {
- try {
- getLogdService().decline(mUid, mGid, mPid, mFd);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- }
+ try {
+ mLogdService.approve(mUid, mGid, mPid, mFd);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Fails to call remote functions ", ex);
}
}
}
@@ -395,8 +100,6 @@
mContext = context;
mBinderService = new BinderService();
mThreadExecutor = Executors.newCachedThreadPool();
- mActivityManager = context.getSystemService(ActivityManager.class);
- mNotificationManager = mContext.getSystemService(NotificationManager.class);
}
@Override
@@ -411,4 +114,5 @@
private void addLogdService() {
mLogdService = ILogd.Stub.asInterface(ServiceManager.getService("logd"));
}
+
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index aafcc58..2717f0c 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -83,6 +83,7 @@
import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
+import static android.service.notification.NotificationListenerService.REASON_ASSISTANT_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
import static android.service.notification.NotificationListenerService.REASON_CHANNEL_BANNED;
@@ -476,6 +477,14 @@
@LoggingOnly
private static final long RATE_LIMIT_TOASTS = 174840628L;
+ /**
+ * Whether listeners understand the more specific reason provided for notification
+ * cancellations from an assistant, rather than using the more general REASON_LISTENER_CANCEL.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S_V2)
+ private static final long NOTIFICATION_LOG_ASSISTANT_CANCEL = 195579280L;
+
private IActivityManager mAm;
private ActivityTaskManagerInternal mAtm;
private ActivityManager mActivityManager;
@@ -4388,6 +4397,13 @@
synchronized (mNotificationLock) {
final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+ // Cancellation reason. If the token comes from assistant, label the
+ // cancellation as coming from the assistant; default to LISTENER_CANCEL.
+ int reason = REASON_LISTENER_CANCEL;
+ if (mAssistants.isServiceTokenValidLocked(token)) {
+ reason = REASON_ASSISTANT_CANCEL;
+ }
+
if (keys != null) {
final int N = keys.length;
for (int i = 0; i < N; i++) {
@@ -4400,7 +4416,7 @@
}
cancelNotificationFromListenerLocked(info, callingUid, callingPid,
r.getSbn().getPackageName(), r.getSbn().getTag(),
- r.getSbn().getId(), userId);
+ r.getSbn().getId(), userId, reason);
}
} else {
cancelAllLocked(callingUid, callingPid, info.userid,
@@ -4494,12 +4510,13 @@
*/
@GuardedBy("mNotificationLock")
private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
- int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
+ int callingUid, int callingPid, String pkg, String tag, int id, int userId,
+ int reason) {
int mustNotHaveFlags = FLAG_ONGOING_EVENT;
cancelNotification(callingUid, callingPid, pkg, tag, id, 0 /* mustHaveFlags */,
mustNotHaveFlags,
true,
- userId, REASON_LISTENER_CANCEL, info);
+ userId, reason, info);
}
/**
@@ -4641,13 +4658,17 @@
try {
synchronized (mNotificationLock) {
final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+ int cancelReason = REASON_LISTENER_CANCEL;
+ if (mAssistants.isServiceTokenValidLocked(token)) {
+ cancelReason = REASON_ASSISTANT_CANCEL;
+ }
if (info.supportsProfiles()) {
Slog.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
+ "from " + info.component
+ " use cancelNotification(key) instead.");
} else {
cancelNotificationFromListenerLocked(info, callingUid, callingPid,
- pkg, tag, id, info.userid);
+ pkg, tag, id, info.userid, cancelReason);
}
}
} finally {
@@ -11049,6 +11070,12 @@
&& (reason == REASON_CHANNEL_REMOVED || reason == REASON_CLEAR_DATA)) {
reason = REASON_CHANNEL_BANNED;
}
+ // apps before T don't know about REASON_ASSISTANT, so replace it with the
+ // previously-used case, REASON_LISTENER_CANCEL
+ if (!CompatChanges.isChangeEnabled(NOTIFICATION_LOG_ASSISTANT_CANCEL, info.uid)
+ && reason == REASON_ASSISTANT_CANCEL) {
+ reason = REASON_LISTENER_CANCEL;
+ }
listener.onNotificationRemoved(sbnHolder, rankingUpdate, stats, reason);
} catch (RemoteException ex) {
Slog.e(TAG, "unable to notify listener (removed): " + info, ex);
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index f3dc2dd..9a89efa 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
+import static android.service.notification.NotificationListenerService.REASON_ASSISTANT_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CLICK;
import static android.service.notification.NotificationListenerService.REASON_TIMEOUT;
@@ -180,7 +181,9 @@
+ " shade.")
NOTIFICATION_CANCEL_USER_SHADE(192),
@UiEvent(doc = "Notification was canceled due to user dismissal from the lockscreen")
- NOTIFICATION_CANCEL_USER_LOCKSCREEN(193);
+ NOTIFICATION_CANCEL_USER_LOCKSCREEN(193),
+ @UiEvent(doc = "Notification was canceled due to an assistant adjustment update.")
+ NOTIFICATION_CANCEL_ASSISTANT(906);
private final int mId;
NotificationCancelledEvent(int id) {
@@ -206,6 +209,9 @@
if ((REASON_CLICK <= reason) && (reason <= REASON_TIMEOUT)) {
return NotificationCancelledEvent.values()[reason];
}
+ if (reason == REASON_ASSISTANT_CANCEL) {
+ return NotificationCancelledEvent.NOTIFICATION_CANCEL_ASSISTANT;
+ }
if (NotificationManagerService.DBG) {
throw new IllegalArgumentException("Unexpected cancel reason " + reason);
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 2760578..1cf2dc5 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -735,6 +735,9 @@
if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "unloadUserLocked: user=" + userId);
}
+ // Cancel any ongoing background tasks.
+ getUserShortcutsLocked(userId).cancelAllInFlightTasks();
+
// Save all dirty information.
saveDirtyInfo(false);
@@ -3736,6 +3739,7 @@
synchronized (mLock) {
if (mHandler.hasCallbacks(mSaveDirtyInfoRunner)) {
mHandler.removeCallbacks(mSaveDirtyInfoRunner);
+ forEachLoadedUserLocked(ShortcutUser::cancelAllInFlightTasks);
saveDirtyInfo(false);
}
mShutdown.set(true);
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 408f045..4bb5dcf 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -33,6 +33,7 @@
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.logging.MetricsLogger;
@@ -50,7 +51,9 @@
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -138,6 +141,11 @@
private String mLastAppScanOsFingerprint;
private String mRestoreFromOsFingerprint;
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final ArrayList<AndroidFuture<AppSearchSession>> mInFlightSessions = new ArrayList<>();
+
public ShortcutUser(ShortcutService service, int userId) {
mService = service;
mUserId = userId;
@@ -718,6 +726,10 @@
AndroidFuture<AppSearchSession> getAppSearch(
@NonNull final AppSearchManager.SearchContext searchContext) {
final AndroidFuture<AppSearchSession> future = new AndroidFuture<>();
+ synchronized (mLock) {
+ mInFlightSessions.removeIf(CompletableFuture::isDone);
+ mInFlightSessions.add(future);
+ }
if (mAppSearchManager == null) {
future.completeExceptionally(new RuntimeException("app search manager is null"));
return future;
@@ -743,4 +755,13 @@
}
return future;
}
+
+ void cancelAllInFlightTasks() {
+ synchronized (mLock) {
+ for (AndroidFuture<AppSearchSession> session : mInFlightSessions) {
+ session.cancel(true);
+ }
+ mInFlightSessions.clear();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 3857072..e523153 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -521,6 +521,9 @@
// The screen off timeout setting value in milliseconds.
private long mScreenOffTimeoutSetting;
+ // The screen off timeout setting value in milliseconds to apply while device is docked.
+ private long mScreenOffTimeoutDockedSetting;
+
// Default for attentive warning duration.
private long mAttentiveWarningDurationConfig;
@@ -1272,6 +1275,9 @@
resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.SCREEN_OFF_TIMEOUT),
false, mSettingsObserver, UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.SCREEN_OFF_TIMEOUT_DOCKED),
+ false, mSettingsObserver, UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.SLEEP_TIMEOUT),
false, mSettingsObserver, UserHandle.USER_ALL);
@@ -1394,6 +1400,9 @@
mScreenOffTimeoutSetting = Settings.System.getIntForUser(resolver,
Settings.System.SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_OFF_TIMEOUT,
UserHandle.USER_CURRENT);
+ mScreenOffTimeoutDockedSetting = Settings.System.getLongForUser(resolver,
+ Settings.System.SCREEN_OFF_TIMEOUT_DOCKED, mScreenOffTimeoutSetting,
+ UserHandle.USER_CURRENT);
mSleepTimeoutSetting = Settings.Secure.getIntForUser(resolver,
Settings.Secure.SLEEP_TIMEOUT, DEFAULT_SLEEP_TIMEOUT,
UserHandle.USER_CURRENT);
@@ -2946,7 +2955,9 @@
@GuardedBy("mLock")
private long getScreenOffTimeoutLocked(long sleepTimeout, long attentiveTimeout) {
- long timeout = mScreenOffTimeoutSetting;
+ long timeout = mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED
+ ? mScreenOffTimeoutSetting
+ : mScreenOffTimeoutDockedSetting;
if (isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
timeout = Math.min(timeout, mMaximumScreenOffTimeoutFromDeviceAdmin);
}
@@ -4974,7 +4985,8 @@
}
}
- private final class DockReceiver extends BroadcastReceiver {
+ @VisibleForTesting
+ final class DockReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index f15d2bb..e02fabd 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -2543,6 +2543,7 @@
@Override
public int getClientPriority(int useCase, String sessionId) {
+ ensureTunerResourceAccessPermission();
final int callingPid = Binder.getCallingPid();
final long identity = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index ff96aeb..ec4c58f 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1135,13 +1135,13 @@
}
@Override
- public void setDisablePreviewScreenshots(IBinder token, boolean disable) {
+ public void setRecentsScreenshotEnabled(IBinder token, boolean enabled) {
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
if (r != null) {
- r.setDisablePreviewScreenshots(disable);
+ r.setRecentsScreenshotEnabled(enabled);
}
}
} finally {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 0769286..9532a5c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -766,7 +766,7 @@
// Last visibility state we reported to the app token.
boolean reportedVisible;
- boolean mDisablePreviewScreenshots;
+ boolean mEnablePreviewScreenshots = true;
// Information about an application starting window if displayed.
// Note: these are de-referenced before the starting window animates away.
@@ -5116,22 +5116,22 @@
}
/**
- * See {@link Activity#setDisablePreviewScreenshots}.
+ * See {@link Activity#setRecentsScreenshotEnabled}.
*/
- void setDisablePreviewScreenshots(boolean disable) {
- mDisablePreviewScreenshots = disable;
+ void setRecentsScreenshotEnabled(boolean enabled) {
+ mEnablePreviewScreenshots = enabled;
}
/**
* Retrieves whether we'd like to generate a snapshot that's based solely on the theme. This is
- * the case when preview screenshots are disabled {@link #setDisablePreviewScreenshots} or when
+ * the case when preview screenshots are disabled {@link #setRecentsScreenshotEnabled} or when
* we can't take a snapshot for other reasons, for example, if we have a secure window.
*
* @return True if we need to generate an app theme snapshot, false if we'd like to take a real
* screenshot.
*/
boolean shouldUseAppThemeSnapshot() {
- return mDisablePreviewScreenshots || forAllWindows(WindowState::isSecureLocked,
+ return !mEnablePreviewScreenshots || forAllWindows(WindowState::isSecureLocked,
true /* topToBottom */);
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index ca4d717..580ab179 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5267,6 +5267,18 @@
}
}
+ /**
+ * Returns {@code true} if the process represented by the pid passed as argument is
+ * instrumented.
+ */
+ boolean isInstrumenting(int pid) {
+ final WindowProcessController process;
+ synchronized (mGlobalLock) {
+ process = mProcessMap.getProcess(pid);
+ }
+ return process != null ? process.isInstrumenting() : false;
+ }
+
final class H extends Handler {
static final int REPORT_TIME_TRACKER_MSG = 1;
static final int UPDATE_PROCESS_ANIMATING_STATE = 2;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index bf11ebc..a9add59 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -196,15 +196,21 @@
snapshotTasks(tasks, false /* allowSnapshotHome */);
}
- void recordTaskSnapshot(Task task, boolean allowSnapshotHome) {
+ /**
+ * This is different than {@link #recordTaskSnapshot(Task, boolean)} because it doesn't store
+ * the snapshot to the cache and returns the TaskSnapshot immediately.
+ *
+ * This is only used for testing so the snapshot content can be verified.
+ */
+ @VisibleForTesting
+ TaskSnapshot captureTaskSnapshot(Task task, boolean snapshotHome) {
final TaskSnapshot snapshot;
- final boolean snapshotHome = allowSnapshotHome && task.isActivityTypeHome();
if (snapshotHome) {
snapshot = snapshotTask(task);
} else {
switch (getSnapshotMode(task)) {
case SNAPSHOT_MODE_NONE:
- return;
+ return null;
case SNAPSHOT_MODE_APP_THEME:
snapshot = drawAppThemeSnapshot(task);
break;
@@ -216,19 +222,27 @@
break;
}
}
- if (snapshot != null) {
- final HardwareBuffer buffer = snapshot.getHardwareBuffer();
- if (buffer.getWidth() == 0 || buffer.getHeight() == 0) {
- buffer.close();
- Slog.e(TAG, "Invalid task snapshot dimensions " + buffer.getWidth() + "x"
- + buffer.getHeight());
- } else {
- mCache.putSnapshot(task, snapshot);
- // Don't persist or notify the change for the temporal snapshot.
- if (!snapshotHome) {
- mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
- task.onSnapshotChanged(snapshot);
- }
+ return snapshot;
+ }
+
+ void recordTaskSnapshot(Task task, boolean allowSnapshotHome) {
+ final boolean snapshotHome = allowSnapshotHome && task.isActivityTypeHome();
+ final TaskSnapshot snapshot = captureTaskSnapshot(task, snapshotHome);
+ if (snapshot == null) {
+ return;
+ }
+
+ final HardwareBuffer buffer = snapshot.getHardwareBuffer();
+ if (buffer.getWidth() == 0 || buffer.getHeight() == 0) {
+ buffer.close();
+ Slog.e(TAG, "Invalid task snapshot dimensions " + buffer.getWidth() + "x"
+ + buffer.getHeight());
+ } else {
+ mCache.putSnapshot(task, snapshot);
+ // Don't persist or notify the change for the temporal snapshot.
+ if (!snapshotHome) {
+ mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
+ task.onSnapshotChanged(snapshot);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 71f4882..0f3b903 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -116,6 +116,7 @@
import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
+import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
@@ -3682,7 +3683,8 @@
* Sets the touch mode state.
*
* To be able to change touch mode state, the caller must either own the focused window, or must
- * have the MODIFY_TOUCH_MODE_STATE permission.
+ * have the MODIFY_TOUCH_MODE_STATE permission. Instrumented processes are allowed to switch
+ * touch mode at any time.
*
* @param mode the touch mode to set
*/
@@ -3694,8 +3696,9 @@
}
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
- final boolean hasPermission = checkCallingPermission(MODIFY_TOUCH_MODE_STATE,
- "setInTouchMode()");
+
+ final boolean hasPermission = mAtmService.isInstrumenting(pid)
+ || checkCallingPermission(MODIFY_TOUCH_MODE_STATE, "setInTouchMode()");
final long token = Binder.clearCallingIdentity();
try {
if (mInputManager.setInTouchMode(mode, pid, uid, hasPermission)) {
@@ -8908,4 +8911,27 @@
mTaskFpsCallbackController.unregisterCallback(listener);
}
+
+ @Override
+ public Bitmap snapshotTaskForRecents(int taskId) {
+ if (!checkCallingPermission(READ_FRAME_BUFFER, "snapshotTaskForRecents()")) {
+ throw new SecurityException("Requires READ_FRAME_BUFFER permission");
+ }
+
+ TaskSnapshot taskSnapshot;
+ synchronized (mGlobalLock) {
+ Task task = mRoot.anyTaskForId(taskId, MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
+ if (task == null) {
+ throw new IllegalArgumentException(
+ "Failed to find matching task for taskId=" + taskId);
+ }
+ taskSnapshot = mTaskSnapshotController.captureTaskSnapshot(task, false);
+ }
+
+ if (taskSnapshot == null || taskSnapshot.getHardwareBuffer() == null) {
+ return null;
+ }
+ return Bitmap.wrapHardwareBuffer(taskSnapshot.getHardwareBuffer(),
+ taskSnapshot.getColorSpace());
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 27024ce..1d5c184 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1146,7 +1146,11 @@
final PendingTransaction pt = mPendingTransactions.remove(0);
pt.startSync();
// Post this so that the now-playing transition setup isn't interrupted.
- mService.mH.post(pt::startTransaction);
+ mService.mH.post(() -> {
+ synchronized (mGlobalLock) {
+ pt.startTransaction();
+ }
+ });
}
@Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ebfa2c6..80011d1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -10982,13 +10982,12 @@
}
@Override
- public void acknowledgeNewUserDisclaimer() {
+ public void acknowledgeNewUserDisclaimer(@UserIdInt int userId) {
CallerIdentity callerIdentity = getCallerIdentity();
Preconditions.checkCallAuthorization(canManageUsers(callerIdentity)
|| hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS));
- setShowNewUserDisclaimer(callerIdentity.getUserId(),
- DevicePolicyData.NEW_USER_DISCLAIMER_ACKNOWLEDGED);
+ setShowNewUserDisclaimer(userId, DevicePolicyData.NEW_USER_DISCLAIMER_ACKNOWLEDGED);
}
private void setShowNewUserDisclaimer(@UserIdInt int userId, String value) {
@@ -11021,11 +11020,10 @@
}
@Override
- public boolean isNewUserDisclaimerAcknowledged() {
+ public boolean isNewUserDisclaimerAcknowledged(@UserIdInt int userId) {
CallerIdentity callerIdentity = getCallerIdentity();
Preconditions.checkCallAuthorization(canManageUsers(callerIdentity)
|| hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS));
- int userId = callerIdentity.getUserId();
synchronized (getLockObject()) {
DevicePolicyData policyData = getUserData(userId);
return policyData.isNewUserDisclaimerAcknowledged();
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 62a16f7..c5f990d 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -60,6 +60,12 @@
private static ProfcollectForwardingService sSelfService;
private final Handler mHandler = new ProfcollectdHandler(IoThread.getHandler().getLooper());
+ private IProviderStatusCallback mProviderStatusCallback = new IProviderStatusCallback.Stub() {
+ public void onProviderReady() {
+ mHandler.sendEmptyMessage(ProfcollectdHandler.MESSAGE_REGISTER_SCHEDULERS);
+ }
+ };
+
public ProfcollectForwardingService(Context context) {
super(context);
@@ -93,13 +99,23 @@
}
BackgroundThread.get().getThreadHandler().post(() -> {
if (serviceHasSupportedTraceProvider()) {
- registerObservers();
- ProfcollectBGJobService.schedule(getContext());
+ registerProviderStatusCallback();
}
});
}
}
+ private void registerProviderStatusCallback() {
+ if (mIProfcollect == null) {
+ return;
+ }
+ try {
+ mIProfcollect.registerProviderStatusCallback(mProviderStatusCallback);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, e.getMessage());
+ }
+ }
+
private boolean serviceHasSupportedTraceProvider() {
if (mIProfcollect == null) {
return false;
@@ -141,6 +157,7 @@
}
public static final int MESSAGE_BINDER_CONNECT = 0;
+ public static final int MESSAGE_REGISTER_SCHEDULERS = 1;
@Override
public void handleMessage(android.os.Message message) {
@@ -148,6 +165,10 @@
case MESSAGE_BINDER_CONNECT:
connectNativeService();
break;
+ case MESSAGE_REGISTER_SCHEDULERS:
+ registerObservers();
+ ProfcollectBGJobService.schedule(getContext());
+ break;
default:
throw new AssertionError("Unknown message: " + message);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index d710308..ac115a2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -30,7 +30,6 @@
import static com.android.server.DeviceIdleController.LIGHT_STATE_IDLE_MAINTENANCE;
import static com.android.server.DeviceIdleController.LIGHT_STATE_INACTIVE;
import static com.android.server.DeviceIdleController.LIGHT_STATE_OVERRIDE;
-import static com.android.server.DeviceIdleController.LIGHT_STATE_PRE_IDLE;
import static com.android.server.DeviceIdleController.LIGHT_STATE_WAITING_FOR_NETWORK;
import static com.android.server.DeviceIdleController.MSG_REPORT_STATIONARY_STATUS;
import static com.android.server.DeviceIdleController.MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR;
@@ -112,6 +111,7 @@
/**
* Tests for {@link com.android.server.DeviceIdleController}.
*/
+@SuppressWarnings("GuardedBy")
@RunWith(AndroidJUnit4.class)
public class DeviceIdleControllerTest {
private DeviceIdleController mDeviceIdleController;
@@ -875,7 +875,7 @@
@Test
public void testLightStepIdleStateLocked_InvalidStates() {
mDeviceIdleController.becomeActiveLocked("testing", 0);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
// stepLightIdleStateLocked doesn't handle the ACTIVE case, so the state
// should stay as ACTIVE.
verifyLightStateConditions(LIGHT_STATE_ACTIVE);
@@ -888,7 +888,7 @@
@Test
public void testLightStepIdleStateLocked_Overriden() {
enterLightState(LIGHT_STATE_OVERRIDE);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_OVERRIDE);
}
@@ -906,18 +906,18 @@
verifyLightStateConditions(LIGHT_STATE_INACTIVE);
// No active ops means INACTIVE should go straight to IDLE.
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE);
// Should just alternate between IDLE and IDLE_MAINTENANCE now.
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
}
@@ -930,26 +930,22 @@
setScreenOn(false);
verifyLightStateConditions(LIGHT_STATE_INACTIVE);
- // Active ops means INACTIVE should go to PRE_IDLE to wait.
+ // After enough time, INACTIVE should go to IDLE regardless of any active ops.
mDeviceIdleController.setJobsActive(true);
mDeviceIdleController.setAlarmsActive(true);
mDeviceIdleController.setActiveIdleOpsForTest(1);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
- verifyLightStateConditions(LIGHT_STATE_PRE_IDLE);
-
- // Even with active ops, PRE_IDLE should go to IDLE.
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE);
// Should just alternate between IDLE and IDLE_MAINTENANCE now.
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
}
@@ -967,24 +963,24 @@
verifyLightStateConditions(LIGHT_STATE_INACTIVE);
// No active ops means INACTIVE should go straight to IDLE.
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE);
// Should cycle between IDLE, WAITING_FOR_NETWORK, and IDLE_MAINTENANCE now.
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_WAITING_FOR_NETWORK);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_WAITING_FOR_NETWORK);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
}
@@ -997,36 +993,177 @@
setScreenOn(false);
verifyLightStateConditions(LIGHT_STATE_INACTIVE);
- // Active ops means INACTIVE should go to PRE_IDLE to wait.
+ // After enough time, INACTIVE should go to IDLE regardless of any active ops.
mDeviceIdleController.setJobsActive(true);
mDeviceIdleController.setAlarmsActive(true);
mDeviceIdleController.setActiveIdleOpsForTest(1);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
- verifyLightStateConditions(LIGHT_STATE_PRE_IDLE);
-
- // Even with active ops, PRE_IDLE should go to IDLE.
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE);
// Should cycle between IDLE, WAITING_FOR_NETWORK, and IDLE_MAINTENANCE now.
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_WAITING_FOR_NETWORK);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_WAITING_FOR_NETWORK);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
}
@Test
+ public void testLightStepIdleStateSkippedAlarms() {
+ setNetworkConnected(true);
+ mDeviceIdleController.setJobsActive(false);
+ mDeviceIdleController.setAlarmsActive(false);
+ mDeviceIdleController.setActiveIdleOpsForTest(0);
+
+ final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListenerCaptor = ArgumentCaptor
+ .forClass(AlarmManager.OnAlarmListener.class);
+ doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
+ eq("DeviceIdleController.light"), alarmListenerCaptor.capture(), any());
+ doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
+ eq("DeviceIdleController.light"), alarmListenerCaptor.capture(), any());
+
+ // Set state to INACTIVE.
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ setChargingOn(false);
+ setScreenOn(false);
+ verifyLightStateConditions(LIGHT_STATE_INACTIVE);
+
+ final AlarmManager.OnAlarmListener progressionListener =
+ alarmListenerCaptor.getAllValues().get(0);
+ final AlarmManager.OnAlarmListener maintenanceListener =
+ alarmListenerCaptor.getAllValues().get(1);
+
+ // Set things to make it look like the INACTIVE -> IDLE alarm didn't fire and the
+ // MAINTENANCE alarm just fired.
+ mInjector.nowElapsed = mDeviceIdleController.getNextLightMaintenanceAlarmTimeForTesting();
+ // If the non-wakeup alarm doesn't fire in a timely manner, we would see both fire at the
+ // same time.
+ progressionListener.onAlarm();
+ verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+ maintenanceListener.onAlarm();
+ verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+
+ assertTrue(mInjector.nowElapsed < mDeviceIdleController.getNextLightAlarmTimeForTesting());
+
+ // MAINTENANCE->IDLE alarm goes off at correct time.
+ mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting();
+ progressionListener.onAlarm();
+ verifyLightStateConditions(LIGHT_STATE_IDLE);
+
+ // Go back to MAINTENANCE
+ mInjector.nowElapsed = mDeviceIdleController.getNextLightMaintenanceAlarmTimeForTesting();
+ maintenanceListener.onAlarm();
+ verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+
+ assertTrue(mInjector.nowElapsed < mDeviceIdleController.getNextLightAlarmTimeForTesting());
+ assertTrue(mInjector.nowElapsed
+ < mDeviceIdleController.getNextLightMaintenanceAlarmTimeForTesting());
+
+ // MAINTENANCE->IDLE alarm is delayed until IDLE->MAINTENANCE alarm goes off.
+ mInjector.nowElapsed = mDeviceIdleController.getNextLightMaintenanceAlarmTimeForTesting();
+ progressionListener.onAlarm();
+ verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+ maintenanceListener.onAlarm();
+ verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+ }
+
+ @Test
+ public void testLightStepIdleStateIdlingTimeIncreases() {
+ final long maintenanceTimeMs = 60_000L;
+ mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = maintenanceTimeMs;
+ mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = maintenanceTimeMs;
+ mConstants.LIGHT_IDLE_TIMEOUT = 5 * 60_000L;
+ mConstants.LIGHT_MAX_IDLE_TIMEOUT = 20 * 60_000L;
+ mConstants.LIGHT_IDLE_FACTOR = 2f;
+
+ setNetworkConnected(true);
+ mDeviceIdleController.setJobsActive(false);
+ mDeviceIdleController.setAlarmsActive(false);
+ mDeviceIdleController.setActiveIdleOpsForTest(0);
+
+ InOrder alarmManagerInOrder = inOrder(mAlarmManager);
+
+ final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListenerCaptor = ArgumentCaptor
+ .forClass(AlarmManager.OnAlarmListener.class);
+ doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
+ eq("DeviceIdleController.light"), alarmListenerCaptor.capture(), any());
+ doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
+ eq("DeviceIdleController.light"), alarmListenerCaptor.capture(), any());
+
+ // Set state to INACTIVE.
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ setChargingOn(false);
+ setScreenOn(false);
+ verifyLightStateConditions(LIGHT_STATE_INACTIVE);
+ long idlingTimeMs = mConstants.LIGHT_IDLE_TIMEOUT;
+ final long idleAfterInactiveExpiryTime =
+ mInjector.nowElapsed + mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT;
+ alarmManagerInOrder.verify(mAlarmManager).setWindow(
+ eq(AlarmManager.ELAPSED_REALTIME),
+ eq(idleAfterInactiveExpiryTime),
+ anyLong(), anyString(), any(), any());
+ // Maintenance alarm
+ alarmManagerInOrder.verify(mAlarmManager).setWindow(
+ eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+ eq(idleAfterInactiveExpiryTime + idlingTimeMs),
+ anyLong(), anyString(), any(), any());
+
+ final AlarmManager.OnAlarmListener progressionListener =
+ alarmListenerCaptor.getAllValues().get(0);
+ final AlarmManager.OnAlarmListener maintenanceListener =
+ alarmListenerCaptor.getAllValues().get(1);
+
+ // INACTIVE -> IDLE alarm
+ mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting();
+ progressionListener.onAlarm();
+ verifyLightStateConditions(LIGHT_STATE_IDLE);
+ alarmManagerInOrder.verify(mAlarmManager).setWindow(
+ eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+ eq(mInjector.nowElapsed + idlingTimeMs),
+ anyLong(), anyString(), any(), any());
+
+ for (int i = 0; i < 2; ++i) {
+ // IDLE->MAINTENANCE alarm
+ mInjector.nowElapsed =
+ mDeviceIdleController.getNextLightMaintenanceAlarmTimeForTesting();
+ maintenanceListener.onAlarm();
+ verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+ long maintenanceExpiryTime = mInjector.nowElapsed + maintenanceTimeMs;
+ idlingTimeMs *= mConstants.LIGHT_IDLE_FACTOR;
+ // Set MAINTENANCE->IDLE
+ alarmManagerInOrder.verify(mAlarmManager).setWindow(
+ eq(AlarmManager.ELAPSED_REALTIME),
+ eq(maintenanceExpiryTime),
+ anyLong(), anyString(), any(), any());
+ // Set IDLE->MAINTENANCE
+ alarmManagerInOrder.verify(mAlarmManager).setWindow(
+ eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+ eq(maintenanceExpiryTime + idlingTimeMs),
+ anyLong(), anyString(), any(), any());
+
+ // MAINTENANCE->IDLE alarm
+ mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting();
+ progressionListener.onAlarm();
+ verifyLightStateConditions(LIGHT_STATE_IDLE);
+ // Set IDLE->MAINTENANCE again
+ alarmManagerInOrder.verify(mAlarmManager).setWindow(
+ eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+ eq(mInjector.nowElapsed + idlingTimeMs),
+ anyLong(), anyString(), any(), any());
+ }
+ }
+
+ @Test
public void testLightIdleAlarmUnaffectedByMotion() {
setNetworkConnected(true);
mDeviceIdleController.setJobsActive(false);
@@ -1043,45 +1180,37 @@
verifyLightStateConditions(LIGHT_STATE_INACTIVE);
// No active ops means INACTIVE should go straight to IDLE.
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE);
- inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
- longThat(l -> l == mConstants.LIGHT_IDLE_TIMEOUT),
- longThat(l -> l == mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX),
- eq(false));
+ inOrder.verify(mDeviceIdleController).scheduleLightMaintenanceAlarmLocked(
+ longThat(l -> l == mConstants.LIGHT_IDLE_TIMEOUT));
// Should just alternate between IDLE and IDLE_MAINTENANCE now.
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
longThat(l -> l >= mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET),
- longThat(l -> l == mConstants.FLEX_TIME_SHORT),
- eq(true));
+ longThat(l -> l == mConstants.FLEX_TIME_SHORT));
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE);
- inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
- longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT),
- longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX),
- eq(false));
+ inOrder.verify(mDeviceIdleController).scheduleLightMaintenanceAlarmLocked(
+ longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT));
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
longThat(l -> l >= mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET),
- longThat(l -> l == mConstants.FLEX_TIME_SHORT),
- eq(true));
+ longThat(l -> l == mConstants.FLEX_TIME_SHORT));
// Test that motion doesn't reset the idle timeout.
mDeviceIdleController.handleMotionDetectedLocked(50, "test");
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE);
- inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
- longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT),
- longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX),
- eq(false));
+ inOrder.verify(mDeviceIdleController).scheduleLightMaintenanceAlarmLocked(
+ longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT));
}
///////////////// EXIT conditions ///////////////////
@@ -1268,10 +1397,6 @@
mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
verifyLightStateConditions(LIGHT_STATE_INACTIVE);
- enterLightState(LIGHT_STATE_PRE_IDLE);
- mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
- verifyLightStateConditions(LIGHT_STATE_IDLE);
-
enterLightState(LIGHT_STATE_IDLE);
mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
verifyLightStateConditions(LIGHT_STATE_IDLE);
@@ -1307,10 +1432,6 @@
mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
verifyLightStateConditions(LIGHT_STATE_INACTIVE);
- enterLightState(LIGHT_STATE_PRE_IDLE);
- mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
- verifyLightStateConditions(LIGHT_STATE_PRE_IDLE);
-
enterLightState(LIGHT_STATE_IDLE);
mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
verifyLightStateConditions(LIGHT_STATE_IDLE);
@@ -1344,10 +1465,6 @@
mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
verifyLightStateConditions(LIGHT_STATE_INACTIVE);
- enterLightState(LIGHT_STATE_PRE_IDLE);
- mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
- verifyLightStateConditions(LIGHT_STATE_PRE_IDLE);
-
enterLightState(LIGHT_STATE_IDLE);
mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
verifyLightStateConditions(LIGHT_STATE_IDLE);
@@ -1381,10 +1498,6 @@
mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
verifyLightStateConditions(LIGHT_STATE_INACTIVE);
- enterLightState(LIGHT_STATE_PRE_IDLE);
- mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
- verifyLightStateConditions(LIGHT_STATE_PRE_IDLE);
-
enterLightState(LIGHT_STATE_IDLE);
mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
verifyLightStateConditions(LIGHT_STATE_IDLE);
@@ -1510,10 +1623,6 @@
mDeviceIdleController.handleMotionDetectedLocked(50, "test");
verifyLightStateConditions(LIGHT_STATE_INACTIVE);
- enterLightState(LIGHT_STATE_PRE_IDLE);
- mDeviceIdleController.handleMotionDetectedLocked(50, "test");
- verifyLightStateConditions(LIGHT_STATE_PRE_IDLE);
-
enterLightState(LIGHT_STATE_IDLE);
mDeviceIdleController.handleMotionDetectedLocked(50, "test");
verifyLightStateConditions(LIGHT_STATE_IDLE);
@@ -1580,10 +1689,6 @@
mDeviceIdleController.becomeActiveLocked("test", 1000);
verifyLightStateConditions(LIGHT_STATE_ACTIVE);
- enterLightState(LIGHT_STATE_PRE_IDLE);
- mDeviceIdleController.becomeActiveLocked("test", 1000);
- verifyLightStateConditions(LIGHT_STATE_ACTIVE);
-
enterLightState(LIGHT_STATE_IDLE);
mDeviceIdleController.becomeActiveLocked("test", 1000);
verifyLightStateConditions(LIGHT_STATE_ACTIVE);
@@ -2059,7 +2164,7 @@
while (mDeviceIdleController.getLightState() != lightState) {
// Stepping through each state ensures that the proper features are turned
// on/off.
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
count++;
if (count > 10) {
@@ -2068,7 +2173,6 @@
}
}
break;
- case LIGHT_STATE_PRE_IDLE:
case LIGHT_STATE_WAITING_FOR_NETWORK:
case LIGHT_STATE_OVERRIDE:
setScreenOn(false);
@@ -2213,7 +2317,6 @@
> mAlarmManager.getNextWakeFromIdleTime());
break;
case LIGHT_STATE_INACTIVE:
- case LIGHT_STATE_PRE_IDLE:
case LIGHT_STATE_IDLE:
case LIGHT_STATE_WAITING_FOR_NETWORK:
case LIGHT_STATE_IDLE_MAINTENANCE:
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index 67d6929..e7f4d3d 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -34,6 +34,7 @@
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
+import android.app.PropertyInvalidatedCache;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.service.gatekeeper.GateKeeperResponse;
@@ -47,6 +48,7 @@
import com.android.server.locksettings.FakeGateKeeperService.VerifyHandle;
import com.android.server.locksettings.LockSettingsStorage.CredentialHash;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -57,6 +59,11 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
+ @Before
+ public void disableProcessCaches() {
+ PropertyInvalidatedCache.disableForTestMode();
+ }
+
@Test
public void testCreatePasswordPrimaryUser() throws RemoteException {
testCreateCredential(PRIMARY_USER_ID, newPassword("password"));
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java
index 9c0239b..c2f94e2 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java
@@ -24,6 +24,7 @@
import static org.junit.Assert.assertEquals;
+import android.app.PropertyInvalidatedCache;
import android.app.admin.DevicePolicyManager;
import androidx.test.runner.AndroidJUnit4;
@@ -46,6 +47,11 @@
mSettings.setDeviceProvisioned(false);
}
+ @Before
+ public void disableProcessCaches() {
+ PropertyInvalidatedCache.disableForTestMode();
+ }
+
@Test
public void testFrpCredential_setPin() {
mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index c0a38b8..58e1c43 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -36,6 +36,7 @@
import static org.mockito.Mockito.when;
import android.app.admin.PasswordMetrics;
+import android.app.PropertyInvalidatedCache;
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
@@ -49,6 +50,7 @@
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
import com.android.server.locksettings.SyntheticPasswordManager.PasswordData;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -68,6 +70,11 @@
public static final byte[] PAYLOAD = new byte[] {1, 2, -1, -2, 55};
public static final byte[] PAYLOAD2 = new byte[] {2, 3, -2, -3, 44, 1};
+ @Before
+ public void disableProcessCaches() {
+ PropertyInvalidatedCache.disableForTestMode();
+ }
+
@Test
public void testPasswordBasedSyntheticPassword() throws RemoteException {
final int USER_ID = 10;
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 827349a..c94168c 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -87,6 +87,7 @@
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.power.PowerManagerService.BatteryReceiver;
import com.android.server.power.PowerManagerService.BinderService;
+import com.android.server.power.PowerManagerService.DockReceiver;
import com.android.server.power.PowerManagerService.Injector;
import com.android.server.power.PowerManagerService.NativeWrapper;
import com.android.server.power.PowerManagerService.UserSwitchedReceiver;
@@ -152,6 +153,7 @@
private Resources mResourcesSpy;
private OffsettableClock mClock;
private TestLooper mTestLooper;
+ private DockReceiver mDockReceiver;
private static class IntentFilterMatcher implements ArgumentMatcher<IntentFilter> {
private final IntentFilter mFilter;
@@ -337,6 +339,14 @@
argThat(new IntentFilterMatcher(usFilter)), isNull(), isA(Handler.class));
mUserSwitchedReceiver = userSwitchedCaptor.getValue();
+ // Grab the DockReceiver
+ ArgumentCaptor<DockReceiver> dockReceiverCaptor =
+ ArgumentCaptor.forClass(DockReceiver.class);
+ IntentFilter dockFilter = new IntentFilter(Intent.ACTION_DOCK_EVENT);
+ verify(mContextSpy).registerReceiver(dockReceiverCaptor.capture(),
+ argThat(new IntentFilterMatcher(dockFilter)), isNull(), isA(Handler.class));
+ mDockReceiver = dockReceiverCaptor.getValue();
+
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
}
@@ -385,6 +395,16 @@
.thenReturn(minimumScreenOffTimeoutConfigMillis);
}
+ private void setScreenOffTimeout(int screenOffTimeoutMillis) {
+ Settings.System.putInt(mContextSpy.getContentResolver(), Settings.System.SCREEN_OFF_TIMEOUT,
+ screenOffTimeoutMillis);
+ }
+
+ private void setScreenOffTimeoutDocked(int screenOffTimeoutMillis) {
+ Settings.System.putInt(mContextSpy.getContentResolver(),
+ Settings.System.SCREEN_OFF_TIMEOUT_DOCKED, screenOffTimeoutMillis);
+ }
+
private void advanceTime(long timeMs) {
mClock.fastForward(timeMs);
mTestLooper.dispatchAll();
@@ -883,6 +903,71 @@
}
@Test
+ public void testScreenOffTimeout_goesToSleepAfterTimeout() {
+ final DisplayInfo info = new DisplayInfo();
+ info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
+ when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
+
+ setMinimumScreenOffTimeoutConfig(10);
+ setScreenOffTimeout(10);
+
+ createService();
+ startSystem();
+
+ mService.getBinderServiceInstance().userActivity(Display.DEFAULT_DISPLAY, mClock.now(),
+ PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+ advanceTime(15);
+ assertThat(mService.getGlobalWakefulnessLocked()).isNotEqualTo(WAKEFULNESS_AWAKE);
+ }
+
+ @Test
+ public void testScreenOffTimeout_usesRegularTimeoutWhenNotDocked() {
+ final DisplayInfo info = new DisplayInfo();
+ info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
+ when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
+
+ setMinimumScreenOffTimeoutConfig(10);
+ setScreenOffTimeout(10);
+ setScreenOffTimeoutDocked(30);
+
+ createService();
+ startSystem();
+
+ mService.getBinderServiceInstance().userActivity(Display.DEFAULT_DISPLAY, mClock.now(),
+ PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+ advanceTime(15);
+ assertThat(mService.getGlobalWakefulnessLocked()).isNotEqualTo(WAKEFULNESS_AWAKE);
+ }
+
+ @Test
+ public void testScreenOffTimeout_usesDockedTimeoutWhenDocked() {
+ final DisplayInfo info = new DisplayInfo();
+ info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
+ when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
+
+ setMinimumScreenOffTimeoutConfig(10);
+ setScreenOffTimeout(10);
+ setScreenOffTimeoutDocked(30);
+
+ createService();
+ startSystem();
+
+ mService.getBinderServiceInstance().userActivity(Display.DEFAULT_DISPLAY, mClock.now(),
+ PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
+ mDockReceiver.onReceive(mContextSpy,
+ new Intent(Intent.ACTION_DOCK_EVENT).putExtra(Intent.EXTRA_DOCK_STATE,
+ Intent.EXTRA_DOCK_STATE_DESK));
+
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+ advanceTime(15);
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+ advanceTime(20);
+ assertThat(mService.getGlobalWakefulnessLocked()).isNotEqualTo(WAKEFULNESS_AWAKE);
+ }
+
+ @Test
public void testInattentiveSleep_goesToSleepWithWakeLock() {
final String pkg = mContextSpy.getOpPackageName();
final Binder token = new Binder();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 511296a..ef9494a 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -4659,6 +4659,59 @@
}
@Test
+ public void testAdjustmentToImportanceNone_cancelsNotification() throws Exception {
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mService.setHandler(handler);
+ when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(true);
+ when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+
+ // Set up notifications: r1 is adjusted, r2 is not
+ final NotificationRecord r1 = generateNotificationRecord(
+ mTestNotificationChannel, 1, null, true);
+ r1.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
+ mService.addNotification(r1);
+ final NotificationRecord r2 = generateNotificationRecord(
+ mTestNotificationChannel, 2, null, true);
+ r2.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
+ mService.addNotification(r2);
+
+ // Test an adjustment that sets importance to none (meaning it's cancelling)
+ Bundle signals1 = new Bundle();
+ signals1.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_NONE);
+ Adjustment adjustment1 = new Adjustment(
+ r1.getSbn().getPackageName(), r1.getKey(), signals1, "",
+ r1.getUser().getIdentifier());
+
+ mBinderService.applyAdjustmentFromAssistant(null, adjustment1);
+
+ // Actually apply the adjustments & recalculate importance when run
+ doAnswer(invocationOnMock -> {
+ ((NotificationRecord) invocationOnMock.getArguments()[0])
+ .applyAdjustments();
+ ((NotificationRecord) invocationOnMock.getArguments()[0])
+ .calculateImportance();
+ return null;
+ }).when(mRankingHelper).extractSignals(any(NotificationRecord.class));
+
+ // run the CancelNotificationRunnable when it happens
+ ArgumentCaptor<NotificationManagerService.CancelNotificationRunnable> captor =
+ ArgumentCaptor.forClass(
+ NotificationManagerService.CancelNotificationRunnable.class);
+
+ verify(handler, times(1)).scheduleCancelNotification(
+ captor.capture());
+
+ // Run the runnable given to the cancel notification, and see if it logs properly
+ NotificationManagerService.CancelNotificationRunnable runnable = captor.getValue();
+ runnable.run();
+ assertEquals(1, mNotificationRecordLogger.numCalls());
+ assertEquals(
+ NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_ASSISTANT,
+ mNotificationRecordLogger.event(0));
+ }
+
+ @Test
public void testEnqueuedAdjustmentAppliesAdjustments() throws Exception {
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addEnqueuedNotification(r);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index f573b70..e433684 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -116,7 +116,7 @@
public void testGetSnapshotMode() {
final WindowState disabledWindow = createWindow(null,
FIRST_APPLICATION_WINDOW, mDisplayContent, "disabledWindow");
- disabledWindow.mActivityRecord.setDisablePreviewScreenshots(true);
+ disabledWindow.mActivityRecord.setRecentsScreenshotEnabled(false);
assertEquals(SNAPSHOT_MODE_APP_THEME,
mWm.mTaskSnapshotController.getSnapshotMode(disabledWindow.getTask()));
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ba1a6ed..81bcf75 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -7639,7 +7639,7 @@
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
- * TODO: remove this one. use {@link #rebootRadio()} for reset type 1 and
+ * TODO: remove this one. use {@link #rebootModem()} for reset type 1 and
* {@link #resetRadioConfig()} for reset type 3
*
* @param resetType reset type: 1: reload NV reset, 2: erase NV reset, 3: factory NV reset
@@ -7706,6 +7706,8 @@
*
* @return {@code true} on success; {@code false} on any failure.
*
+ * @deprecated Using {@link #rebootModem()} instead.
+ *
* @hide
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -7726,6 +7728,30 @@
}
/**
+ * Generate a radio modem reset. Used for device configuration by some carriers.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+ * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ * @throws IllegalStateException if the Telephony process is not currently available.
+ * @throws RuntimeException
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+ public void rebootModem() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ telephony.rebootModem(getSlotIndex());
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "rebootRadio RemoteException", ex);
+ throw ex.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
* Return an appropriate subscription ID for any situation.
*
* If this object has been created with {@link #createForSubscriptionId}, then the provided
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/DisableScreenshotsActivity.java b/tests/ActivityTests/src/com/google/android/test/activity/DisableScreenshotsActivity.java
index fa5724e..ca909a4 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/DisableScreenshotsActivity.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/DisableScreenshotsActivity.java
@@ -30,7 +30,7 @@
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setDisablePreviewScreenshots(true);
+ setRecentsScreenshotEnabled(false);
getWindow().getDecorView().setBackgroundColor(Color.RED);
}
diff --git a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryStatsHelperPerfTest.java b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryStatsHelperPerfTest.java
deleted file mode 100644
index 6266cda..0000000
--- a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryStatsHelperPerfTest.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2020 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.internal.os;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.os.BatteryStats;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class BatteryStatsHelperPerfTest {
-
- @Rule
- public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
-
- /**
- * Measures the performance of {@link BatteryStatsHelper#getStats()}, which triggers
- * a battery stats sync on every iteration.
- */
- @Test
- public void testGetStats_forceUpdate() {
- final Context context = InstrumentationRegistry.getContext();
- final BatteryStatsHelper statsHelper = new BatteryStatsHelper(context,
- true /* collectBatteryBroadcast */);
- statsHelper.create((Bundle) null);
- statsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId());
-
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- while (state.keepRunning()) {
- state.pauseTiming();
- statsHelper.clearStats();
- state.resumeTiming();
-
- statsHelper.getStats();
-
- assertThat(statsHelper.getUsageList()).isNotEmpty();
- }
- }
-
- /**
- * Measures performance of the {@link BatteryStatsHelper#getStats(boolean)}, which does
- * not trigger a sync and just returns current values.
- */
- @Test
- public void testGetStats_cached() {
- final Context context = InstrumentationRegistry.getContext();
- final BatteryStatsHelper statsHelper = new BatteryStatsHelper(context,
- true /* collectBatteryBroadcast */);
- statsHelper.create((Bundle) null);
- statsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId());
-
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- while (state.keepRunning()) {
- state.pauseTiming();
- statsHelper.clearStats();
- state.resumeTiming();
-
- statsHelper.getStats(false /* forceUpdate */);
-
- assertThat(statsHelper.getUsageList()).isNotEmpty();
- }
- }
-
- @Test
- public void testPowerCalculation() {
- final Context context = InstrumentationRegistry.getContext();
- final BatteryStatsHelper statsHelper = new BatteryStatsHelper(context,
- true /* collectBatteryBroadcast */);
- statsHelper.create((Bundle) null);
- statsHelper.getStats();
-
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- while (state.keepRunning()) {
- // This will use the cached BatteryStatsObject
- statsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId());
-
- assertThat(statsHelper.getUsageList()).isNotEmpty();
- }
- }
-
- @Test
- public void testEndToEnd() {
- final Context context = InstrumentationRegistry.getContext();
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- while (state.keepRunning()) {
- final BatteryStatsHelper statsHelper = new BatteryStatsHelper(context,
- true /* collectBatteryBroadcast */);
- statsHelper.create((Bundle) null);
- statsHelper.clearStats();
- statsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId());
-
- state.pauseTiming();
-
- List<BatterySipper> usageList = statsHelper.getUsageList();
- double power = 0;
- for (int i = 0; i < usageList.size(); i++) {
- BatterySipper sipper = usageList.get(i);
- power += sipper.sumPower();
- }
-
- assertThat(power).isGreaterThan(0.0);
-
- state.resumeTiming();
- }
- }
-}