Merge "Refactor PackageParser (2/n)" into sc-dev
diff --git a/METADATA b/METADATA
index d97975c..95577d8 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,4 @@
third_party {
- license_type: NOTICE
+ # would be NOTICE save for libs/usb/tests/accessorytest/f_accessory.h
+ license_type: RESTRICTED
}
diff --git a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
index cc3e9c3..c332a59 100644
--- a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
+++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
@@ -105,10 +105,6 @@
@GuardedBy("mLock")
final SparseBooleanArray mActiveUids = new SparseBooleanArray();
- /** UIDs that are in the foreground. */
- @GuardedBy("mLock")
- final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
-
/**
* System except-idle + user exemption list in the device idle controller.
*/
@@ -286,13 +282,6 @@
}
/**
- * This is called when the foreground state changed for a UID.
- */
- private void onUidForegroundStateChanged(AppStateTrackerImpl sender, int uid) {
- onUidForeground(uid, sender.isUidInForeground(uid));
- }
-
- /**
* This is called when the active/idle state changed for a UID.
*/
private void onUidActiveStateChanged(AppStateTrackerImpl sender, int uid) {
@@ -416,14 +405,6 @@
}
/**
- * Called when a UID comes into the foreground or the background.
- *
- * @see #isUidInForeground(int)
- */
- public void onUidForeground(int uid, boolean foreground) {
- }
-
- /**
* Called when an ephemeral uid goes to the background, so its alarms need to be removed.
*/
public void removeAlarmsForUid(int uid) {
@@ -460,7 +441,6 @@
mExemptedBucketPackages.remove(userId, pkgName);
mRunAnyRestrictedPackages.remove(Pair.create(uid, pkgName));
mActiveUids.delete(uid);
- mForegroundUids.delete(uid);
}
break;
}
@@ -496,8 +476,7 @@
mIActivityManager.registerUidObserver(new UidObserver(),
ActivityManager.UID_OBSERVER_GONE
| ActivityManager.UID_OBSERVER_IDLE
- | ActivityManager.UID_OBSERVER_ACTIVE
- | ActivityManager.UID_OBSERVER_PROCSTATE,
+ | ActivityManager.UID_OBSERVER_ACTIVE,
ActivityManager.PROCESS_STATE_UNKNOWN, null);
mAppOpsService.startWatchingMode(TARGET_OP, null,
new AppOpsWatcher());
@@ -698,7 +677,6 @@
private final class UidObserver extends IUidObserver.Stub {
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
- mHandler.onUidStateChanged(uid, procState);
}
@Override
@@ -769,7 +747,6 @@
private class MyHandler extends Handler {
private static final int MSG_UID_ACTIVE_STATE_CHANGED = 0;
- private static final int MSG_UID_FG_STATE_CHANGED = 1;
private static final int MSG_RUN_ANY_CHANGED = 3;
private static final int MSG_ALL_UNEXEMPTED = 4;
private static final int MSG_ALL_EXEMPTION_LIST_CHANGED = 5;
@@ -779,7 +756,6 @@
private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 9;
private static final int MSG_EXEMPTED_BUCKET_CHANGED = 10;
- private static final int MSG_ON_UID_STATE_CHANGED = 11;
private static final int MSG_ON_UID_ACTIVE = 12;
private static final int MSG_ON_UID_GONE = 13;
private static final int MSG_ON_UID_IDLE = 14;
@@ -792,10 +768,6 @@
obtainMessage(MSG_UID_ACTIVE_STATE_CHANGED, uid, 0).sendToTarget();
}
- public void notifyUidForegroundStateChanged(int uid) {
- obtainMessage(MSG_UID_FG_STATE_CHANGED, uid, 0).sendToTarget();
- }
-
public void notifyRunAnyAppOpsChanged(int uid, @NonNull String packageName) {
obtainMessage(MSG_RUN_ANY_CHANGED, uid, 0, packageName).sendToTarget();
}
@@ -834,10 +806,6 @@
obtainMessage(MSG_USER_REMOVED, userId, 0).sendToTarget();
}
- public void onUidStateChanged(int uid, int procState) {
- obtainMessage(MSG_ON_UID_STATE_CHANGED, uid, procState).sendToTarget();
- }
-
public void onUidActive(int uid) {
obtainMessage(MSG_ON_UID_ACTIVE, uid, 0).sendToTarget();
}
@@ -875,13 +843,6 @@
mStatLogger.logDurationStat(Stats.UID_ACTIVE_STATE_CHANGED, start);
return;
- case MSG_UID_FG_STATE_CHANGED:
- for (Listener l : cloneListeners()) {
- l.onUidForegroundStateChanged(sender, msg.arg1);
- }
- mStatLogger.logDurationStat(Stats.UID_FG_STATE_CHANGED, start);
- return;
-
case MSG_RUN_ANY_CHANGED:
for (Listener l : cloneListeners()) {
l.onRunAnyAppOpsChanged(sender, msg.arg1, (String) msg.obj);
@@ -944,9 +905,6 @@
handleUserRemoved(msg.arg1);
return;
- case MSG_ON_UID_STATE_CHANGED:
- handleUidStateChanged(msg.arg1, msg.arg2);
- return;
case MSG_ON_UID_ACTIVE:
handleUidActive(msg.arg1);
return;
@@ -971,20 +929,6 @@
}
}
- public void handleUidStateChanged(int uid, int procState) {
- synchronized (mLock) {
- if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
- if (removeUidFromArray(mForegroundUids, uid, false)) {
- mHandler.notifyUidForegroundStateChanged(uid);
- }
- } else {
- if (addUidToArray(mForegroundUids, uid)) {
- mHandler.notifyUidForegroundStateChanged(uid);
- }
- }
- }
- }
-
public void handleUidActive(int uid) {
synchronized (mLock) {
if (addUidToArray(mActiveUids, uid)) {
@@ -1007,9 +951,6 @@
if (removeUidFromArray(mActiveUids, uid, remove)) {
mHandler.notifyUidActiveStateChanged(uid);
}
- if (removeUidFromArray(mForegroundUids, uid, remove)) {
- mHandler.notifyUidForegroundStateChanged(uid);
- }
}
}
}
@@ -1026,7 +967,6 @@
}
}
cleanUpArrayForUser(mActiveUids, removedUserId);
- cleanUpArrayForUser(mForegroundUids, removedUserId);
mExemptedBucketPackages.remove(removedUserId);
}
}
@@ -1222,22 +1162,6 @@
}
/**
- * @return whether a UID is in the foreground or not.
- *
- * Note this information is based on the UID proc state callback, meaning it's updated
- * asynchronously and may subtly be stale. If the fresh data is needed, use
- * {@link ActivityManagerInternal#getUidProcessState} instead.
- */
- public boolean isUidInForeground(int uid) {
- if (UserHandle.isCore(uid)) {
- return true;
- }
- synchronized (mLock) {
- return mForegroundUids.get(uid);
- }
- }
-
- /**
* @return whether force all apps standby is enabled or not.
*/
public boolean isForceAllAppsStandbyEnabled() {
@@ -1315,9 +1239,6 @@
pw.print("Active uids: ");
dumpUids(pw, mActiveUids);
- pw.print("Foreground uids: ");
- dumpUids(pw, mForegroundUids);
-
pw.print("Except-idle + user exemption list appids: ");
pw.println(Arrays.toString(mPowerExemptAllAppIds));
@@ -1395,12 +1316,6 @@
}
}
- for (int i = 0; i < mForegroundUids.size(); i++) {
- if (mForegroundUids.valueAt(i)) {
- proto.write(AppStateTrackerProto.FOREGROUND_UIDS, mForegroundUids.keyAt(i));
- }
- }
-
for (int appId : mPowerExemptAllAppIds) {
proto.write(AppStateTrackerProto.POWER_SAVE_EXEMPT_APP_IDS, appId);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index aa46cfd..d44169d1 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -90,7 +90,6 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
-import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -152,7 +151,8 @@
static final boolean DEBUG_BG_LIMIT = localLOGV || false;
static final boolean DEBUG_STANDBY = localLOGV || false;
static final boolean RECORD_ALARMS_IN_HISTORY = true;
- static final boolean RECORD_DEVICE_IDLE_ALARMS = false;
+ // TODO (b/178484639): Turn off once allow-while-idle revamp is completed.
+ static final boolean RECORD_DEVICE_IDLE_ALARMS = true;
static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
static final int TICK_HISTORY_DEPTH = 10;
@@ -208,6 +208,7 @@
new ArrayList<>();
AlarmHandler mHandler;
AppWakeupHistory mAppWakeupHistory;
+ AppWakeupHistory mAllowWhileIdleHistory;
ClockReceiver mClockReceiver;
final DeliveryTracker mDeliveryTracker = new DeliveryTracker();
IBinder.DeathRecipient mListenerDeathRecipient;
@@ -230,19 +231,6 @@
*/
int mSystemUiUid;
- /**
- * For each uid, this is the last time we dispatched an "allow while idle" alarm,
- * used to determine the earliest we can dispatch the next such alarm. Times are in the
- * 'elapsed' timebase.
- */
- final SparseLongArray mLastAllowWhileIdleDispatch = new SparseLongArray();
-
- /**
- * For each uid, we store whether the last allow-while-idle alarm was dispatched while
- * the uid was in foreground or not. We will use the allow_while_idle_short_time in such cases.
- */
- final SparseBooleanArray mUseAllowWhileIdleShortTime = new SparseBooleanArray();
-
static boolean isTimeTickAlarm(Alarm a) {
return a.uid == Process.SYSTEM_UID && TIME_TICK_TAG.equals(a.listenerTag);
}
@@ -290,9 +278,11 @@
private boolean mAppStandbyParole;
/**
- * A rolling window history of previous times when an alarm was sent to a package.
+ * A container to keep rolling window history of previous times when an alarm was sent to
+ * a package.
*/
- private static class AppWakeupHistory {
+ @VisibleForTesting
+ static class AppWakeupHistory {
private ArrayMap<Pair<String, Integer>, LongArrayQueue> mPackageHistory =
new ArrayMap<>();
private long mWindowSize;
@@ -353,7 +343,6 @@
}
void dump(IndentingPrintWriter pw, long nowElapsed) {
- pw.println("App Alarm history:");
pw.increaseIndent();
for (int i = 0; i < mPackageHistory.size(); i++) {
final Pair<String, Integer> packageUser = mPackageHistory.keyAt(i);
@@ -389,10 +378,6 @@
@VisibleForTesting
static final String KEY_MAX_INTERVAL = "max_interval";
@VisibleForTesting
- static final String KEY_ALLOW_WHILE_IDLE_SHORT_TIME = "allow_while_idle_short_time";
- @VisibleForTesting
- static final String KEY_ALLOW_WHILE_IDLE_LONG_TIME = "allow_while_idle_long_time";
- @VisibleForTesting
static final String KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION
= "allow_while_idle_whitelist_duration";
@VisibleForTesting
@@ -422,11 +407,12 @@
private static final String KEY_TIME_TICK_ALLOWED_WHILE_IDLE =
"time_tick_allowed_while_idle";
+ @VisibleForTesting
+ static final String KEY_ALLOW_WHILE_IDLE_QUOTA = "allow_while_idle_quota";
+
private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
private static final long DEFAULT_MAX_INTERVAL = 365 * DateUtils.DAY_IN_MILLIS;
- private static final long DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME = DEFAULT_MIN_FUTURITY;
- private static final long DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME = 9 * 60 * 1000;
private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10 * 1000;
private static final long DEFAULT_LISTENER_TIMEOUT = 5 * 1000;
private static final int DEFAULT_MAX_ALARMS_PER_UID = 500;
@@ -447,6 +433,9 @@
private static final boolean DEFAULT_LAZY_BATCHING = true;
private static final boolean DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE = true;
+ private static final int DEFAULT_ALLOW_WHILE_IDLE_QUOTA = 7;
+ public static final long ALLOW_WHILE_IDLE_WINDOW = 60 * 60 * 1000; // 1 hour.
+
// Minimum futurity of a new alarm
public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
@@ -456,12 +445,6 @@
// Maximum alarm recurrence interval
public long MAX_INTERVAL = DEFAULT_MAX_INTERVAL;
- // Minimum time between ALLOW_WHILE_IDLE alarms when system is not idle.
- public long ALLOW_WHILE_IDLE_SHORT_TIME = DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME;
-
- // Minimum time between ALLOW_WHILE_IDLE alarms when system is idling.
- public long ALLOW_WHILE_IDLE_LONG_TIME = DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME;
-
// BroadcastOptions.setTemporaryAppWhitelistDuration() to use for FLAG_ALLOW_WHILE_IDLE.
public long ALLOW_WHILE_IDLE_WHITELIST_DURATION
= DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
@@ -478,6 +461,8 @@
public boolean LAZY_BATCHING = DEFAULT_LAZY_BATCHING;
public boolean TIME_TICK_ALLOWED_WHILE_IDLE = DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE;
+ public int ALLOW_WHILE_IDLE_QUOTA = DEFAULT_ALLOW_WHILE_IDLE_QUOTA;
+
private long mLastAllowWhileIdleWhitelistDuration = -1;
Constants() {
@@ -523,15 +508,13 @@
MAX_INTERVAL = properties.getLong(
KEY_MAX_INTERVAL, DEFAULT_MAX_INTERVAL);
break;
- case KEY_ALLOW_WHILE_IDLE_SHORT_TIME:
- ALLOW_WHILE_IDLE_SHORT_TIME = properties.getLong(
- KEY_ALLOW_WHILE_IDLE_SHORT_TIME,
- DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME);
- break;
- case KEY_ALLOW_WHILE_IDLE_LONG_TIME:
- ALLOW_WHILE_IDLE_LONG_TIME = properties.getLong(
- KEY_ALLOW_WHILE_IDLE_LONG_TIME,
- DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME);
+ case KEY_ALLOW_WHILE_IDLE_QUOTA:
+ ALLOW_WHILE_IDLE_QUOTA = properties.getInt(KEY_ALLOW_WHILE_IDLE_QUOTA,
+ DEFAULT_ALLOW_WHILE_IDLE_QUOTA);
+ if (ALLOW_WHILE_IDLE_QUOTA <= 0) {
+ Slog.w(TAG, "Cannot have allow-while-idle quota lower than 1.");
+ ALLOW_WHILE_IDLE_QUOTA = 1;
+ }
break;
case KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION:
ALLOW_WHILE_IDLE_WHITELIST_DURATION = properties.getLong(
@@ -660,14 +643,11 @@
TimeUtils.formatDuration(LISTENER_TIMEOUT, pw);
pw.println();
- pw.print(KEY_ALLOW_WHILE_IDLE_SHORT_TIME);
- pw.print("=");
- TimeUtils.formatDuration(ALLOW_WHILE_IDLE_SHORT_TIME, pw);
+ pw.print("allow_while_idle_window=");
+ TimeUtils.formatDuration(ALLOW_WHILE_IDLE_WINDOW, pw);
pw.println();
- pw.print(KEY_ALLOW_WHILE_IDLE_LONG_TIME);
- pw.print("=");
- TimeUtils.formatDuration(ALLOW_WHILE_IDLE_LONG_TIME, pw);
+ pw.print(KEY_ALLOW_WHILE_IDLE_QUOTA, ALLOW_WHILE_IDLE_QUOTA);
pw.println();
pw.print(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION);
@@ -675,9 +655,8 @@
TimeUtils.formatDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION, pw);
pw.println();
- pw.print(KEY_MAX_ALARMS_PER_UID);
- pw.print("=");
- pw.println(MAX_ALARMS_PER_UID);
+ pw.print(KEY_MAX_ALARMS_PER_UID, MAX_ALARMS_PER_UID);
+ pw.println();
pw.print(KEY_APP_STANDBY_WINDOW);
pw.print("=");
@@ -685,14 +664,12 @@
pw.println();
for (int i = 0; i < KEYS_APP_STANDBY_QUOTAS.length; i++) {
- pw.print(KEYS_APP_STANDBY_QUOTAS[i]);
- pw.print("=");
- pw.println(APP_STANDBY_QUOTAS[i]);
+ pw.print(KEYS_APP_STANDBY_QUOTAS[i], APP_STANDBY_QUOTAS[i]);
+ pw.println();
}
- pw.print(KEY_APP_STANDBY_RESTRICTED_QUOTA);
- pw.print("=");
- pw.println(APP_STANDBY_RESTRICTED_QUOTA);
+ pw.print(KEY_APP_STANDBY_RESTRICTED_QUOTA, APP_STANDBY_RESTRICTED_QUOTA);
+ pw.println();
pw.print(KEY_APP_STANDBY_RESTRICTED_WINDOW);
pw.print("=");
@@ -715,10 +692,6 @@
proto.write(ConstantsProto.MIN_INTERVAL_DURATION_MS, MIN_INTERVAL);
proto.write(ConstantsProto.MAX_INTERVAL_DURATION_MS, MAX_INTERVAL);
proto.write(ConstantsProto.LISTENER_TIMEOUT_DURATION_MS, LISTENER_TIMEOUT);
- proto.write(ConstantsProto.ALLOW_WHILE_IDLE_SHORT_DURATION_MS,
- ALLOW_WHILE_IDLE_SHORT_TIME);
- proto.write(ConstantsProto.ALLOW_WHILE_IDLE_LONG_DURATION_MS,
- ALLOW_WHILE_IDLE_LONG_TIME);
proto.write(ConstantsProto.ALLOW_WHILE_IDLE_WHITELIST_DURATION_MS,
ALLOW_WHILE_IDLE_WHITELIST_DURATION);
@@ -1268,6 +1241,7 @@
mAlarmStore.setAlarmClockRemovalListener(mAlarmClockUpdater);
mAppWakeupHistory = new AppWakeupHistory(Constants.DEFAULT_APP_STANDBY_WINDOW);
+ mAllowWhileIdleHistory = new AppWakeupHistory(Constants.ALLOW_WHILE_IDLE_WINDOW);
mNextWakeup = mNextNonWakeup = 0;
@@ -1636,25 +1610,28 @@
return alarm.setPolicyElapsed(BATTERY_SAVER_POLICY_INDEX, nowElapsed);
}
- final long batterSaverPolicyElapsed;
+ final long batterySaverPolicyElapsed;
if ((alarm.flags & (AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED)) != 0) {
// Unrestricted.
- batterSaverPolicyElapsed = nowElapsed;
+ batterySaverPolicyElapsed = nowElapsed;
} else if ((alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
// Allowed but limited.
- final long minDelay;
- if (mUseAllowWhileIdleShortTime.get(alarm.creatorUid)) {
- minDelay = mConstants.ALLOW_WHILE_IDLE_SHORT_TIME;
+ final int userId = UserHandle.getUserId(alarm.creatorUid);
+ final int quota = mConstants.ALLOW_WHILE_IDLE_QUOTA;
+ final int dispatchesInWindow = mAllowWhileIdleHistory.getTotalWakeupsInWindow(
+ alarm.sourcePackage, userId);
+ if (dispatchesInWindow < quota) {
+ // fine to go out immediately.
+ batterySaverPolicyElapsed = nowElapsed;
} else {
- minDelay = mConstants.ALLOW_WHILE_IDLE_LONG_TIME;
+ batterySaverPolicyElapsed = mAllowWhileIdleHistory.getNthLastWakeupForPackage(
+ alarm.sourcePackage, userId, quota) + Constants.ALLOW_WHILE_IDLE_WINDOW;
}
- final long lastDispatch = mLastAllowWhileIdleDispatch.get(alarm.creatorUid, 0);
- batterSaverPolicyElapsed = (lastDispatch == 0) ? nowElapsed : lastDispatch + minDelay;
} else {
// Not allowed.
- batterSaverPolicyElapsed = nowElapsed + INDEFINITE_DELAY;
+ batterySaverPolicyElapsed = nowElapsed + INDEFINITE_DELAY;
}
- return alarm.setPolicyElapsed(BATTERY_SAVER_POLICY_INDEX, batterSaverPolicyElapsed);
+ return alarm.setPolicyElapsed(BATTERY_SAVER_POLICY_INDEX, batterySaverPolicyElapsed);
}
/**
@@ -1676,9 +1653,18 @@
deviceIdlePolicyTime = nowElapsed;
} else if ((alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
// Allowed but limited.
- final long lastDispatch = mLastAllowWhileIdleDispatch.get(alarm.creatorUid, 0);
- deviceIdlePolicyTime = (lastDispatch == 0) ? nowElapsed
- : lastDispatch + mConstants.ALLOW_WHILE_IDLE_LONG_TIME;
+ final int userId = UserHandle.getUserId(alarm.creatorUid);
+ final int quota = mConstants.ALLOW_WHILE_IDLE_QUOTA;
+ final int dispatchesInWindow = mAllowWhileIdleHistory.getTotalWakeupsInWindow(
+ alarm.sourcePackage, userId);
+ if (dispatchesInWindow < quota) {
+ // fine to go out immediately.
+ deviceIdlePolicyTime = nowElapsed;
+ } else {
+ final long whenInQuota = mAllowWhileIdleHistory.getNthLastWakeupForPackage(
+ alarm.sourcePackage, userId, quota) + Constants.ALLOW_WHILE_IDLE_WINDOW;
+ deviceIdlePolicyTime = Math.min(whenInQuota, mPendingIdleUntil.getWhenElapsed());
+ }
} else {
// Not allowed.
deviceIdlePolicyTime = mPendingIdleUntil.getWhenElapsed();
@@ -1723,11 +1709,11 @@
if (wakeupsInWindow >= quotaForBucket) {
final long minElapsed;
if (quotaForBucket <= 0) {
- // Just keep deferring for a day till the quota changes
- minElapsed = nowElapsed + MILLIS_IN_DAY;
+ // Just keep deferring indefinitely till the quota changes.
+ minElapsed = nowElapsed + INDEFINITE_DELAY;
} else {
// Suppose the quota for window was q, and the qth last delivery time for this
- // package was t(q) then the next delivery must be after t(q) + <window_size>
+ // package was t(q) then the next delivery must be after t(q) + <window_size>.
final long t = mAppWakeupHistory.getNthLastWakeupForPackage(
sourcePackage, sourceUserId, quotaForBucket);
minElapsed = t + mConstants.APP_STANDBY_WINDOW;
@@ -1748,17 +1734,10 @@
ent.uid = a.uid;
ent.pkg = a.operation.getCreatorPackage();
ent.tag = a.operation.getTag("");
- ent.op = "SET";
+ ent.op = "START IDLE";
ent.elapsedRealtime = mInjector.getElapsedRealtime();
ent.argRealtime = a.getWhenElapsed();
mAllowWhileIdleDispatches.add(ent);
- if (mPendingIdleUntil == null) {
- IdleDispatchEntry ent2 = new IdleDispatchEntry();
- ent2.uid = 0;
- ent2.pkg = "START IDLE";
- ent2.elapsedRealtime = mInjector.getElapsedRealtime();
- mAllowWhileIdleDispatches.add(ent2);
- }
}
if ((mPendingIdleUntil != a) && (mPendingIdleUntil != null)) {
Slog.wtfStack(TAG, "setImplLocked: idle until changed from " + mPendingIdleUntil
@@ -2182,6 +2161,7 @@
pw.println("]");
pw.println();
+ pw.println("App Alarm history:");
mAppWakeupHistory.dump(pw, nowELAPSED);
if (mPendingIdleUntil != null) {
@@ -2259,30 +2239,8 @@
pw.println();
}
- if (mLastAllowWhileIdleDispatch.size() > 0) {
- pw.println("Last allow while idle dispatch times:");
- pw.increaseIndent();
- for (int i = 0; i < mLastAllowWhileIdleDispatch.size(); i++) {
- pw.print("UID ");
- final int uid = mLastAllowWhileIdleDispatch.keyAt(i);
- UserHandle.formatUid(pw, uid);
- pw.print(": ");
- final long lastTime = mLastAllowWhileIdleDispatch.valueAt(i);
- TimeUtils.formatDuration(lastTime, nowELAPSED, pw);
- pw.println();
- }
- pw.decreaseIndent();
- }
-
- pw.print("mUseAllowWhileIdleShortTime: [");
- for (int i = 0; i < mUseAllowWhileIdleShortTime.size(); i++) {
- if (mUseAllowWhileIdleShortTime.valueAt(i)) {
- UserHandle.formatUid(pw, mUseAllowWhileIdleShortTime.keyAt(i));
- pw.print(" ");
- }
- }
- pw.println("]");
- pw.println();
+ pw.println("Allow while idle history:");
+ mAllowWhileIdleHistory.dump(pw, nowELAPSED);
if (mLog.dump(pw, "Recent problems:")) {
pw.println();
@@ -2533,25 +2491,6 @@
f.dumpDebug(proto, AlarmManagerServiceDumpProto.OUTSTANDING_DELIVERIES);
}
- for (int i = 0; i < mLastAllowWhileIdleDispatch.size(); ++i) {
- final long token = proto.start(
- AlarmManagerServiceDumpProto.LAST_ALLOW_WHILE_IDLE_DISPATCH_TIMES);
- final int uid = mLastAllowWhileIdleDispatch.keyAt(i);
- final long lastTime = mLastAllowWhileIdleDispatch.valueAt(i);
-
- proto.write(AlarmManagerServiceDumpProto.LastAllowWhileIdleDispatch.UID, uid);
- proto.write(AlarmManagerServiceDumpProto.LastAllowWhileIdleDispatch.TIME_MS,
- lastTime);
- proto.end(token);
- }
-
- for (int i = 0; i < mUseAllowWhileIdleShortTime.size(); i++) {
- if (mUseAllowWhileIdleShortTime.valueAt(i)) {
- proto.write(AlarmManagerServiceDumpProto.USE_ALLOW_WHILE_IDLE_SHORT_TIME,
- mUseAllowWhileIdleShortTime.keyAt(i));
- }
- }
-
mLog.dumpDebug(proto, AlarmManagerServiceDumpProto.RECENT_PROBLEMS);
final FilterStats[] topFilters = new FilterStats[10];
@@ -3049,11 +2988,6 @@
mPendingBackgroundAlarms.removeAt(i);
}
}
- for (int i = mLastAllowWhileIdleDispatch.size() - 1; i >= 0; i--) {
- if (UserHandle.getUserId(mLastAllowWhileIdleDispatch.keyAt(i)) == userHandle) {
- mLastAllowWhileIdleDispatch.removeAt(i);
- }
- }
if (mNextWakeFromIdle != null && whichAlarms.test(mNextWakeFromIdle)) {
mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm();
if (mPendingIdleUntil != null) {
@@ -3215,6 +3149,16 @@
if (mPendingIdleUntil == alarm) {
mPendingIdleUntil = null;
mAlarmStore.updateAlarmDeliveries(a -> adjustDeliveryTimeBasedOnDeviceIdle(a));
+ if (RECORD_DEVICE_IDLE_ALARMS) {
+ IdleDispatchEntry ent = new IdleDispatchEntry();
+ ent.uid = alarm.uid;
+ ent.pkg = alarm.operation.getCreatorPackage();
+ ent.tag = alarm.operation.getTag("");
+ ent.op = "END IDLE";
+ ent.elapsedRealtime = mInjector.getElapsedRealtime();
+ ent.argRealtime = alarm.getWhenElapsed();
+ mAllowWhileIdleDispatches.add(ent);
+ }
}
if (mNextWakeFromIdle == alarm) {
mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm();
@@ -3829,7 +3773,6 @@
IntentFilter sdFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
sdFilter.addAction(Intent.ACTION_USER_STOPPED);
- sdFilter.addAction(Intent.ACTION_UID_REMOVED);
getContext().registerReceiver(this, sdFilter);
}
@@ -3856,12 +3799,7 @@
if (userHandle >= 0) {
removeUserLocked(userHandle);
mAppWakeupHistory.removeForUser(userHandle);
- }
- return;
- case Intent.ACTION_UID_REMOVED:
- if (uid >= 0) {
- mLastAllowWhileIdleDispatch.delete(uid);
- mUseAllowWhileIdleShortTime.delete(uid);
+ mAllowWhileIdleHistory.removeForUser(userHandle);
}
return;
case Intent.ACTION_PACKAGE_REMOVED:
@@ -3885,6 +3823,7 @@
if (uid >= 0) {
// package-removed and package-restarted case
mAppWakeupHistory.removeForPackage(pkg, UserHandle.getUserId(uid));
+ mAllowWhileIdleHistory.removeForPackage(pkg, UserHandle.getUserId(uid));
removeLocked(uid);
} else {
// external-applications-unavailable case
@@ -3980,23 +3919,6 @@
}
@Override
- public void onUidForeground(int uid, boolean foreground) {
- synchronized (mLock) {
- if (foreground) {
- mUseAllowWhileIdleShortTime.put(uid, true);
- if (mAlarmStore.updateAlarmDeliveries(a -> {
- if (a.creatorUid != uid || (a.flags & FLAG_ALLOW_WHILE_IDLE) == 0) {
- return false;
- }
- return adjustDeliveryTimeBasedOnBatterySaver(a);
- })) {
- rescheduleKernelAlarmsLocked();
- }
- }
- }
- }
-
- @Override
public void removeAlarmsForUid(int uid) {
synchronized (mLock) {
removeForStoppedLocked(uid);
@@ -4273,22 +4195,23 @@
notifyBroadcastAlarmPendingLocked(alarm.uid);
}
if (allowWhileIdle) {
- // Record the last time this uid handled an ALLOW_WHILE_IDLE alarm.
- mLastAllowWhileIdleDispatch.put(alarm.creatorUid, nowELAPSED);
- if ((mAppStateTracker == null)
- || mAppStateTracker.isUidInForeground(alarm.creatorUid)) {
- mUseAllowWhileIdleShortTime.put(alarm.creatorUid, true);
- } else {
- mUseAllowWhileIdleShortTime.put(alarm.creatorUid, false);
+ final boolean doze = (mPendingIdleUntil != null);
+ final boolean batterySaver = (mAppStateTracker != null
+ && mAppStateTracker.isForceAllAppsStandbyEnabled());
+ if (doze || batterySaver) {
+ // Record the last time this uid handled an ALLOW_WHILE_IDLE alarm while the
+ // device was in doze or battery saver.
+ mAllowWhileIdleHistory.recordAlarmForPackage(alarm.sourcePackage,
+ UserHandle.getUserId(alarm.creatorUid), nowELAPSED);
+ mAlarmStore.updateAlarmDeliveries(a -> {
+ if (a.creatorUid != alarm.creatorUid
+ || (a.flags & FLAG_ALLOW_WHILE_IDLE) == 0) {
+ return false;
+ }
+ return (doze && adjustDeliveryTimeBasedOnDeviceIdle(a))
+ || (batterySaver && adjustDeliveryTimeBasedOnBatterySaver(a));
+ });
}
- mAlarmStore.updateAlarmDeliveries(a -> {
- if (a.creatorUid != alarm.creatorUid
- || (a.flags & FLAG_ALLOW_WHILE_IDLE) == 0) {
- return false;
- }
- return adjustDeliveryTimeBasedOnDeviceIdle(a)
- | adjustDeliveryTimeBasedOnBatterySaver(a);
- });
if (RECORD_DEVICE_IDLE_ALARMS) {
IdleDispatchEntry ent = new IdleDispatchEntry();
ent.uid = alarm.uid;
@@ -4300,8 +4223,6 @@
}
}
if (!isExemptFromAppStandby(alarm)) {
- final Pair<String, Integer> packageUser = Pair.create(alarm.sourcePackage,
- UserHandle.getUserId(alarm.creatorUid));
mAppWakeupHistory.recordAlarmForPackage(alarm.sourcePackage,
UserHandle.getUserId(alarm.creatorUid), nowELAPSED);
}
diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp
index 2150cb4..52a6167 100644
--- a/apex/permission/framework/Android.bp
+++ b/apex/permission/framework/Android.bp
@@ -27,7 +27,10 @@
defaults: ["framework-module-defaults"],
// Restrict access to implementation library.
- impl_library_visibility: ["//frameworks/base/apex/permission:__subpackages__"],
+ impl_library_visibility: [
+ "//frameworks/base/apex/permission:__subpackages__",
+ "//packages/modules/Permission:__subpackages__",
+ ],
srcs: [
":framework-permission-sources",
diff --git a/apex/permission/service/Android.bp b/apex/permission/service/Android.bp
index b7f8082..d0fc5b9 100644
--- a/apex/permission/service/Android.bp
+++ b/apex/permission/service/Android.bp
@@ -78,6 +78,7 @@
"//frameworks/base/apex/permission/tests",
"//frameworks/base/services/tests/mockingservicestests",
"//frameworks/base/services/tests/servicestests",
+ "//packages/modules/Permission/tests",
],
srcs: [
":service-permission-sources",
diff --git a/core/api/current.txt b/core/api/current.txt
index e8174c0..f49ce1f 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -12344,6 +12344,8 @@
field public static final String FEATURE_GAMEPAD = "android.hardware.gamepad";
field public static final String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors";
field public static final String FEATURE_HOME_SCREEN = "android.software.home_screen";
+ field public static final String FEATURE_IDENTITY_CREDENTIAL_HARDWARE = "android.hardware.identity_credential";
+ field public static final String FEATURE_IDENTITY_CREDENTIAL_HARDWARE_DIRECT_ACCESS = "android.hardware.identity_credential_direct_access";
field public static final String FEATURE_INPUT_METHODS = "android.software.input_methods";
field public static final String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
field public static final String FEATURE_IRIS = "android.hardware.biometrics.iris";
@@ -25626,6 +25628,8 @@
method public void applyTransportModeTransform(@NonNull java.net.Socket, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
method public void applyTransportModeTransform(@NonNull java.net.DatagramSocket, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
method public void applyTransportModeTransform(@NonNull java.io.FileDescriptor, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
+ method @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
+ method @NonNull @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method public void removeTransportModeTransforms(@NonNull java.net.Socket) throws java.io.IOException;
@@ -25635,6 +25639,12 @@
field public static final int DIRECTION_OUT = 1; // 0x1
}
+ public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
+ method @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public void addAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
+ method public void close();
+ method @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public void removeAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
+ }
+
public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException {
}
@@ -36708,15 +36718,20 @@
public abstract class IdentityCredential {
method @NonNull public abstract java.security.KeyPair createEphemeralKeyPair();
method @NonNull public abstract byte[] decryptMessageFromReader(@NonNull byte[]) throws android.security.identity.MessageDecryptionException;
+ method @NonNull public byte[] delete(@NonNull byte[]);
method @NonNull public abstract byte[] encryptMessageToReader(@NonNull byte[]);
method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getAuthKeysNeedingCertification();
method @NonNull public abstract int[] getAuthenticationDataUsageCount();
method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getCredentialKeyCertificateChain();
method @NonNull public abstract android.security.identity.ResultData getEntries(@Nullable byte[], @NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>, @Nullable byte[], @Nullable byte[]) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException, android.security.identity.SessionTranscriptMismatchException;
+ method @NonNull public byte[] proveOwnership(@NonNull byte[]);
method public abstract void setAllowUsingExhaustedKeys(boolean);
+ method public void setAllowUsingExpiredKeys(boolean);
method public abstract void setAvailableAuthenticationKeys(int, int);
method public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
- method public abstract void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
+ method @Deprecated public abstract void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
+ method public void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull java.time.Instant, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
+ method @NonNull public byte[] update(@NonNull android.security.identity.PersonalizationData);
}
public class IdentityCredentialException extends java.lang.Exception {
@@ -36726,7 +36741,7 @@
public abstract class IdentityCredentialStore {
method @NonNull public abstract android.security.identity.WritableIdentityCredential createCredential(@NonNull String, @NonNull String) throws android.security.identity.AlreadyPersonalizedException, android.security.identity.DocTypeNotSupportedException;
- method @Nullable public abstract byte[] deleteCredentialByName(@NonNull String);
+ method @Deprecated @Nullable public abstract byte[] deleteCredentialByName(@NonNull String);
method @Nullable public abstract android.security.identity.IdentityCredential getCredentialByName(@NonNull String, int) throws android.security.identity.CipherSuiteNotSupportedException;
method @Nullable public static android.security.identity.IdentityCredentialStore getDirectAccessInstance(@NonNull android.content.Context);
method @Nullable public static android.security.identity.IdentityCredentialStore getInstance(@NonNull android.content.Context);
@@ -40208,6 +40223,7 @@
field public static final String KEY_RTT_DOWNGRADE_SUPPORTED_BOOL = "rtt_downgrade_supported_bool";
field public static final String KEY_RTT_SUPPORTED_BOOL = "rtt_supported_bool";
field public static final String KEY_RTT_SUPPORTED_FOR_VT_BOOL = "rtt_supported_for_vt_bool";
+ field public static final String KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL = "rtt_supported_while_roaming_bool";
field public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool";
field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool";
field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool";
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index cb2810c..0915d07 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -160,6 +160,10 @@
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
}
+ public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
+ method public int getResourceId();
+ }
+
public final class NetworkCapabilities implements android.os.Parcelable {
field public static final int TRANSPORT_TEST = 7; // 0x7
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d34f956..2a2b799 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -217,6 +217,7 @@
field public static final String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE";
field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
+ field public static final String RESTART_WIFI_SUBSYSTEM = "android.permission.RESTART_WIFI_SUBSYSTEM";
field public static final String RESTORE_RUNTIME_PERMISSIONS = "android.permission.RESTORE_RUNTIME_PERMISSIONS";
field public static final String RESTRICTED_VR_ACCESS = "android.permission.RESTRICTED_VR_ACCESS";
field public static final String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
@@ -1624,6 +1625,15 @@
}
+package android.apphibernation {
+
+ public final class AppHibernationManager {
+ method public boolean isHibernating(@NonNull String);
+ method public void setHibernating(@NonNull String, boolean);
+ }
+
+}
+
package android.bluetooth {
public final class BluetoothA2dp implements android.bluetooth.BluetoothProfile {
@@ -1920,6 +1930,7 @@
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle);
method public abstract void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
+ field public static final String APP_HIBERNATION_SERVICE = "app_hibernation";
field public static final String APP_INTEGRITY_SERVICE = "app_integrity";
field public static final String APP_PREDICTION_SERVICE = "app_prediction";
field public static final String BACKUP_SERVICE = "backup";
@@ -6926,15 +6937,11 @@
}
public final class IpSecManager {
- method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
- method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
}
public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
- method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void addAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
- method public void close();
method @NonNull public String getInterfaceName();
- method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void removeAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
}
public static class IpSecTransform.Builder {
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index 60bfac5..afa1560 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -50,6 +50,9 @@
# ResourcesManager
per-file ResourcesManager = rtmitchell@google.com, toddke@google.com
+# VoiceInteraction
+per-file *VoiceInteract* = file:/core/java/android/service/voice/OWNERS
+
# Wallpaper
per-file *Wallpaper* = file:/core/java/android/service/wallpaper/OWNERS
diff --git a/core/java/android/apphibernation/AppHibernationManager.java b/core/java/android/apphibernation/AppHibernationManager.java
new file mode 100644
index 0000000..8f1934c
--- /dev/null
+++ b/core/java/android/apphibernation/AppHibernationManager.java
@@ -0,0 +1,79 @@
+/*
+ * 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 android.apphibernation;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+/**
+ * This class provides an API surface for system apps to manipulate the app hibernation
+ * state of a package for the user provided in the context.
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.APP_HIBERNATION_SERVICE)
+public final class AppHibernationManager {
+ private static final String TAG = "AppHibernationManager";
+ private final Context mContext;
+ private final IAppHibernationService mIAppHibernationService;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param context The current context associated with the user
+ *
+ * @hide
+ */
+ public AppHibernationManager(@NonNull Context context) {
+ mContext = context;
+ mIAppHibernationService = IAppHibernationService.Stub.asInterface(
+ ServiceManager.getService(Context.APP_HIBERNATION_SERVICE));
+ }
+
+ /**
+ * Returns true if the package is hibernating, false otherwise.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isHibernating(@NonNull String packageName) {
+ try {
+ return mIAppHibernationService.isHibernating(packageName, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set whether the package is hibernating.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void setHibernating(@NonNull String packageName, boolean isHibernating) {
+ try {
+ mIAppHibernationService.setHibernating(packageName, mContext.getUserId(),
+ isHibernating);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/com/android/internal/net/VpnInfo.aidl b/core/java/android/apphibernation/IAppHibernationService.aidl
similarity index 62%
copy from core/java/com/android/internal/net/VpnInfo.aidl
copy to core/java/android/apphibernation/IAppHibernationService.aidl
index 6fc97be..db57ecb 100644
--- a/core/java/com/android/internal/net/VpnInfo.aidl
+++ b/core/java/android/apphibernation/IAppHibernationService.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -14,6 +14,13 @@
* limitations under the License.
*/
-package com.android.internal.net;
+package android.apphibernation;
-parcelable VpnInfo;
+/**
+ * Binder interface to communicate with AppHibernationService.
+ * @hide
+ */
+interface IAppHibernationService {
+ boolean isHibernating(String packageName, int userId);
+ void setHibernating(String packageName, int userId, boolean isHibernating);
+}
\ No newline at end of file
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index fcb0f4f..c5206d7 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4668,6 +4668,17 @@
public static final String PERMISSION_CONTROLLER_SERVICE = "permission_controller";
/**
+ * Use with {@link #getSystemService(String) to retrieve an
+ * {@link android.apphibernation.AppHibernationManager}} for
+ * communicating with the hibernation service.
+ * @hide
+ *
+ * @see #getSystemService(String)
+ */
+ @SystemApi
+ public static final String APP_HIBERNATION_SERVICE = "app_hibernation";
+
+ /**
* Use with {@link #getSystemService(String)} to retrieve an
* {@link android.app.backup.IBackupManager IBackupManager} for communicating
* with the backup mechanism.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 4ffdf67..fcd573e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2443,6 +2443,35 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature(String, int)}: If this feature is supported, the device supports
+ * {@link android.security.identity.IdentityCredentialStore} implemented in secure hardware
+ * at the given feature version.
+ *
+ * <p>Known feature versions include:
+ * <ul>
+ * <li><code>202009</code>: corresponds to the features included in the Identity Credential
+ * API shipped in Android 11.
+ * <li><code>202101</code>: corresponds to the features included in the Identity Credential
+ * API shipped in Android 12.
+ * </ul>
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_IDENTITY_CREDENTIAL_HARDWARE =
+ "android.hardware.identity_credential";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature(String, int)}: If this feature is supported, the device supports
+ * {@link android.security.identity.IdentityCredentialStore} implemented in secure hardware
+ * with direct access at the given feature version.
+ * See {@link #FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known feature versions.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_IDENTITY_CREDENTIAL_HARDWARE_DIRECT_ACCESS =
+ "android.hardware.identity_credential_direct_access";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports one or more methods of
* reporting current location.
*/
diff --git a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
index c3b6c05..c093489 100644
--- a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
+++ b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
@@ -32,6 +32,12 @@
// Hides the overlay.
void hideUdfpsOverlay(int sensorId);
+ // Notifies of enrollment progress changes.
+ void onEnrollmentProgress(int sensorId, int remaining);
+
+ // Notifies when a non-terminal error occurs (e.g. user moved their finger too fast).
+ void onEnrollmentHelp(int sensorId);
+
// Shows debug messages on the UDFPS overlay.
void setDebugMessage(int sensorId, String message);
}
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 4ec3f92..b90c728 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -503,6 +503,29 @@
@Retention(RetentionPolicy.SOURCE)
public @interface TvWakeOnOneTouchPlay {}
+ // -- Whether TV should send <Standby> on sleep.
+ /**
+ * Sending <Standby> on sleep.
+ *
+ * @hide
+ */
+ public static final int TV_SEND_STANDBY_ON_SLEEP_ENABLED = 1;
+ /**
+ * Not sending <Standby> on sleep.
+ *
+ * @hide
+ */
+ public static final int TV_SEND_STANDBY_ON_SLEEP_DISABLED = 0;
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "TV_SEND_STANDBY_ON_SLEEP_" }, value = {
+ TV_SEND_STANDBY_ON_SLEEP_ENABLED,
+ TV_SEND_STANDBY_ON_SLEEP_DISABLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TvSendStandbyOnSleep {}
+
// -- The RC profile of a TV panel.
/**
* RC profile none.
@@ -747,6 +770,14 @@
public static final String CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY =
"tv_wake_on_one_touch_play";
/**
+ * Name of a setting deciding whether the device will also turn off other CEC devices
+ * when it goes to standby mode.
+ *
+ * @hide
+ */
+ public static final String CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP =
+ "tv_send_standby_on_sleep";
+ /**
* Name of a setting representing the RC profile of a TV panel.
*
* @hide
@@ -805,12 +836,13 @@
CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
+ CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
CEC_SETTING_NAME_RC_PROFILE_TV,
CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
- CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU
+ CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU,
})
public @interface CecSettingName {}
@@ -2158,4 +2190,48 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Set the current status of TV send <Standby> on Sleep.
+ *
+ * <p>Sets whether the device will also turn off other CEC devices
+ * when it goes to standby mode.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void setTvSendStandbyOnSleep(@NonNull @TvSendStandbyOnSleep int value) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ mService.setCecSettingIntValue(CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP, value);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the current status of TV send <Standby> on Sleep.
+ *
+ * <p>Reflects whether the device will also turn off other CEC devices
+ * when it goes to standby mode.
+ *
+ * @hide
+ */
+ @NonNull
+ @TvSendStandbyOnSleep
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public int getTvSendStandbyOnSleep() {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ return mService.getCecSettingIntValue(CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 6fecee6..7197831 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -31,6 +31,7 @@
import android.net.NetworkState;
import android.net.ProxyInfo;
import android.net.UidRange;
+import android.net.VpnInfo;
import android.net.QosSocketInfo;
import android.os.Bundle;
import android.os.IBinder;
@@ -43,7 +44,6 @@
import com.android.connectivity.aidl.INetworkAgent;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
-import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
/**
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index 1a3dc97..d5aede7 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -23,11 +23,11 @@
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
+import android.net.VpnInfo;
import android.net.netstats.provider.INetworkStatsProvider;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.os.IBinder;
import android.os.Messenger;
-import com.android.internal.net.VpnInfo;
/** {@hide} */
interface INetworkStatsService {
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index d83715c..60923f5 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -15,6 +15,8 @@
*/
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.NonNull;
@@ -628,7 +630,7 @@
}
/** @hide */
- @VisibleForTesting
+ @SystemApi(client = MODULE_LIBRARIES)
public int getResourceId() {
return mResourceId;
}
@@ -705,7 +707,7 @@
}
/**
- * This class represents an IpSecTunnelInterface
+ * This class represents an IpSecTunnelInterface.
*
* <p>IpSecTunnelInterface objects track tunnel interfaces that serve as
* local endpoints for IPsec tunnels.
@@ -714,9 +716,7 @@
* applied to provide IPsec security to packets sent through the tunnel. While a tunnel
* cannot be used in standalone mode within Android, the higher layers may use the tunnel
* to create Network objects which are accessible to the Android system.
- * @hide
*/
- @SystemApi
public static final class IpSecTunnelInterface implements AutoCloseable {
private final String mOpPackageName;
private final IIpSecService mService;
@@ -727,23 +727,26 @@
private String mInterfaceName;
private int mResourceId = INVALID_RESOURCE_ID;
- /** Get the underlying SPI held by this object. */
+ /**
+ * Get the underlying SPI held by this object.
+ *
+ * @hide
+ */
+ @SystemApi
@NonNull
public String getInterfaceName() {
return mInterfaceName;
}
/**
- * Add an address to the IpSecTunnelInterface
+ * Add an address to the IpSecTunnelInterface.
*
* <p>Add an address which may be used as the local inner address for
* tunneled traffic.
*
* @param address the local address for traffic inside the tunnel
* @param prefixLen length of the InetAddress prefix
- * @hide
*/
- @SystemApi
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void addAddress(@NonNull InetAddress address, int prefixLen) throws IOException {
@@ -758,15 +761,13 @@
}
/**
- * Remove an address from the IpSecTunnelInterface
+ * Remove an address from the IpSecTunnelInterface.
*
- * <p>Remove an address which was previously added to the IpSecTunnelInterface
+ * <p>Remove an address which was previously added to the IpSecTunnelInterface.
*
* @param address to be removed
* @param prefixLen length of the InetAddress prefix
- * @hide
*/
- @SystemApi
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void removeAddress(@NonNull InetAddress address, int prefixLen) throws IOException {
@@ -817,7 +818,7 @@
}
/**
- * Delete an IpSecTunnelInterface
+ * Delete an IpSecTunnelInterface.
*
* <p>Calling close will deallocate the IpSecTunnelInterface and all of its system
* resources. Any packets bound for this interface either inbound or outbound will
@@ -839,7 +840,12 @@
}
}
- /** Check that the Interface was closed properly. */
+
+ /**
+ * Check that the Interface was closed properly.
+ *
+ * @hide
+ */
@Override
protected void finalize() throws Throwable {
if (mCloseGuard != null) {
@@ -871,17 +877,52 @@
* Create a new IpSecTunnelInterface as a local endpoint for tunneled IPsec traffic.
*
* <p>An application that creates tunnels is responsible for cleaning up the tunnel when the
- * underlying network goes away, and the onLost() callback is received.
+ * underlying network disconnects, and the {@link
+ * ConnectivityManager.NetworkCallback#onLost(Network)} callback is received.
*
- * @param localAddress The local addres of the tunnel
- * @param remoteAddress The local addres of the tunnel
- * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel.
- * This network should almost certainly be a network such as WiFi with an L2 address.
- * @return a new {@link IpSecManager#IpSecTunnelInterface} with the specified properties
- * @throws IOException indicating that the socket could not be opened or bound
- * @throws ResourceUnavailableException indicating that too many encapsulation sockets are open
- * @hide
+ * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel. Packets
+ * that go through the tunnel will need a underlying network to transit to the IPsec peer.
+ * This network should almost certainly be a physical network such as WiFi.
+ * @return a new {@link IpSecTunnelInterface} with the specified properties
+ * @throws IOException indicating that the tunnel could not be created due to a lower-layer
+ * error
+ * @throws ResourceUnavailableException indicating that the number of opening tunnels has
+ * reached the limit.
*/
+ @NonNull
+ @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
+ @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
+ public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull Network underlyingNetwork)
+ throws ResourceUnavailableException, IOException {
+
+ // TODO: Remove the need for adding two unused addresses with IPsec tunnels when {@link
+ // #createIpSecTunnelInterface(localAddress, remoteAddress, underlyingNetwork)} can be
+ // safely removed.
+ final InetAddress address = InetAddress.getLocalHost();
+ return createIpSecTunnelInterface(address, address, underlyingNetwork);
+ }
+
+ /**
+ * Create a new IpSecTunnelInterface as a local endpoint for tunneled IPsec traffic.
+ *
+ * <p>An application that creates tunnels is responsible for cleaning up the tunnel when the
+ * underlying network disconnects, and the {@link
+ * ConnectivityManager.NetworkCallback#onLost(Network)} callback is received.
+ *
+ * @param localAddress The local address of the tunnel
+ * @param remoteAddress The local address of the tunnel
+ * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel. Packets
+ * that go through the tunnel will need a underlying network to transit to the IPsec peer.
+ * This network should almost certainly be a physical network such as WiFi.
+ * @return a new {@link IpSecTunnelInterface} with the specified properties
+ * @throws IOException indicating that the tunnel could not be created due to a lower-layer
+ * error
+ * @throws ResourceUnavailableException indicating that the number of opening tunnels has
+ * reached the limit.
+ * @hide
+ * @deprecated Callers should use {@link #createIpSecTunnelInterface(Network)}
+ */
+ @Deprecated
@SystemApi
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@@ -905,16 +946,14 @@
* <p>Applications should probably not use this API directly.
*
*
- * @param tunnel The {@link IpSecManager#IpSecTunnelInterface} that will use the supplied
+ * @param tunnel The {@link IpSecTunnelInterface} that will use the supplied
* transform.
- * @param direction the direction, {@link DIRECTION_OUT} or {@link #DIRECTION_IN} in which
+ * @param direction the direction, {@link #DIRECTION_OUT} or {@link #DIRECTION_IN} in which
* the transform will be used.
* @param transform an {@link IpSecTransform} created in tunnel mode
- * @throws IOException indicating that the transform could not be applied due to a lower
- * layer failure.
- * @hide
+ * @throws IOException indicating that the transform could not be applied due to a lower-layer
+ * error
*/
- @SystemApi
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void applyTunnelModeTransform(@NonNull IpSecTunnelInterface tunnel,
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 53996a5..e829821 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -421,7 +421,7 @@
throw new SocketException("Only AF_INET/AF_INET6 sockets supported");
}
- final int err = NetworkUtils.bindSocketToNetwork(fd.getInt$(), netId);
+ final int err = NetworkUtils.bindSocketToNetwork(fd, netId);
if (err != 0) {
// bindSocketToNetwork returns negative errno.
throw new ErrnoException("Binding socket to network " + netId, -err)
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index e65c27c..06d0fc1 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -195,13 +195,15 @@
subscriberId = state.subscriberId;
if (type == TYPE_WIFI) {
- if (state.networkId != null) {
- networkId = state.networkId;
- } else {
- final WifiManager wifi = (WifiManager) context.getSystemService(
- Context.WIFI_SERVICE);
- final WifiInfo info = wifi.getConnectionInfo();
- networkId = info != null ? info.getSSID() : null;
+ if (state.networkCapabilities.getSsid() != null) {
+ networkId = state.networkCapabilities.getSsid();
+ if (networkId == null) {
+ // TODO: Figure out if this code path never runs. If so, remove them.
+ final WifiManager wifi = (WifiManager) context.getSystemService(
+ Context.WIFI_SERVICE);
+ final WifiInfo info = wifi.getConnectionInfo();
+ networkId = info != null ? info.getSSID() : null;
+ }
}
}
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index b5962c5..8be4af7 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -81,11 +81,11 @@
public native static boolean bindProcessToNetworkForHostResolution(int netId);
/**
- * Explicitly binds {@code socketfd} to the network designated by {@code netId}. This
+ * Explicitly binds {@code fd} to the network designated by {@code netId}. This
* overrides any binding via {@link #bindProcessToNetwork}.
* @return 0 on success or negative errno on failure.
*/
- public native static int bindSocketToNetwork(int socketfd, int netId);
+ public static native int bindSocketToNetwork(FileDescriptor fd, int netId);
/**
* Protect {@code fd} from VPN connections. After protecting, data sent through
@@ -93,9 +93,7 @@
* forwarded through the VPN.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static boolean protectFromVpn(FileDescriptor fd) {
- return protectFromVpn(fd.getInt$());
- }
+ public static native boolean protectFromVpn(FileDescriptor fd);
/**
* Protect {@code socketfd} from VPN connections. After protecting, data sent through
diff --git a/core/java/com/android/internal/net/VpnInfo.aidl b/core/java/android/net/VpnInfo.aidl
similarity index 94%
rename from core/java/com/android/internal/net/VpnInfo.aidl
rename to core/java/android/net/VpnInfo.aidl
index 6fc97be..8bcaa81 100644
--- a/core/java/com/android/internal/net/VpnInfo.aidl
+++ b/core/java/android/net/VpnInfo.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.internal.net;
+package android.net;
parcelable VpnInfo;
diff --git a/core/java/com/android/internal/net/VpnInfo.java b/core/java/android/net/VpnInfo.java
similarity index 63%
rename from core/java/com/android/internal/net/VpnInfo.java
rename to core/java/android/net/VpnInfo.java
index e74af5e..cf58c57 100644
--- a/core/java/com/android/internal/net/VpnInfo.java
+++ b/core/java/android/net/VpnInfo.java
@@ -11,11 +11,13 @@
* 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
+ * limitations under the License.
*/
-package com.android.internal.net;
+package android.net;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -23,14 +25,28 @@
/**
* A lightweight container used to carry information of the ongoing VPN.
- * Internal use only..
+ * Internal use only.
*
* @hide
*/
public class VpnInfo implements Parcelable {
- public int ownerUid;
- public String vpnIface;
- public String[] underlyingIfaces;
+ public final int ownerUid;
+ @Nullable
+ public final String vpnIface;
+ @Nullable
+ public final String[] underlyingIfaces;
+
+ public VpnInfo(int ownerUid, @Nullable String vpnIface, @Nullable String[] underlyingIfaces) {
+ this.ownerUid = ownerUid;
+ this.vpnIface = vpnIface;
+ this.underlyingIfaces = underlyingIfaces;
+ }
+
+ private VpnInfo(@NonNull Parcel in) {
+ this.ownerUid = in.readInt();
+ this.vpnIface = in.readString();
+ this.underlyingIfaces = in.createStringArray();
+ }
@Override
public String toString() {
@@ -47,22 +63,21 @@
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(ownerUid);
dest.writeString(vpnIface);
dest.writeStringArray(underlyingIfaces);
}
+ @NonNull
public static final Parcelable.Creator<VpnInfo> CREATOR = new Parcelable.Creator<VpnInfo>() {
+ @NonNull
@Override
- public VpnInfo createFromParcel(Parcel source) {
- VpnInfo info = new VpnInfo();
- info.ownerUid = source.readInt();
- info.vpnIface = source.readString();
- info.underlyingIfaces = source.readStringArray();
- return info;
+ public VpnInfo createFromParcel(@NonNull Parcel in) {
+ return new VpnInfo(in);
}
+ @NonNull
@Override
public VpnInfo[] newArray(int size) {
return new VpnInfo[size];
diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java
index ede8faa..5eb4ba6 100644
--- a/core/java/android/net/vcn/VcnConfig.java
+++ b/core/java/android/net/vcn/VcnConfig.java
@@ -96,7 +96,11 @@
return mPackageName;
}
- /** Retrieves the set of configured tunnels. */
+ /**
+ * Retrieves the set of configured tunnels.
+ *
+ * @hide
+ */
@NonNull
public Set<VcnGatewayConnectionConfig> getGatewayConnectionConfigs() {
return Collections.unmodifiableSet(mGatewayConnectionConfigs);
@@ -146,7 +150,7 @@
}
@Override
- public void writeToParcel(Parcel out, int flags) {
+ public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeParcelable(toPersistableBundle(), flags);
}
@@ -164,8 +168,12 @@
}
};
- /** This class is used to incrementally build {@link VcnConfig} objects. */
- public static class Builder {
+ /**
+ * This class is used to incrementally build {@link VcnConfig} objects.
+ *
+ * @hide
+ */
+ public static final class Builder {
@NonNull private final String mPackageName;
@NonNull
@@ -182,6 +190,7 @@
*
* @param gatewayConnectionConfig the configuration for an individual gateway connection
* @return this {@link Builder} instance, for chaining
+ * @hide
*/
@NonNull
public Builder addGatewayConnectionConfig(
@@ -196,6 +205,7 @@
* Builds and validates the VcnConfig.
*
* @return an immutable VcnConfig instance
+ * @hide
*/
@NonNull
public VcnConfig build() {
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index d531cdb..cead2f1 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -17,6 +17,7 @@
import static com.android.internal.annotations.VisibleForTesting.Visibility;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -25,14 +26,19 @@
import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
import com.android.server.vcn.util.PersistableBundleUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
/**
@@ -97,6 +103,26 @@
ALLOWED_CAPABILITIES = Collections.unmodifiableSet(allowedCaps);
}
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"NET_CAPABILITY_"},
+ value = {
+ NetworkCapabilities.NET_CAPABILITY_MMS,
+ NetworkCapabilities.NET_CAPABILITY_SUPL,
+ NetworkCapabilities.NET_CAPABILITY_DUN,
+ NetworkCapabilities.NET_CAPABILITY_FOTA,
+ NetworkCapabilities.NET_CAPABILITY_IMS,
+ NetworkCapabilities.NET_CAPABILITY_CBS,
+ NetworkCapabilities.NET_CAPABILITY_IA,
+ NetworkCapabilities.NET_CAPABILITY_RCS,
+ NetworkCapabilities.NET_CAPABILITY_XCAP,
+ NetworkCapabilities.NET_CAPABILITY_EIMS,
+ NetworkCapabilities.NET_CAPABILITY_INTERNET,
+ NetworkCapabilities.NET_CAPABILITY_MCX,
+ })
+ public @interface VcnSupportedCapability {}
+
private static final int DEFAULT_MAX_MTU = 1500;
/**
@@ -128,10 +154,10 @@
};
private static final String EXPOSED_CAPABILITIES_KEY = "mExposedCapabilities";
- @NonNull private final Set<Integer> mExposedCapabilities;
+ @NonNull private final SortedSet<Integer> mExposedCapabilities;
private static final String UNDERLYING_CAPABILITIES_KEY = "mUnderlyingCapabilities";
- @NonNull private final Set<Integer> mUnderlyingCapabilities;
+ @NonNull private final SortedSet<Integer> mUnderlyingCapabilities;
// TODO: Add Ike/ChildSessionParams as a subclass - maybe VcnIkeGatewayConnectionConfig
@@ -141,14 +167,14 @@
private static final String RETRY_INTERVAL_MS_KEY = "mRetryIntervalsMs";
@NonNull private final long[] mRetryIntervalsMs;
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- public VcnGatewayConnectionConfig(
+ /** Builds a VcnGatewayConnectionConfig with the specified parameters. */
+ private VcnGatewayConnectionConfig(
@NonNull Set<Integer> exposedCapabilities,
@NonNull Set<Integer> underlyingCapabilities,
@NonNull long[] retryIntervalsMs,
@IntRange(from = MIN_MTU_V6) int maxMtu) {
- mExposedCapabilities = exposedCapabilities;
- mUnderlyingCapabilities = underlyingCapabilities;
+ mExposedCapabilities = new TreeSet(exposedCapabilities);
+ mUnderlyingCapabilities = new TreeSet(underlyingCapabilities);
mRetryIntervalsMs = retryIntervalsMs;
mMaxMtu = maxMtu;
@@ -163,9 +189,9 @@
final PersistableBundle underlyingCapsBundle =
in.getPersistableBundle(UNDERLYING_CAPABILITIES_KEY);
- mExposedCapabilities = new ArraySet<>(PersistableBundleUtils.toList(
+ mExposedCapabilities = new TreeSet<>(PersistableBundleUtils.toList(
exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
- mUnderlyingCapabilities = new ArraySet<>(PersistableBundleUtils.toList(
+ mUnderlyingCapabilities = new TreeSet<>(PersistableBundleUtils.toList(
underlyingCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY);
mMaxMtu = in.getInt(MAX_MTU_KEY);
@@ -219,52 +245,93 @@
/**
* Returns all exposed capabilities.
*
+ * <p>The returned integer-value capabilities will not contain duplicates, and will be sorted in
+ * ascending numerical order.
+ *
+ * @see Builder#addExposedCapability(int)
+ * @see Builder#clearExposedCapability(int)
* @hide
*/
@NonNull
+ public int[] getExposedCapabilities() {
+ // Sorted set guarantees ordering
+ return ArrayUtils.convertToIntArray(new ArrayList<>(mExposedCapabilities));
+ }
+
+ /**
+ * Returns all exposed capabilities.
+ *
+ * <p>Left to prevent the need to make major changes while changes are actively in flight.
+ *
+ * @deprecated use getExposedCapabilities() instead
+ * @hide
+ */
+ @Deprecated
+ @NonNull
public Set<Integer> getAllExposedCapabilities() {
return Collections.unmodifiableSet(mExposedCapabilities);
}
/**
- * Checks if this config is configured to support/expose a specific capability.
+ * Returns all capabilities required of underlying networks.
*
- * @param capability the capability to check for
+ * <p>The returned integer-value capabilities will be sorted in ascending numerical order.
+ *
+ * @see Builder#addRequiredUnderlyingCapability(int)
+ * @see Builder#clearRequiredUnderlyingCapability(int)
+ * @hide
*/
- public boolean hasExposedCapability(int capability) {
- checkValidCapability(capability);
-
- return mExposedCapabilities.contains(capability);
+ @NonNull
+ public int[] getRequiredUnderlyingCapabilities() {
+ // Sorted set guarantees ordering
+ return ArrayUtils.convertToIntArray(new ArrayList<>(mUnderlyingCapabilities));
}
/**
* Returns all capabilities required of underlying networks.
*
+ * <p>Left to prevent the need to make major changes while changes are actively in flight.
+ *
+ * @deprecated use getRequiredUnderlyingCapabilities() instead
* @hide
*/
+ @Deprecated
@NonNull
public Set<Integer> getAllUnderlyingCapabilities() {
return Collections.unmodifiableSet(mUnderlyingCapabilities);
}
/**
- * Checks if this config requires an underlying network to have the specified capability.
+ * Retrieves the configured retry intervals.
*
- * @param capability the capability to check for
+ * @see Builder#setRetryInterval(long[])
+ * @hide
*/
- public boolean requiresUnderlyingCapability(int capability) {
- checkValidCapability(capability);
-
- return mUnderlyingCapabilities.contains(capability);
- }
-
- /** Retrieves the configured retry intervals. */
@NonNull
- public long[] getRetryIntervalsMs() {
+ public long[] getRetryInterval() {
return Arrays.copyOf(mRetryIntervalsMs, mRetryIntervalsMs.length);
}
- /** Retrieves the maximum MTU allowed for this Gateway Connection. */
+ /**
+ * Retrieves the configured retry intervals.
+ *
+ * <p>Left to prevent the need to make major changes while changes are actively in flight.
+ *
+ * @deprecated use getRequiredUnderlyingCapabilities() instead
+ * @hide
+ */
+ @Deprecated
+ @NonNull
+ public long[] getRetryIntervalsMs() {
+ return getRetryInterval();
+ }
+
+ /**
+ * Retrieves the maximum MTU allowed for this Gateway Connection.
+ *
+ * @see Builder.setMaxMtu(int)
+ * @hide
+ */
@IntRange(from = MIN_MTU_V6)
public int getMaxMtu() {
return mMaxMtu;
@@ -319,8 +386,12 @@
&& mMaxMtu == rhs.mMaxMtu;
}
- /** This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects. */
- public static class Builder {
+ /**
+ * This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects.
+ *
+ * @hide
+ */
+ public static final class Builder {
@NonNull private final Set<Integer> mExposedCapabilities = new ArraySet();
@NonNull private final Set<Integer> mUnderlyingCapabilities = new ArraySet();
@NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
@@ -338,8 +409,10 @@
* @return this {@link Builder} instance, for chaining
* @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway
* Connection
+ * @hide
*/
- public Builder addExposedCapability(int exposedCapability) {
+ @NonNull
+ public Builder addExposedCapability(@VcnSupportedCapability int exposedCapability) {
checkValidCapability(exposedCapability);
mExposedCapabilities.add(exposedCapability);
@@ -354,8 +427,10 @@
* @return this {@link Builder} instance, for chaining
* @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway
* Connection
+ * @hide
*/
- public Builder removeExposedCapability(int exposedCapability) {
+ @NonNull
+ public Builder clearExposedCapability(@VcnSupportedCapability int exposedCapability) {
checkValidCapability(exposedCapability);
mExposedCapabilities.remove(exposedCapability);
@@ -370,8 +445,11 @@
* @return this {@link Builder} instance, for chaining
* @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying
* networks
+ * @hide
*/
- public Builder addRequiredUnderlyingCapability(int underlyingCapability) {
+ @NonNull
+ public Builder addRequiredUnderlyingCapability(
+ @VcnSupportedCapability int underlyingCapability) {
checkValidCapability(underlyingCapability);
mUnderlyingCapabilities.add(underlyingCapability);
@@ -390,8 +468,11 @@
* @return this {@link Builder} instance, for chaining
* @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying
* networks
+ * @hide
*/
- public Builder removeRequiredUnderlyingCapability(int underlyingCapability) {
+ @NonNull
+ public Builder clearRequiredUnderlyingCapability(
+ @VcnSupportedCapability int underlyingCapability) {
checkValidCapability(underlyingCapability);
mUnderlyingCapabilities.remove(underlyingCapability);
@@ -420,6 +501,7 @@
* 15m]}
* @return this {@link Builder} instance, for chaining
* @see VcnManager for additional discussion on fail-safe mode
+ * @hide
*/
@NonNull
public Builder setRetryInterval(@NonNull long[] retryIntervalsMs) {
@@ -441,6 +523,7 @@
* @param maxMtu the maximum MTU allowed for this Gateway Connection. Must be greater than
* the IPv6 minimum MTU of 1280. Defaults to 1500.
* @return this {@link Builder} instance, for chaining
+ * @hide
*/
@NonNull
public Builder setMaxMtu(@IntRange(from = MIN_MTU_V6) int maxMtu) {
@@ -455,6 +538,7 @@
* Builds and validates the VcnGatewayConnectionConfig.
*
* @return an immutable VcnGatewayConnectionConfig instance
+ * @hide
*/
@NonNull
public VcnGatewayConnectionConfig build() {
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 2ccdc26..2d0a6d7 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -65,6 +65,7 @@
public final class VcnManager {
@NonNull private static final String TAG = VcnManager.class.getSimpleName();
+ /** @hide */
@VisibleForTesting
public static final Map<
VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
diff --git a/core/java/android/os/storage/OWNERS b/core/java/android/os/storage/OWNERS
index 7e17a08..ff126e1 100644
--- a/core/java/android/os/storage/OWNERS
+++ b/core/java/android/os/storage/OWNERS
@@ -1,10 +1,10 @@
# Bug component: 95221
-narayan@google.com
-nandana@google.com
corinac@google.com
+nandana@google.com
zezeozue@google.com
maco@google.com
sahanas@google.com
abkaur@google.com
chiangi@google.com
+narayan@google.com
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index db55e1c..b1b2925 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -88,10 +88,8 @@
private static final long DEFAULT_RECENT_TIME_MS = 30000L;
private static boolean shouldShowIndicators() {
- return true;
- // TODO ntmyren: remove true set when device config is configured correctly
- //DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- //PROPERTY_CAMERA_MIC_ICONS_ENABLED, true);
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_CAMERA_MIC_ICONS_ENABLED, true);
}
private static boolean shouldShowLocationIndicator() {
@@ -142,7 +140,7 @@
}
private Context mContext;
- private Map<UserHandle, Context> mUserContexts;
+ private ArrayMap<UserHandle, Context> mUserContexts;
private PackageManager mPkgManager;
private AppOpsManager mAppOpsManager;
@@ -154,7 +152,8 @@
mContext = context;
mPkgManager = context.getPackageManager();
mAppOpsManager = context.getSystemService(AppOpsManager.class);
- mUserContexts = Map.of(Process.myUserHandle(), mContext);
+ mUserContexts = new ArrayMap<>();
+ mUserContexts.put(Process.myUserHandle(), mContext);
}
private Context getUserContext(UserHandle user) {
diff --git a/core/java/android/util/OWNERS b/core/java/android/util/OWNERS
index 8f3d9f6..14aa386 100644
--- a/core/java/android/util/OWNERS
+++ b/core/java/android/util/OWNERS
@@ -1,3 +1,6 @@
per-file FeatureFlagUtils.java = sbasi@google.com
per-file FeatureFlagUtils.java = tmfang@google.com
per-file FeatureFlagUtils.java = asapperstein@google.com
+
+per-file TypedValue.java = file:/core/java/android/content/res/OWNERS
+per-file AttributeSet.java = file:/core/java/android/content/res/OWNERS
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 2155246..e2af87e 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -18,6 +18,7 @@
#include <vector>
+#include <android/file_descriptor_jni.h>
#include <arpa/inet.h>
#include <linux/filter.h>
#include <linux/if_arp.h>
@@ -83,7 +84,7 @@
filter_code,
};
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
@@ -93,7 +94,7 @@
static void android_net_utils_detachBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
{
int optval_ignored = 0;
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, &optval_ignored, sizeof(optval_ignored)) !=
0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
@@ -117,10 +118,9 @@
return (jboolean) !setNetworkForResolv(netId);
}
-static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jint socket,
- jint netId)
-{
- return setNetworkForSocket(netId, socket);
+static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jobject javaFd,
+ jint netId) {
+ return setNetworkForSocket(netId, AFileDescriptor_getFD(env, javaFd));
}
static jboolean android_net_utils_protectFromVpn(JNIEnv *env, jobject thiz, jint socket)
@@ -128,6 +128,10 @@
return (jboolean) !protectFromVpn(socket);
}
+static jboolean android_net_utils_protectFromVpnWithFd(JNIEnv *env, jobject thiz, jobject javaFd) {
+ return android_net_utils_protectFromVpn(env, thiz, AFileDescriptor_getFD(env, javaFd));
+}
+
static jboolean android_net_utils_queryUserAccess(JNIEnv *env, jobject thiz, jint uid, jint netId)
{
return (jboolean) !queryUserAccess(uid, netId);
@@ -178,7 +182,7 @@
}
static jobject android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, jobject javaFd) {
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
int rcode;
std::vector<uint8_t> buf(MAXPACKETSIZE, 0);
@@ -205,7 +209,7 @@
}
static void android_net_utils_resNetworkCancel(JNIEnv *env, jobject thiz, jobject javaFd) {
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
resNetworkCancel(fd);
jniSetFileDescriptorOfFD(env, javaFd, -1);
}
@@ -231,7 +235,7 @@
return NULL;
}
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
struct tcp_repair_window trw = {};
socklen_t size = sizeof(trw);
@@ -271,8 +275,9 @@
{ "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork },
{ "getBoundNetworkForProcess", "()I", (void*) android_net_utils_getBoundNetworkForProcess },
{ "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
- { "bindSocketToNetwork", "(II)I", (void*) android_net_utils_bindSocketToNetwork },
- { "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn },
+ { "bindSocketToNetwork", "(Ljava/io/FileDescriptor;I)I", (void*) android_net_utils_bindSocketToNetwork },
+ { "protectFromVpn", "(I)Z", (void*) android_net_utils_protectFromVpn },
+ { "protectFromVpn", "(Ljava/io/FileDescriptor;)Z", (void*) android_net_utils_protectFromVpnWithFd },
{ "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
{ "attachDropAllBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDropAllBPFFilter },
{ "detachBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_detachBPFFilter },
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 610e0e0..c359a7d 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -68,7 +68,7 @@
// Whether or not the home activity is the recents activity. This is needed for the CTS tests to
// know what activity types to check for when invoking splitscreen multi-window.
optional bool is_home_recents_component = 6;
- repeated IdentifierProto pending_activities = 7;
+ repeated IdentifierProto pending_activities = 7 [deprecated=true];
}
message BarControllerProto {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3975529..1b9455c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1744,6 +1744,12 @@
<permission android:name="android.permission.REQUEST_NETWORK_SCORES"
android:protectionLevel="signature|setup" />
+ <!-- Allows applications to restart the Wi-Fi subsystem.
+ @SystemApi
+ <p>Not for use by third-party applications. @hide -->
+ <permission android:name="android.permission.RESTART_WIFI_SUBSYSTEM"
+ android:protectionLevel="signature|setup" />
+
<!-- @SystemApi @hide Allows applications to toggle airplane mode.
<p>Not for use by third-party or privileged applications.
-->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 996fbb3..98b36c5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -298,10 +298,10 @@
<item>@string/crossSimFormat_spn_cross_sim_calling</item>
</string-array>
- <!-- Spn during Cross-SIM Calling: "<operator> " [CHAR LIMIT=NONE] -->
+ <!-- Spn during Backup Calling: "<operator> " [CHAR LIMIT=NONE] -->
<string name="crossSimFormat_spn"><xliff:g id="spn" example="Operator">%s</xliff:g></string>
- <!-- Spn during Cross SIM Calling: "<operator> Cross-SIM Calling" [CHAR LIMIT=NONE] -->
- <string name="crossSimFormat_spn_cross_sim_calling"><xliff:g id="spn" example="Operator">%s</xliff:g> Cross-SIM Calling</string>
+ <!-- Spn during Backup Calling: "<operator> Backup Calling" [CHAR LIMIT=NONE] -->
+ <string name="crossSimFormat_spn_cross_sim_calling"><xliff:g id="spn" example="Operator">%s</xliff:g> Backup Calling</string>
<!--
{0} is one of "bearerServiceCode*"
diff --git a/core/sysprop/WatchdogProperties.sysprop b/core/sysprop/WatchdogProperties.sysprop
index 1bcc773..93e8b78 100644
--- a/core/sysprop/WatchdogProperties.sysprop
+++ b/core/sysprop/WatchdogProperties.sysprop
@@ -16,7 +16,7 @@
owner: Platform
# To escape the watchdog timeout loop, fatal reboot the system when
-# watchdog timed out 'fatal_count' times in 'fatal_window_second'
+# watchdog timed out 'fatal_count' times in 'fatal_window_seconds'
# seconds, if both values are not 0. Default value of both is 0.
prop {
api_name: "fatal_count"
@@ -26,8 +26,9 @@
access: Readonly
}
+# See 'fatal_count' for documentation.
prop {
- api_name: "fatal_window_second"
+ api_name: "fatal_window_seconds"
type: Integer
prop_name: "framework_watchdog.fatal_window.second"
scope: Internal
@@ -35,9 +36,9 @@
}
# The fatal counting can be disabled by setting property
-# 'is_fatal_ignore' to true.
+# 'should_ignore_fatal_count' to true.
prop {
- api_name: "is_fatal_ignore"
+ api_name: "should_ignore_fatal_count"
type: Boolean
prop_name: "persist.debug.framework_watchdog.fatal_ignore"
scope: Internal
diff --git a/core/sysprop/api/com.android.sysprop.watchdog-latest.txt b/core/sysprop/api/com.android.sysprop.watchdog-latest.txt
index d901aef..c846211 100644
--- a/core/sysprop/api/com.android.sysprop.watchdog-latest.txt
+++ b/core/sysprop/api/com.android.sysprop.watchdog-latest.txt
@@ -7,13 +7,13 @@
prop_name: "framework_watchdog.fatal_count"
}
prop {
- api_name: "fatal_window_second"
+ api_name: "fatal_window_seconds"
type: Integer
scope: Internal
prop_name: "framework_watchdog.fatal_window.second"
}
prop {
- api_name: "is_fatal_ignore"
+ api_name: "should_ignore_fatal_count"
scope: Internal
prop_name: "persist.debug.framework_watchdog.fatal_ignore"
}
diff --git a/core/tests/coretests/src/android/app/OWNERS b/core/tests/coretests/src/android/app/OWNERS
index bd7da0c..b3f3993 100644
--- a/core/tests/coretests/src/android/app/OWNERS
+++ b/core/tests/coretests/src/android/app/OWNERS
@@ -1 +1,6 @@
per-file Window*.java = file:/services/core/java/com/android/server/wm/OWNERS
+
+# Notification, DND, Status bar
+per-file *Notification* = file:/packages/SystemUI/OWNERS
+per-file *Zen* = file:/packages/SystemUI/OWNERS
+per-file *StatusBar* = file:/packages/SystemUI/OWNERS
diff --git a/core/tests/coretests/src/android/app/people/OWNERS b/core/tests/coretests/src/android/app/people/OWNERS
new file mode 100644
index 0000000..6ec8e6a
--- /dev/null
+++ b/core/tests/coretests/src/android/app/people/OWNERS
@@ -0,0 +1 @@
+file:/core/java/android/app/people/OWNERS
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/content/pm/OWNERS b/core/tests/coretests/src/android/content/pm/OWNERS
index 711f5f0..7b76706 100644
--- a/core/tests/coretests/src/android/content/pm/OWNERS
+++ b/core/tests/coretests/src/android/content/pm/OWNERS
@@ -1,2 +1,3 @@
per-file AppSearchPersonTest.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS
-
+per-file SigningDetailsTest.java = mpgroover@google.com
+per-file SigningDetailsTest.java = cbrubaker@google.com
diff --git a/data/etc/com.android.emergency.xml b/data/etc/com.android.emergency.xml
index 28f99dd..734561c 100644
--- a/data/etc/com.android.emergency.xml
+++ b/data/etc/com.android.emergency.xml
@@ -19,5 +19,6 @@
<!-- Required to place emergency calls from emergency info screen. -->
<permission name="android.permission.CALL_PRIVILEGED"/>
<permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
</privapp-permissions>
</permissions>
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredential.java b/identity/java/android/security/identity/CredstoreIdentityCredential.java
index 7c0af6d..6398cee 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredential.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredential.java
@@ -37,6 +37,7 @@
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.time.Instant;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Map;
@@ -237,12 +238,18 @@
}
private boolean mAllowUsingExhaustedKeys = true;
+ private boolean mAllowUsingExpiredKeys = false;
@Override
public void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys) {
mAllowUsingExhaustedKeys = allowUsingExhaustedKeys;
}
+ @Override
+ public void setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys) {
+ mAllowUsingExpiredKeys = allowUsingExpiredKeys;
+ }
+
private boolean mOperationHandleSet = false;
private long mOperationHandle = 0;
@@ -256,7 +263,8 @@
public long getCredstoreOperationHandle() {
if (!mOperationHandleSet) {
try {
- mOperationHandle = mBinder.selectAuthKey(mAllowUsingExhaustedKeys);
+ mOperationHandle = mBinder.selectAuthKey(mAllowUsingExhaustedKeys,
+ mAllowUsingExpiredKeys);
mOperationHandleSet = true;
} catch (android.os.RemoteException e) {
throw new RuntimeException("Unexpected RemoteException ", e);
@@ -306,7 +314,8 @@
rnsParcels,
sessionTranscript != null ? sessionTranscript : new byte[0],
readerSignature != null ? readerSignature : new byte[0],
- mAllowUsingExhaustedKeys);
+ mAllowUsingExhaustedKeys,
+ mAllowUsingExpiredKeys);
} catch (android.os.RemoteException e) {
throw new RuntimeException("Unexpected RemoteException ", e);
} catch (android.os.ServiceSpecificException e) {
@@ -410,6 +419,34 @@
}
@Override
+ public void storeStaticAuthenticationData(X509Certificate authenticationKey,
+ Instant expirationDate,
+ byte[] staticAuthData)
+ throws UnknownAuthenticationKeyException {
+ try {
+ AuthKeyParcel authKeyParcel = new AuthKeyParcel();
+ authKeyParcel.x509cert = authenticationKey.getEncoded();
+ long millisSinceEpoch = (expirationDate.getEpochSecond() * 1000)
+ + (expirationDate.getNano() / 1000000);
+ mBinder.storeStaticAuthenticationDataWithExpiration(authKeyParcel,
+ millisSinceEpoch, staticAuthData);
+ } catch (CertificateEncodingException e) {
+ throw new RuntimeException("Error encoding authenticationKey", e);
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ if (e.errorCode == ICredentialStore.ERROR_NOT_SUPPORTED) {
+ throw new UnsupportedOperationException("Not supported", e);
+ } else if (e.errorCode == ICredentialStore.ERROR_AUTHENTICATION_KEY_NOT_FOUND) {
+ throw new UnknownAuthenticationKeyException(e.getMessage(), e);
+ } else {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+ }
+
+ @Override
public @NonNull int[] getAuthenticationDataUsageCount() {
try {
int[] usageCount = mBinder.getAuthenticationDataUsageCount();
@@ -421,4 +458,49 @@
+ e.errorCode, e);
}
}
+
+ @Override
+ public @NonNull byte[] proveOwnership(@NonNull byte[] challenge) {
+ try {
+ byte[] proofOfOwnership = mBinder.proveOwnership(challenge);
+ return proofOfOwnership;
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ if (e.errorCode == ICredentialStore.ERROR_NOT_SUPPORTED) {
+ throw new UnsupportedOperationException("Not supported", e);
+ } else {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+ }
+
+ @Override
+ public @NonNull byte[] delete(@NonNull byte[] challenge) {
+ try {
+ byte[] proofOfDeletion = mBinder.deleteWithChallenge(challenge);
+ return proofOfDeletion;
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+
+ @Override
+ public @NonNull byte[] update(@NonNull PersonalizationData personalizationData) {
+ try {
+ IWritableCredential binder = mBinder.update();
+ byte[] proofOfProvision =
+ CredstoreWritableIdentityCredential.personalize(binder, personalizationData);
+ return proofOfProvision;
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
}
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
index 1290633..d8d4742 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
@@ -162,5 +162,4 @@
+ e.errorCode, e);
}
}
-
}
diff --git a/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java b/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
index 725e3d8..d2e7984 100644
--- a/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
+++ b/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
@@ -76,7 +76,14 @@
@NonNull @Override
public byte[] personalize(@NonNull PersonalizationData personalizationData) {
+ return personalize(mBinder, personalizationData);
+ }
+ // Used by both personalize() and CredstoreIdentityCredential.update().
+ //
+ @NonNull
+ static byte[] personalize(IWritableCredential binder,
+ @NonNull PersonalizationData personalizationData) {
Collection<AccessControlProfile> accessControlProfiles =
personalizationData.getAccessControlProfiles();
@@ -144,7 +151,7 @@
secureUserId = getRootSid();
}
try {
- byte[] personalizationReceipt = mBinder.personalize(acpParcels, ensParcels,
+ byte[] personalizationReceipt = binder.personalize(acpParcels, ensParcels,
secureUserId);
return personalizationReceipt;
} catch (android.os.RemoteException e) {
@@ -164,5 +171,4 @@
return rootSid;
}
-
}
diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java
index 4eb6e42..8f175bb 100644
--- a/identity/java/android/security/identity/IdentityCredential.java
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -23,6 +23,7 @@
import java.security.KeyPair;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
+import java.time.Instant;
import java.util.Collection;
import java.util.Map;
@@ -114,6 +115,25 @@
public abstract void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys);
/**
+ * Sets whether to allow using an authentication key which has been expired if no
+ * other key is available. This must be called prior to calling
+ * {@link #getEntries(byte[], Map, byte[], byte[])}.
+ *
+ * <p>By default this is set to false.
+ *
+ * <p>This is only implemented in feature version 202101 or later. If not implemented, the call
+ * fails with {@link UnsupportedOperationException}. See
+ * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+ * feature versions.
+ *
+ * @param allowUsingExpiredKeys whether to allow using an authentication key which use count
+ * has been exceeded if no other key is available.
+ */
+ public void setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
* Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
* operation handle.
*
@@ -289,6 +309,21 @@
*
* <p>Each X.509 certificate is signed by CredentialKey. The certificate chain for CredentialKey
* can be obtained using the {@link #getCredentialKeyCertificateChain()} method.
+
+ * <p>If the implementation is feature version 202101 or later,
+ * each X.509 certificate contains an X.509 extension at OID 1.3.6.1.4.1.11129.2.1.26 which
+ * contains a DER encoded OCTET STRING with the bytes of the CBOR with the following CDDL:
+ * <pre>
+ * ProofOfBinding = [
+ * "ProofOfBinding",
+ * bstr, // Contains SHA-256(ProofOfProvisioning)
+ * ]
+ * </pre>
+ * <p>This CBOR enables an issuer to determine the exact state of the credential it
+ * returns issuer-signed data for.
+ *
+ * <p> See {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for
+ * known feature versions.
*
* @return A collection of X.509 certificates for dynamic authentication keys that need issuer
* certification.
@@ -308,16 +343,136 @@
* the authenticity
* and integrity of the credential data fields.
* @throws UnknownAuthenticationKeyException If the given authentication key is not recognized.
+ * @deprecated Use {@link #storeStaticAuthenticationData(X509Certificate, Instant, byte[])}
+ * instead.
*/
+ @Deprecated
public abstract void storeStaticAuthenticationData(
@NonNull X509Certificate authenticationKey,
@NonNull byte[] staticAuthData)
throws UnknownAuthenticationKeyException;
/**
+ * Store authentication data associated with a dynamic authentication key.
+ *
+ * This should only be called for an authenticated key returned by
+ * {@link #getAuthKeysNeedingCertification()}.
+ *
+ * <p>This is only implemented in feature version 202101 or later. If not implemented, the call
+ * fails with {@link UnsupportedOperationException}. See
+ * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+ * feature versions.
+ *
+ * @param authenticationKey The dynamic authentication key for which certification and
+ * associated static
+ * authentication data is being provided.
+ * @param expirationDate The expiration date of the static authentication data.
+ * @param staticAuthData Static authentication data provided by the issuer that validates
+ * the authenticity
+ * and integrity of the credential data fields.
+ * @throws UnknownAuthenticationKeyException If the given authentication key is not recognized.
+ */
+ public void storeStaticAuthenticationData(
+ @NonNull X509Certificate authenticationKey,
+ @NonNull Instant expirationDate,
+ @NonNull byte[] staticAuthData)
+ throws UnknownAuthenticationKeyException {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
* Get the number of times the dynamic authentication keys have been used.
*
* @return int array of dynamic authentication key usage counts.
*/
public @NonNull abstract int[] getAuthenticationDataUsageCount();
+
+ /**
+ * Proves ownership of a credential.
+ *
+ * <p>This method returns a COSE_Sign1 data structure signed by the CredentialKey
+ * with payload set to {@code ProofOfDeletion} as defined below.</p>
+ *
+ * <p>The returned CBOR is the following:</p>
+ * <pre>
+ * ProofOfOwnership = [
+ * "ProofOfOwnership", ; tstr
+ * tstr, ; DocType
+ * bstr, ; Challenge
+ * bool ; true if this is a test credential, should
+ * ; always be false.
+ * ]
+ * </pre>
+ *
+ * <p>This is only implemented in feature version 202101 or later. If not implemented, the call
+ * fails with {@link UnsupportedOperationException}. See
+ * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+ * feature versions.
+ *
+ * @param challenge is a non-empty byte array whose contents should be unique, fresh and
+ * provided by the issuing authority. The value provided is embedded in the
+ * generated CBOR and enables the issuing authority to verify that the
+ * returned proof is fresh.
+ * @return the COSE_Sign1 data structure above
+ */
+ public @NonNull byte[] proveOwnership(@NonNull byte[] challenge) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Deletes a credential.
+ *
+ * <p>This method returns a COSE_Sign1 data structure signed by the CredentialKey
+ * with payload set to {@code ProofOfDeletion} as defined below.</p>
+ *
+ * <pre>
+ * ProofOfDeletion = [
+ * "ProofOfDeletion", ; tstr
+ * tstr, ; DocType
+ * bstr, ; Challenge
+ * bool ; true if this is a test credential, should
+ * ; always be false.
+ * ]
+ * </pre>
+ *
+ * <p>This is only implemented in feature version 202101 or later. If not implemented, the call
+ * fails with {@link UnsupportedOperationException}. See
+ * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+ * feature versions.
+ *
+ * @param challenge is a non-empty byte array whose contents should be unique, fresh and
+ * provided by the issuing authority. The value provided is embedded in the
+ * generated CBOR and enables the issuing authority to verify that the
+ * returned proof is fresh.
+ * @return the COSE_Sign1 data structure above
+ */
+ public @NonNull byte[] delete(@NonNull byte[] challenge) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Updates the credential with new access control profiles and data items.
+ *
+ * <p>This method is similar to
+ * {@link WritableIdentityCredential#personalize(PersonalizationData)} except that it operates
+ * on an existing credential, see the documentation for that method for the format of the
+ * returned data.
+ *
+ * <p>If this call succeeds an side-effect is that all dynamic authentication keys for the
+ * credential are deleted. The application will need to use
+ * {@link #getAuthKeysNeedingCertification()} to generate replacement keys and return
+ * them for issuer certification.
+ *
+ * <p>This is only implemented in feature version 202101 or later. If not implemented, the call
+ * fails with {@link UnsupportedOperationException}. See
+ * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+ * feature versions.
+ *
+ * @param personalizationData The data to update, including access control profiles
+ * and data elements and their values, grouped into namespaces.
+ * @return A COSE_Sign1 data structure, see above.
+ */
+ public @NonNull byte[] update(@NonNull PersonalizationData personalizationData) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/identity/java/android/security/identity/IdentityCredentialStore.java b/identity/java/android/security/identity/IdentityCredentialStore.java
index 3843d92..6ccd0e8 100644
--- a/identity/java/android/security/identity/IdentityCredentialStore.java
+++ b/identity/java/android/security/identity/IdentityCredentialStore.java
@@ -72,6 +72,17 @@
* <p>Credentials provisioned to the direct access store should <strong>always</strong> use reader
* authentication to protect data elements. The reason for this is user authentication or user
* approval of data release is not possible when the device is off.
+ *
+ * <p>The Identity Credential API is designed to be able to evolve and change over time
+ * but still provide 100% backwards compatibility. This is complicated by the fact that
+ * there may be a version skew between the API used by the application and the version
+ * implemented in secure hardware. To solve this problem, the API provides for a way
+ * for the application to query which feature version the hardware implements (if any
+ * at all) using
+ * {@link android.content.pm#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} and
+ * {@link android.content.pm#FEATURE_IDENTITY_CREDENTIAL_HARDWARE_DIRECT_ACCESS}.
+ * Methods which only work on certain feature versions are clearly documented as
+ * such.
*/
public abstract class IdentityCredentialStore {
IdentityCredentialStore() {}
@@ -193,7 +204,9 @@
* @param credentialName the name of the credential to delete.
* @return {@code null} if the credential was not found, the COSE_Sign1 data structure above
* if the credential was found and deleted.
+ * @deprecated Use {@link IdentityCredential#delete(byte[])} instead.
*/
+ @Deprecated
public abstract @Nullable byte[] deleteCredentialByName(@NonNull String credentialName);
/** @hide */
@@ -201,5 +214,4 @@
@Retention(RetentionPolicy.SOURCE)
public @interface Ciphersuite {
}
-
}
diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java
index fcc518c..21d23b1 100644
--- a/keystore/java/android/security/Authorization.java
+++ b/keystore/java/android/security/Authorization.java
@@ -82,7 +82,7 @@
*
* @param locked - whether it is a lock (true) or unlock (false) event
* @param syntheticPassword - if it is an unlock event with the password, pass the synthetic
- * password provided by the LockSettingService
+ * password provided by the LockSettingService
*
* @return 0 if successful or a {@code ResponseCode}.
*/
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java
index 6ddaa70..b631999 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java
@@ -38,9 +38,10 @@
public AndroidKeyStoreECPublicKey(@NonNull KeyDescriptor descriptor,
@NonNull KeyMetadata metadata,
+ @NonNull byte[] x509EncodedForm,
@NonNull KeyStoreSecurityLevel securityLevel,
@NonNull ECParameterSpec params, @NonNull ECPoint w) {
- super(descriptor, metadata, KeyProperties.KEY_ALGORITHM_EC, securityLevel);
+ super(descriptor, metadata, x509EncodedForm, KeyProperties.KEY_ALGORITHM_EC, securityLevel);
mParams = params;
mW = w;
}
@@ -48,7 +49,7 @@
public AndroidKeyStoreECPublicKey(@NonNull KeyDescriptor descriptor,
@NonNull KeyMetadata metadata,
@NonNull KeyStoreSecurityLevel securityLevel, @NonNull ECPublicKey info) {
- this(descriptor, metadata, securityLevel, info.getParams(), info.getW());
+ this(descriptor, metadata, info.getEncoded(), securityLevel, info.getParams(), info.getW());
if (!"X.509".equalsIgnoreCase(info.getFormat())) {
throw new IllegalArgumentException(
"Unsupported key export format: " + info.getFormat());
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java
index 49dd77e..db3e567 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java
@@ -32,13 +32,15 @@
public abstract class AndroidKeyStorePublicKey extends AndroidKeyStoreKey implements PublicKey {
private final byte[] mCertificate;
private final byte[] mCertificateChain;
+ private final byte[] mEncoded;
public AndroidKeyStorePublicKey(@NonNull KeyDescriptor descriptor,
- @NonNull KeyMetadata metadata, @NonNull String algorithm,
- @NonNull KeyStoreSecurityLevel securityLevel) {
+ @NonNull KeyMetadata metadata, @NonNull byte[] x509EncodedForm,
+ @NonNull String algorithm, @NonNull KeyStoreSecurityLevel securityLevel) {
super(descriptor, metadata.key.nspace, metadata.authorizations, algorithm, securityLevel);
mCertificate = metadata.certificate;
mCertificateChain = metadata.certificateChain;
+ mEncoded = x509EncodedForm;
}
abstract AndroidKeyStorePrivateKey getPrivateKey();
@@ -50,7 +52,7 @@
@Override
public byte[] getEncoded() {
- return ArrayUtils.cloneIfNotEmpty(mCertificate);
+ return ArrayUtils.cloneIfNotEmpty(mEncoded);
}
@Override
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSAPublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSAPublicKey.java
index b578ea9..9fe6cf3 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSAPublicKey.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSAPublicKey.java
@@ -36,9 +36,11 @@
public AndroidKeyStoreRSAPublicKey(@NonNull KeyDescriptor descriptor,
@NonNull KeyMetadata metadata,
+ @NonNull byte[] x509EncodedForm,
@NonNull KeyStoreSecurityLevel securityLevel, @NonNull BigInteger modulus,
@NonNull BigInteger publicExponent) {
- super(descriptor, metadata, KeyProperties.KEY_ALGORITHM_RSA, securityLevel);
+ super(descriptor, metadata, x509EncodedForm, KeyProperties.KEY_ALGORITHM_RSA,
+ securityLevel);
mModulus = modulus;
mPublicExponent = publicExponent;
}
@@ -46,7 +48,8 @@
public AndroidKeyStoreRSAPublicKey(@NonNull KeyDescriptor descriptor,
@NonNull KeyMetadata metadata,
@NonNull KeyStoreSecurityLevel securityLevel, @NonNull RSAPublicKey info) {
- this(descriptor, metadata, securityLevel, info.getModulus(), info.getPublicExponent());
+ this(descriptor, metadata, info.getEncoded(), securityLevel, info.getModulus(),
+ info.getPublicExponent());
if (!"X.509".equalsIgnoreCase(info.getFormat())) {
throw new IllegalArgumentException(
"Unsupported key export format: " + info.getFormat());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
index 5cd660a..7ea4689 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
@@ -472,11 +472,6 @@
* animator is under test.
*/
internal fun startInternal() {
- if (!Looper.getMainLooper().isCurrentThread) {
- Log.e(TAG, "Animations can only be started on the main thread. If you are seeing " +
- "this message in a test, call PhysicsAnimatorTestUtils#prepareForTest in " +
- "your test setup.")
- }
val target = weakTarget.get()
if (target == null) {
Log.w(TAG, "Trying to animate a GC-ed object.")
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
index d37e628..1149cce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
@@ -17,10 +17,15 @@
package com.android.wm.shell.common;
import android.os.Looper;
+import android.os.SystemClock;
+import android.os.Trace;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
+import java.util.function.BooleanSupplier;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
/**
* Super basic Executor interface that adds support for delayed execution and removing callbacks.
@@ -65,17 +70,17 @@
/**
* See {@link android.os.Handler#postDelayed(Runnable, long)}.
*/
- void executeDelayed(Runnable r, long delayMillis);
+ void executeDelayed(Runnable runnable, long delayMillis);
/**
* See {@link android.os.Handler#removeCallbacks}.
*/
- void removeCallbacks(Runnable r);
+ void removeCallbacks(Runnable runnable);
/**
* See {@link android.os.Handler#hasCallbacks(Runnable)}.
*/
- boolean hasCallback(Runnable r);
+ boolean hasCallback(Runnable runnable);
/**
* Returns the looper that this executor is running on.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
index 1f07542..d14c3e3c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
@@ -35,54 +35,17 @@
@ExternalThread
public interface Pip {
/**
- * Closes PIP (PIPed activity and PIP system UI).
- */
- default void closePip() {
- }
-
- /**
- * Dump the current state and information if need.
- *
- * @param pw The stream to dump information to.
- */
- default void dump(PrintWriter pw) {
- }
-
- /**
* Expand PIP, it's possible that specific request to activate the window via Alt-tab.
*/
default void expandPip() {
}
/**
- * Get the touch handler which manages all the touch handling for PIP on the Phone,
- * including moving, dismissing and expanding the PIP. (Do not use in TV)
- *
- * @return
- */
- default @Nullable PipTouchHandler getPipTouchHandler() {
- return null;
- }
-
- /**
* Hides the PIP menu.
*/
default void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {}
/**
- * Returns {@code true} if PIP is shown.
- */
- default boolean isPipShown() {
- return false;
- }
-
- /**
- * Moves the PIPed activity to the fullscreen and closes PIP system UI.
- */
- default void movePipToFullscreen() {
- }
-
- /**
* Called when configuration is changed.
*/
default void onConfigurationChanged(Configuration newConfig) {
@@ -101,12 +64,6 @@
}
/**
- * Registers the session listener for the current user.
- */
- default void registerSessionListenerForCurrentUser() {
- }
-
- /**
* Called when SysUI state changed.
*
* @param isSysUiStateValid Is SysUI state valid or not.
@@ -116,19 +73,9 @@
}
/**
- * Resize the Pip to the appropriate size for the input state.
- *
- * @param state In Pip state also used to determine the new size for the Pip.
+ * Registers the session listener for the current user.
*/
- default void resizePinnedStack(int state) {
- }
-
- /**
- * Resumes resizing operation on the Pip that was previously suspended.
- *
- * @param reason The reason resizing operations on the Pip was suspended.
- */
- default void resumePipResizing(int reason) {
+ default void registerSessionListenerForCurrentUser() {
}
/**
@@ -162,14 +109,6 @@
default void showPictureInPictureMenu() {}
/**
- * Suspends resizing operation on the Pip until {@link #resumePipResizing} is called.
- *
- * @param reason The reason for suspending resizing operations on the Pip.
- */
- default void suspendPipResizing(int reason) {
- }
-
- /**
* Called by Launcher when swiping an auto-pip enabled Activity to home starts
* @param componentName {@link ComponentName} represents the Activity entering PiP
* @param activityInfo {@link ActivityInfo} tied to the Activity
@@ -199,4 +138,12 @@
* PiP and the Back-from-Edge gesture.
*/
default void setPipExclusionBoundsChangeListener(Consumer<Rect> listener) { }
+
+ /**
+ * Dump the current state and information if need.
+ *
+ * @param pw The stream to dump information to.
+ */
+ default void dump(PrintWriter pw) {
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
index d96d4d0..1a4616c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
@@ -31,6 +31,7 @@
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
+import android.os.Handler;
import android.os.UserHandle;
import androidx.annotation.Nullable;
@@ -74,6 +75,7 @@
}
private final Context mContext;
+ private final Handler mMainHandler;
private final MediaSessionManager mMediaSessionManager;
private MediaController mMediaController;
@@ -118,15 +120,16 @@
private final ArrayList<ActionListener> mActionListeners = new ArrayList<>();
private final ArrayList<MetadataListener> mMetadataListeners = new ArrayList<>();
- public PipMediaController(Context context) {
+ public PipMediaController(Context context, Handler mainHandler) {
mContext = context;
+ mMainHandler = mainHandler;
IntentFilter mediaControlFilter = new IntentFilter();
mediaControlFilter.addAction(ACTION_PLAY);
mediaControlFilter.addAction(ACTION_PAUSE);
mediaControlFilter.addAction(ACTION_NEXT);
mediaControlFilter.addAction(ACTION_PREV);
- mContext.registerReceiver(mPlayPauseActionReceiver, mediaControlFilter,
- UserHandle.USER_ALL);
+ mContext.registerReceiverForAllUsers(mPlayPauseActionReceiver, mediaControlFilter,
+ null /* permission */, mainHandler);
createMediaActions();
mMediaSessionManager = context.getSystemService(MediaSessionManager.class);
@@ -245,7 +248,7 @@
public void registerSessionListenerForCurrentUser() {
mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionsChangedListener);
mMediaSessionManager.addOnActiveSessionsChangedListener(mSessionsChangedListener, null,
- UserHandle.CURRENT, null);
+ UserHandle.CURRENT, mMainHandler);
}
/**
@@ -277,7 +280,7 @@
}
mMediaController = controller;
if (controller != null) {
- controller.registerCallback(mPlaybackChangedListener);
+ controller.registerCallback(mPlaybackChangedListener, mMainHandler);
}
notifyActionsChanged();
notifyMetadataChanged(getMediaMetadata());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 1279cd3..b80f285 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -50,9 +50,7 @@
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import android.util.Rational;
@@ -64,14 +62,14 @@
import android.window.WindowContainerTransactionCallback;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.os.SomeArgs;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.pip.phone.PipMotionHelper;
-import com.android.wm.shell.pip.phone.PipUpdateThread;
-
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
@@ -98,12 +96,6 @@
private static final String TAG = PipTaskOrganizer.class.getSimpleName();
private static final boolean DEBUG = false;
- private static final int MSG_RESIZE_IMMEDIATE = 1;
- private static final int MSG_RESIZE_ANIMATE = 2;
- private static final int MSG_OFFSET_ANIMATE = 3;
- private static final int MSG_FINISH_RESIZE = 4;
- private static final int MSG_RESIZE_USER = 5;
-
// Not a complete set of states but serves what we want right now.
private enum State {
UNDEFINED(0),
@@ -135,8 +127,6 @@
}
}
- private final Handler mMainHandler;
- private final Handler mUpdateHandler;
private final PipBoundsState mPipBoundsState;
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
private final @NonNull PipMenuController mPipMenuController;
@@ -148,6 +138,7 @@
private final Map<IBinder, Configuration> mInitialState = new HashMap<>();
private final Optional<LegacySplitScreen> mSplitScreenOptional;
protected final ShellTaskOrganizer mTaskOrganizer;
+ protected final ShellExecutor mMainExecutor;
// These callbacks are called on the update thread
private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
@@ -183,68 +174,6 @@
}
};
- @SuppressWarnings("unchecked")
- private final Handler.Callback mUpdateCallbacks = (msg) -> {
- SomeArgs args = (SomeArgs) msg.obj;
- Consumer<Rect> updateBoundsCallback = (Consumer<Rect>) args.arg1;
- switch (msg.what) {
- case MSG_RESIZE_IMMEDIATE: {
- Rect toBounds = (Rect) args.arg2;
- resizePip(toBounds);
- if (updateBoundsCallback != null) {
- updateBoundsCallback.accept(toBounds);
- }
- break;
- }
- case MSG_RESIZE_ANIMATE: {
- Rect currentBounds = (Rect) args.arg2;
- Rect toBounds = (Rect) args.arg3;
- Rect sourceHintRect = (Rect) args.arg4;
- float startingAngle = (float) args.arg5;
- int duration = args.argi2;
- animateResizePip(currentBounds, toBounds, sourceHintRect,
- args.argi1 /* direction */, duration, startingAngle);
- if (updateBoundsCallback != null) {
- updateBoundsCallback.accept(toBounds);
- }
- break;
- }
- case MSG_OFFSET_ANIMATE: {
- Rect originalBounds = (Rect) args.arg2;
- final int offset = args.argi1;
- final int duration = args.argi2;
- offsetPip(originalBounds, 0 /* xOffset */, offset, duration);
- Rect toBounds = new Rect(originalBounds);
- toBounds.offset(0, offset);
- if (updateBoundsCallback != null) {
- updateBoundsCallback.accept(toBounds);
- }
- break;
- }
- case MSG_FINISH_RESIZE: {
- SurfaceControl.Transaction tx = (SurfaceControl.Transaction) args.arg2;
- Rect toBounds = (Rect) args.arg3;
- finishResize(tx, toBounds, args.argi1 /* direction */, -1);
- if (updateBoundsCallback != null) {
- updateBoundsCallback.accept(toBounds);
- }
- break;
- }
- case MSG_RESIZE_USER: {
- Rect startBounds = (Rect) args.arg2;
- Rect toBounds = (Rect) args.arg3;
- float degrees = (float) args.arg4;
- userResizePip(startBounds, toBounds, degrees);
- if (updateBoundsCallback != null) {
- updateBoundsCallback.accept(toBounds);
- }
- break;
- }
- }
- args.recycle();
- return true;
- };
-
private ActivityManager.RunningTaskInfo mTaskInfo;
private WindowContainerToken mToken;
private SurfaceControl mLeash;
@@ -276,9 +205,8 @@
Optional<LegacySplitScreen> splitScreenOptional,
@NonNull DisplayController displayController,
@NonNull PipUiEventLogger pipUiEventLogger,
- @NonNull ShellTaskOrganizer shellTaskOrganizer) {
- mMainHandler = new Handler(Looper.getMainLooper());
- mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks);
+ @NonNull ShellTaskOrganizer shellTaskOrganizer,
+ @ShellMainThread ShellExecutor mainExecutor) {
mPipBoundsState = pipBoundsState;
mPipBoundsAlgorithm = boundsHandler;
mPipMenuController = pipMenuController;
@@ -290,12 +218,13 @@
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
mSplitScreenOptional = splitScreenOptional;
mTaskOrganizer = shellTaskOrganizer;
- mTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_PIP);
- displayController.addDisplayWindowListener(this);
- }
+ mMainExecutor = mainExecutor;
- public Handler getUpdateHandler() {
- return mUpdateHandler;
+ // TODO: Can be removed once wm components are created on the shell-main thread
+ mMainExecutor.execute(() -> {
+ mTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_PIP);
+ });
+ displayController.addDisplayWindowListener(this);
}
public Rect getCurrentOrAnimatingBounds() {
@@ -428,15 +357,17 @@
mTaskOrganizer.applySyncTransaction(wct, new WindowContainerTransactionCallback() {
@Override
public void onTransactionReady(int id, SurfaceControl.Transaction t) {
- t.apply();
- // Make sure to grab the latest source hint rect as it could have been updated
- // right after applying the windowing mode change.
- final Rect sourceHintRect = getValidSourceHintRect(mPictureInPictureParams,
- destinationBounds);
- scheduleAnimateResizePip(mPipBoundsState.getBounds(), destinationBounds,
- 0 /* startingAngle */, sourceHintRect, direction, animationDurationMs,
- null /* updateBoundsCallback */);
- mState = State.EXITING_PIP;
+ mMainExecutor.execute(() -> {
+ t.apply();
+ // Make sure to grab the latest source hint rect as it could have been
+ // updated right after applying the windowing mode change.
+ final Rect sourceHintRect = getValidSourceHintRect(mPictureInPictureParams,
+ destinationBounds);
+ scheduleAnimateResizePip(mPipBoundsState.getBounds(), destinationBounds,
+ 0 /* startingAngle */, sourceHintRect, direction,
+ animationDurationMs, null /* updateBoundsCallback */);
+ mState = State.EXITING_PIP;
+ });
}
});
}
@@ -465,12 +396,12 @@
}
// removePipImmediately is expected when the following animation finishes.
- mUpdateHandler.post(() -> mPipAnimationController
+ mPipAnimationController
.getAnimator(mLeash, mPipBoundsState.getBounds(), 1f, 0f)
.setTransitionDirection(TRANSITION_DIRECTION_REMOVE_STACK)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(mEnterExitAnimationDuration)
- .start());
+ .start();
mInitialState.remove(mToken.asBinder());
mState = State.EXITING_PIP;
}
@@ -579,12 +510,12 @@
tx.setAlpha(mLeash, 0f);
tx.apply();
applyEnterPipSyncTransaction(destinationBounds, () -> {
- mUpdateHandler.post(() -> mPipAnimationController
+ mPipAnimationController
.getAnimator(mLeash, destinationBounds, 0f, 1f)
.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(durationMs)
- .start());
+ .start();
// mState is set right after the animation is kicked off to block any resize
// requests such as offsetPip that may have been called prior to the transition.
mState = State.ENTERING_PIP;
@@ -599,13 +530,16 @@
wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
wct.setBounds(mToken, destinationBounds);
wct.scheduleFinishEnterPip(mToken, destinationBounds);
+ // TODO: Migrate to SyncTransactionQueue
mTaskOrganizer.applySyncTransaction(wct, new WindowContainerTransactionCallback() {
@Override
public void onTransactionReady(int id, SurfaceControl.Transaction t) {
- t.apply();
- if (runnable != null) {
- runnable.run();
- }
+ mMainExecutor.execute(() -> {
+ t.apply();
+ if (runnable != null) {
+ runnable.run();
+ }
+ });
}
});
}
@@ -621,12 +555,10 @@
mState = State.ENTERING_PIP;
}
final Rect pipBounds = mPipBoundsState.getBounds();
- runOnMainHandler(() -> {
- for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
- final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
- callback.onPipTransitionStarted(componentName, direction, pipBounds);
- }
- });
+ for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
+ final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
+ callback.onPipTransitionStarted(componentName, direction, pipBounds);
+ }
}
private void sendOnPipTransitionFinished(
@@ -634,29 +566,17 @@
if (direction == TRANSITION_DIRECTION_TO_PIP) {
mState = State.ENTERED_PIP;
}
- runOnMainHandler(() -> {
- for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
- final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
- callback.onPipTransitionFinished(mTaskInfo.baseActivity, direction);
- }
- });
+ for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
+ final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
+ callback.onPipTransitionFinished(mTaskInfo.baseActivity, direction);
+ }
}
private void sendOnPipTransitionCancelled(
@PipAnimationController.TransitionDirection int direction) {
- runOnMainHandler(() -> {
- for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
- final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
- callback.onPipTransitionCanceled(mTaskInfo.baseActivity, direction);
- }
- });
- }
-
- private void runOnMainHandler(Runnable r) {
- if (Looper.getMainLooper() == Looper.myLooper()) {
- r.run();
- } else {
- mMainHandler.post(r);
+ for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
+ final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
+ callback.onPipTransitionCanceled(mTaskInfo.baseActivity, direction);
}
}
@@ -872,15 +792,11 @@
return;
}
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = updateBoundsCallback;
- args.arg2 = currentBounds;
- args.arg3 = destinationBounds;
- args.arg4 = sourceHintRect;
- args.arg5 = startingAngle;
- args.argi1 = direction;
- args.argi2 = durationMs;
- mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_ANIMATE, args));
+ animateResizePip(currentBounds, destinationBounds, sourceHintRect, direction, durationMs,
+ startingAngle);
+ if (updateBoundsCallback != null) {
+ updateBoundsCallback.accept(destinationBounds);
+ }
}
/**
@@ -888,10 +804,24 @@
* {@link WindowContainerTransaction} until {@link #scheduleFinishResizePip} is called.
*/
public void scheduleResizePip(Rect toBounds, Consumer<Rect> updateBoundsCallback) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = updateBoundsCallback;
- args.arg2 = toBounds;
- mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_IMMEDIATE, args));
+ // Could happen when exitPip
+ if (mToken == null || mLeash == null) {
+ Log.w(TAG, "Abort animation, invalid leash");
+ return;
+ }
+ mPipBoundsState.setBounds(toBounds);
+ final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
+ mSurfaceTransactionHelper
+ .crop(tx, mLeash, toBounds)
+ .round(tx, mLeash, mState.isInPip());
+ if (mPipMenuController.isMenuVisible()) {
+ mPipMenuController.resizePipMenu(mLeash, tx, toBounds);
+ } else {
+ tx.apply();
+ }
+ if (updateBoundsCallback != null) {
+ updateBoundsCallback.accept(toBounds);
+ }
}
/**
@@ -909,12 +839,27 @@
*/
public void scheduleUserResizePip(Rect startBounds, Rect toBounds, float degrees,
Consumer<Rect> updateBoundsCallback) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = updateBoundsCallback;
- args.arg2 = startBounds;
- args.arg3 = toBounds;
- args.arg4 = degrees;
- mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_USER, args));
+ // Could happen when exitPip
+ if (mToken == null || mLeash == null) {
+ Log.w(TAG, "Abort animation, invalid leash");
+ return;
+ }
+
+ if (startBounds.isEmpty() || toBounds.isEmpty()) {
+ Log.w(TAG, "Attempted to user resize PIP to or from empty bounds, aborting.");
+ return;
+ }
+
+ final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
+ mSurfaceTransactionHelper.scale(tx, mLeash, startBounds, toBounds, degrees);
+ if (mPipMenuController.isMenuVisible()) {
+ mPipMenuController.movePipMenu(mLeash, tx, toBounds);
+ } else {
+ tx.apply();
+ }
+ if (updateBoundsCallback != null) {
+ updateBoundsCallback.accept(toBounds);
+ }
}
/**
@@ -948,13 +893,11 @@
return;
}
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = updateBoundsCallback;
- args.arg2 = createFinishResizeSurfaceTransaction(
- destinationBounds);
- args.arg3 = destinationBounds;
- args.argi1 = direction;
- mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_FINISH_RESIZE, args));
+ finishResize(createFinishResizeSurfaceTransaction(destinationBounds), destinationBounds,
+ direction, -1);
+ if (updateBoundsCallback != null) {
+ updateBoundsCallback.accept(destinationBounds);
+ }
}
private SurfaceControl.Transaction createFinishResizeSurfaceTransaction(
@@ -979,20 +922,15 @@
Log.d(TAG, "skip scheduleOffsetPip, entering pip deferred");
return;
}
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = updateBoundsCallback;
- args.arg2 = originalBounds;
- // offset would be zero if triggered from screen rotation.
- args.argi1 = offset;
- args.argi2 = duration;
- mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_ANIMATE, args));
+ offsetPip(originalBounds, 0 /* xOffset */, offset, duration);
+ Rect toBounds = new Rect(originalBounds);
+ toBounds.offset(0, offset);
+ if (updateBoundsCallback != null) {
+ updateBoundsCallback.accept(toBounds);
+ }
}
private void offsetPip(Rect originalBounds, int xOffset, int yOffset, int durationMs) {
- if (Looper.myLooper() != mUpdateHandler.getLooper()) {
- throw new RuntimeException("Callers should call scheduleOffsetPip() instead of this "
- + "directly");
- }
if (mTaskInfo == null) {
Log.w(TAG, "mTaskInfo is not set");
return;
@@ -1003,62 +941,9 @@
TRANSITION_DIRECTION_SAME, durationMs, 0);
}
- private void resizePip(Rect destinationBounds) {
- if (Looper.myLooper() != mUpdateHandler.getLooper()) {
- throw new RuntimeException("Callers should call scheduleResizePip() instead of this "
- + "directly");
- }
- // Could happen when exitPip
- if (mToken == null || mLeash == null) {
- Log.w(TAG, "Abort animation, invalid leash");
- return;
- }
- mPipBoundsState.setBounds(destinationBounds);
- final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
- mSurfaceTransactionHelper
- .crop(tx, mLeash, destinationBounds)
- .round(tx, mLeash, mState.isInPip());
- if (mPipMenuController.isMenuVisible()) {
- runOnMainHandler(() ->
- mPipMenuController.resizePipMenu(mLeash, tx, destinationBounds));
- } else {
- tx.apply();
- }
- }
-
- private void userResizePip(Rect startBounds, Rect destinationBounds, float degrees) {
- if (Looper.myLooper() != mUpdateHandler.getLooper()) {
- throw new RuntimeException("Callers should call scheduleUserResizePip() instead of "
- + "this directly");
- }
- // Could happen when exitPip
- if (mToken == null || mLeash == null) {
- Log.w(TAG, "Abort animation, invalid leash");
- return;
- }
-
- if (startBounds.isEmpty() || destinationBounds.isEmpty()) {
- Log.w(TAG, "Attempted to user resize PIP to or from empty bounds, aborting.");
- return;
- }
-
- final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
- mSurfaceTransactionHelper.scale(tx, mLeash, startBounds, destinationBounds, degrees);
- if (mPipMenuController.isMenuVisible()) {
- runOnMainHandler(() ->
- mPipMenuController.movePipMenu(mLeash, tx, destinationBounds));
- } else {
- tx.apply();
- }
- }
-
private void finishResize(SurfaceControl.Transaction tx, Rect destinationBounds,
@PipAnimationController.TransitionDirection int direction,
@PipAnimationController.AnimationType int type) {
- if (Looper.myLooper() != mUpdateHandler.getLooper()) {
- throw new RuntimeException("Callers should call scheduleResizePip() instead of this "
- + "directly");
- }
mPipBoundsState.setBounds(destinationBounds);
if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
removePipImmediately();
@@ -1097,7 +982,7 @@
mSurfaceTransactionHelper.scale(t, snapshotSurface, snapshotSrc, snapshotDest);
t.apply();
- mUpdateHandler.post(() -> {
+ mMainExecutor.execute(() -> {
// Start animation to fade out the snapshot.
final ValueAnimator animator = ValueAnimator.ofFloat(1.0f, 0.0f);
animator.setDuration(mEnterExitAnimationDuration);
@@ -1129,10 +1014,8 @@
}
private void finishResizeForMenu(Rect destinationBounds) {
- runOnMainHandler(() -> {
- mPipMenuController.movePipMenu(null, null, destinationBounds);
- mPipMenuController.updateMenuBounds(destinationBounds);
- });
+ mPipMenuController.movePipMenu(null, null, destinationBounds);
+ mPipMenuController.updateMenuBounds(destinationBounds);
}
private void prepareFinishResizeTransaction(Rect destinationBounds,
@@ -1185,10 +1068,6 @@
private void animateResizePip(Rect currentBounds, Rect destinationBounds, Rect sourceHintRect,
@PipAnimationController.TransitionDirection int direction, int durationMs,
float startingAngle) {
- if (Looper.myLooper() != mUpdateHandler.getLooper()) {
- throw new RuntimeException("Callers should call scheduleAnimateResizePip() instead of "
- + "this directly");
- }
// Could happen when exitPip
if (mToken == null || mLeash == null) {
Log.w(TAG, "Abort animation, invalid leash");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 5db8f3d..8bf1b46 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -30,6 +30,7 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Debug;
+import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
@@ -39,6 +40,7 @@
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowManagerGlobal;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipMediaController.ActionListener;
@@ -97,6 +99,8 @@
private final RectF mTmpDestinationRectF = new RectF();
private final Context mContext;
private final PipMediaController mMediaController;
+ private final ShellExecutor mMainExecutor;
+ private final Handler mMainHandler;
private final ArrayList<Listener> mListeners = new ArrayList<>();
private final SystemWindows mSystemWindows;
@@ -116,11 +120,14 @@
}
};
- public PhonePipMenuController(Context context,
- PipMediaController mediaController, SystemWindows systemWindows) {
+ public PhonePipMenuController(Context context, PipMediaController mediaController,
+ SystemWindows systemWindows, ShellExecutor mainExecutor,
+ Handler mainHandler) {
mContext = context;
mMediaController = mediaController;
mSystemWindows = systemWindows;
+ mMainExecutor = mainExecutor;
+ mMainHandler = mainHandler;
}
public boolean isMenuVisible() {
@@ -156,7 +163,7 @@
if (mPipMenuView != null) {
detachPipMenuView();
}
- mPipMenuView = new PipMenuView(mContext, this);
+ mPipMenuView = new PipMenuView(mContext, this, mMainExecutor, mMainHandler);
mSystemWindows.addView(mPipMenuView,
getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
0, SHELL_ROOT_LAYER_PIP);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAppOpsListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAppOpsListener.java
index 2cd0107..d97d2d6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAppOpsListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAppOpsListener.java
@@ -21,22 +21,20 @@
import android.app.AppOpsManager;
import android.app.AppOpsManager.OnOpChangedListener;
-import android.app.IActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Handler;
import android.util.Pair;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.PipUtils;
public class PipAppOpsListener {
private static final String TAG = PipAppOpsListener.class.getSimpleName();
private Context mContext;
- private Handler mHandler;
- private IActivityManager mActivityManager;
+ private ShellExecutor mMainExecutor;
private AppOpsManager mAppOpsManager;
private Callback mCallback;
@@ -53,7 +51,7 @@
if (appInfo.packageName.equals(topPipActivityInfo.first.getPackageName()) &&
mAppOpsManager.checkOpNoThrow(OP_PICTURE_IN_PICTURE, appInfo.uid,
packageName) != MODE_ALLOWED) {
- mHandler.post(() -> mCallback.dismissPip());
+ mMainExecutor.execute(() -> mCallback.dismissPip());
}
}
} catch (NameNotFoundException e) {
@@ -63,11 +61,9 @@
}
};
- public PipAppOpsListener(Context context, IActivityManager activityManager,
- Callback callback) {
+ public PipAppOpsListener(Context context, Callback callback, ShellExecutor mainExecutor) {
mContext = context;
- mHandler = new Handler(mContext.getMainLooper());
- mActivityManager = activityManager;
+ mMainExecutor = mainExecutor;
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mCallback = callback;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index fa88084..cefeb939 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -71,11 +71,11 @@
/**
* Manages the picture-in-picture (PIP) UI and states for Phones.
*/
-public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallback {
+public class PipController implements PipTaskOrganizer.PipTransitionCallback {
private static final String TAG = "PipController";
private Context mContext;
- private ShellExecutor mMainExecutor;
+ protected ShellExecutor mMainExecutor;
private DisplayController mDisplayController;
private PipInputConsumer mPipInputConsumer;
private WindowManagerShellWrapper mWindowManagerShellWrapper;
@@ -84,6 +84,7 @@
private PipBoundsAlgorithm mPipBoundsAlgorithm;
private PipBoundsState mPipBoundsState;
private PipTouchHandler mTouchHandler;
+ protected final PipImpl mImpl = new PipImpl();
private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
private final Rect mTmpInsetBounds = new Rect();
@@ -204,6 +205,28 @@
}
}
+
+ /**
+ * Instantiates {@link PipController}, returns {@code null} if the feature not supported.
+ */
+ @Nullable
+ public static Pip create(Context context, DisplayController displayController,
+ PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipBoundsState pipBoundsState, PipMediaController pipMediaController,
+ PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
+ PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper,
+ TaskStackListenerImpl taskStackListener, ShellExecutor mainExecutor) {
+ if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
+ Slog.w(TAG, "Device doesn't support Pip feature");
+ return null;
+ }
+
+ return new PipController(context, displayController, pipAppOpsListener, pipBoundsAlgorithm,
+ pipBoundsState, pipMediaController, phonePipMenuController, pipTaskOrganizer,
+ pipTouchHandler, windowManagerShellWrapper, taskStackListener, mainExecutor)
+ .mImpl;
+ }
+
protected PipController(Context context,
DisplayController displayController,
PipAppOpsListener pipAppOpsListener,
@@ -235,7 +258,7 @@
mTouchHandler = pipTouchHandler;
mAppOpsListener = pipAppOpsListener;
mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(),
- INPUT_CONSUMER_PIP);
+ INPUT_CONSUMER_PIP, mainExecutor);
mPipTaskOrganizer.registerPipTransitionCallback(this);
mPipTaskOrganizer.registerOnDisplayIdChangeCallback((int displayId) -> {
final DisplayInfo newDisplayInfo = new DisplayInfo();
@@ -288,7 +311,7 @@
if (taskInfo != null) {
// If SystemUI restart, and it already existed a pinned stack,
// register the pip input consumer to ensure touch can send to it.
- mPipInputConsumer.registerInputConsumer(true /* withSfVsync */);
+ mPipInputConsumer.registerInputConsumer();
}
} catch (RemoteException | UnsupportedOperationException e) {
Log.e(TAG, "Failed to register pinned stack listener", e);
@@ -301,12 +324,10 @@
@Override
public void onActivityPinned(String packageName, int userId, int taskId,
int stackId) {
- mMainExecutor.execute(() -> {
- mTouchHandler.onActivityPinned();
- mMediaController.onActivityPinned();
- mAppOpsListener.onActivityPinned(packageName);
- });
- mPipInputConsumer.registerInputConsumer(true /* withSfVsync */);
+ mTouchHandler.onActivityPinned();
+ mMediaController.onActivityPinned();
+ mAppOpsListener.onActivityPinned(packageName);
+ mPipInputConsumer.registerInputConsumer();
}
@Override
@@ -314,10 +335,8 @@
final Pair<ComponentName, Integer> topPipActivityInfo =
PipUtils.getTopPipActivity(mContext);
final ComponentName topActivity = topPipActivityInfo.first;
- mMainExecutor.execute(() -> {
- mTouchHandler.onActivityUnpinned(topActivity);
- mAppOpsListener.onActivityUnpinned();
- });
+ mTouchHandler.onActivityUnpinned(topActivity);
+ mAppOpsListener.onActivityUnpinned();
mPipInputConsumer.unregisterInputConsumer();
}
@@ -333,60 +352,46 @@
});
}
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- mMainExecutor.execute(() -> {
- mPipBoundsAlgorithm.onConfigurationChanged(mContext);
- mTouchHandler.onConfigurationChanged();
- mPipBoundsState.onConfigurationChanged();
- });
+ private void onConfigurationChanged(Configuration newConfig) {
+ mPipBoundsAlgorithm.onConfigurationChanged(mContext);
+ mTouchHandler.onConfigurationChanged();
+ mPipBoundsState.onConfigurationChanged();
}
- @Override
- public void onDensityOrFontScaleChanged() {
- mMainExecutor.execute(() -> {
- mPipTaskOrganizer.onDensityOrFontScaleChanged(mContext);
- });
+ private void onDensityOrFontScaleChanged() {
+ mPipTaskOrganizer.onDensityOrFontScaleChanged(mContext);
}
- @Override
- public void onOverlayChanged() {
- mMainExecutor.execute(() -> {
- mPipBoundsState.setDisplayLayout(new DisplayLayout(mContext, mContext.getDisplay()));
- updateMovementBounds(null /* toBounds */,
- false /* fromRotation */, false /* fromImeAdjustment */,
- false /* fromShelfAdjustment */,
- null /* windowContainerTransaction */);
- });
+ private void onOverlayChanged() {
+ mPipBoundsState.setDisplayLayout(new DisplayLayout(mContext, mContext.getDisplay()));
+ updateMovementBounds(null /* toBounds */,
+ false /* fromRotation */, false /* fromImeAdjustment */,
+ false /* fromShelfAdjustment */,
+ null /* windowContainerTransaction */);
}
- @Override
- public void registerSessionListenerForCurrentUser() {
+ private void registerSessionListenerForCurrentUser() {
mMediaController.registerSessionListenerForCurrentUser();
}
- @Override
- public void onSystemUiStateChanged(boolean isValidState, int flag) {
+ private void onSystemUiStateChanged(boolean isValidState, int flag) {
mTouchHandler.onSystemUiStateChanged(isValidState);
}
/**
* Expands the PIP.
*/
- @Override
public void expandPip() {
mTouchHandler.getMotionHelper().expandLeavePip(false /* skipAnimation */);
}
- @Override
- public PipTouchHandler getPipTouchHandler() {
+ private PipTouchHandler getPipTouchHandler() {
return mTouchHandler;
}
/**
* Hides the PIP menu.
*/
- @Override
public void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {
mMenuController.hideMenu(onStartCallback, onEndCallback);
}
@@ -408,9 +413,8 @@
/**
* Sets both shelf visibility and its height.
*/
- @Override
- public void setShelfHeight(boolean visible, int height) {
- mMainExecutor.execute(() -> setShelfHeightLocked(visible, height));
+ private void setShelfHeight(boolean visible, int height) {
+ setShelfHeightLocked(visible, height);
}
private void setShelfHeightLocked(boolean visible, int height) {
@@ -418,18 +422,15 @@
mPipBoundsState.setShelfVisibility(visible, shelfHeight);
}
- @Override
- public void setPinnedStackAnimationType(int animationType) {
- mMainExecutor.execute(() -> mPipTaskOrganizer.setOneShotAnimationType(animationType));
+ private void setPinnedStackAnimationType(int animationType) {
+ mPipTaskOrganizer.setOneShotAnimationType(animationType);
}
- @Override
- public void setPinnedStackAnimationListener(Consumer<Boolean> callback) {
- mMainExecutor.execute(() -> mPinnedStackAnimationRecentsCallback = callback);
+ private void setPinnedStackAnimationListener(Consumer<Boolean> callback) {
+ mPinnedStackAnimationRecentsCallback = callback;
}
- @Override
- public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
+ private Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
PictureInPictureParams pictureInPictureParams,
int launcherRotation, int shelfHeight) {
setShelfHeightLocked(shelfHeight > 0 /* visible */, shelfHeight);
@@ -438,11 +439,19 @@
pictureInPictureParams);
}
- @Override
- public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
+ private void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
mPipTaskOrganizer.stopSwipePipToHome(componentName, destinationBounds);
}
+ /**
+ * Set a listener to watch out for PiP bounds. This is mostly used by SystemUI's
+ * Back-gesture handler, to avoid conflicting with PiP when it's stashed.
+ */
+ private void setPipExclusionBoundsChangeListener(
+ Consumer<Rect> pipExclusionBoundsChangeListener) {
+ mTouchHandler.setPipExclusionBoundsChangeListener(pipExclusionBoundsChangeListener);
+ }
+
@Override
public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) {
if (isOutPipDirection(direction)) {
@@ -468,16 +477,6 @@
}
}
- /**
- * Set a listener to watch out for PiP bounds. This is mostly used by SystemUI's
- * Back-gesture handler, to avoid conflicting with PiP when it's stashed.
- */
- @Override
- public void setPipExclusionBoundsChangeListener(
- Consumer<Rect> pipExclusionBoundsChangeListener) {
- mTouchHandler.setPipExclusionBoundsChangeListener(pipExclusionBoundsChangeListener);
- }
-
@Override
public void onPipTransitionFinished(ComponentName activity, int direction) {
onPipTransitionFinishedOrCanceled(direction);
@@ -607,8 +606,7 @@
}
}
- @Override
- public void dump(PrintWriter pw) {
+ private void dump(PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG);
mMenuController.dump(pw, innerPrefix);
@@ -619,23 +617,123 @@
mPipInputConsumer.dump(pw, innerPrefix);
}
- /**
- * Instantiates {@link PipController}, returns {@code null} if the feature not supported.
- */
- @Nullable
- public static PipController create(Context context, DisplayController displayController,
- PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
- PipBoundsState pipBoundsState, PipMediaController pipMediaController,
- PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
- PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper,
- TaskStackListenerImpl taskStackListener, ShellExecutor mainExecutor) {
- if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
- Slog.w(TAG, "Device doesn't support Pip feature");
- return null;
+ private class PipImpl implements Pip {
+ @Override
+ public void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {
+ mMainExecutor.execute(() -> {
+ PipController.this.hidePipMenu(onStartCallback, onEndCallback);
+ });
}
- return new PipController(context, displayController, pipAppOpsListener, pipBoundsAlgorithm,
- pipBoundsState, pipMediaController, phonePipMenuController, pipTaskOrganizer,
- pipTouchHandler, windowManagerShellWrapper, taskStackListener, mainExecutor);
+ @Override
+ public void expandPip() {
+ mMainExecutor.execute(() -> {
+ PipController.this.expandPip();
+ });
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ mMainExecutor.execute(() -> {
+ PipController.this.onConfigurationChanged(newConfig);
+ });
+ }
+
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ mMainExecutor.execute(() -> {
+ PipController.this.onDensityOrFontScaleChanged();
+ });
+ }
+
+ @Override
+ public void onOverlayChanged() {
+ mMainExecutor.execute(() -> {
+ PipController.this.onOverlayChanged();
+ });
+ }
+
+ @Override
+ public void onSystemUiStateChanged(boolean isSysUiStateValid, int flag) {
+ mMainExecutor.execute(() -> {
+ PipController.this.onSystemUiStateChanged(isSysUiStateValid, flag);
+ });
+ }
+
+ @Override
+ public void registerSessionListenerForCurrentUser() {
+ mMainExecutor.execute(() -> {
+ PipController.this.registerSessionListenerForCurrentUser();
+ });
+ }
+
+ @Override
+ public void setShelfHeight(boolean visible, int height) {
+ mMainExecutor.execute(() -> {
+ PipController.this.setShelfHeight(visible, height);
+ });
+ }
+
+ @Override
+ public void setPinnedStackAnimationListener(Consumer<Boolean> callback) {
+ mMainExecutor.execute(() -> {
+ PipController.this.setPinnedStackAnimationListener(callback);
+ });
+ }
+
+ @Override
+ public void setPinnedStackAnimationType(int animationType) {
+ mMainExecutor.execute(() -> {
+ PipController.this.setPinnedStackAnimationType(animationType);
+ });
+ }
+
+ @Override
+ public void setPipExclusionBoundsChangeListener(Consumer<Rect> listener) {
+ mMainExecutor.execute(() -> {
+ PipController.this.setPipExclusionBoundsChangeListener(listener);
+ });
+ }
+
+ @Override
+ public void showPictureInPictureMenu() {
+ mMainExecutor.execute(() -> {
+ PipController.this.showPictureInPictureMenu();
+ });
+ }
+
+ @Override
+ public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
+ PictureInPictureParams pictureInPictureParams, int launcherRotation,
+ int shelfHeight) {
+ Rect[] result = new Rect[1];
+ try {
+ mMainExecutor.executeBlocking(() -> {
+ result[0] = PipController.this.startSwipePipToHome(componentName, activityInfo,
+ pictureInPictureParams, launcherRotation, shelfHeight);
+ });
+ } catch (InterruptedException e) {
+ Slog.e(TAG, "Failed to start swipe pip to home");
+ }
+ return result[0];
+ }
+
+ @Override
+ public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
+ mMainExecutor.execute(() -> {
+ PipController.this.stopSwipePipToHome(componentName, destinationBounds);
+ });
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ try {
+ mMainExecutor.executeBlocking(() -> {
+ PipController.this.dump(pw);
+ });
+ } catch (InterruptedException e) {
+ Slog.e(TAG, "Failed to dump PipController in 2s");
+ }
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
index bebe5f9..d9a7bdb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -37,9 +37,12 @@
import com.android.wm.shell.R;
import com.android.wm.shell.animation.PhysicsAnimator;
import com.android.wm.shell.common.DismissCircleView;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
import com.android.wm.shell.pip.PipUiEventLogger;
+import java.util.concurrent.TimeUnit;
+
import kotlin.Unit;
/**
@@ -57,36 +60,32 @@
* MagnetizedObject wrapper for PIP. This allows the magnetic target library to locate and move
* PIP.
*/
- private final MagnetizedObject<Rect> mMagnetizedPip;
+ private MagnetizedObject<Rect> mMagnetizedPip;
/**
* Container for the dismiss circle, so that it can be animated within the container via
* translation rather than within the WindowManager via slow layout animations.
*/
- private final ViewGroup mTargetViewContainer;
+ private ViewGroup mTargetViewContainer;
/** Circle view used to render the dismiss target. */
- private final DismissCircleView mTargetView;
+ private DismissCircleView mTargetView;
/**
* MagneticTarget instance wrapping the target view and allowing us to set its magnetic radius.
*/
- private final MagnetizedObject.MagneticTarget mMagneticTarget;
+ private MagnetizedObject.MagneticTarget mMagneticTarget;
- /** PhysicsAnimator instance for animating the dismiss target in/out. */
- private final PhysicsAnimator<View> mMagneticTargetAnimator;
+ /**
+ * PhysicsAnimator instance for animating the dismiss target in/out.
+ */
+ private PhysicsAnimator<View> mMagneticTargetAnimator;
/** Default configuration to use for springing the dismiss target in/out. */
private final PhysicsAnimator.SpringConfig mTargetSpringConfig =
new PhysicsAnimator.SpringConfig(
SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
- /**
- * Runnable that can be posted delayed to show the target. This needs to be saved as a member
- * variable so we can pass it to removeCallbacks.
- */
- private Runnable mShowTargetAction = this::showDismissTargetMaybe;
-
// Allow dragging the PIP to a location to close it
private final boolean mEnableDismissDragToEdge;
@@ -96,74 +95,76 @@
private final PipMotionHelper mMotionHelper;
private final PipUiEventLogger mPipUiEventLogger;
private final WindowManager mWindowManager;
- private final Handler mHandler;
+ private final ShellExecutor mMainExecutor;
public PipDismissTargetHandler(Context context, PipUiEventLogger pipUiEventLogger,
- PipMotionHelper motionHelper, Handler handler) {
+ PipMotionHelper motionHelper, ShellExecutor mainExecutor) {
mContext = context;
mPipUiEventLogger = pipUiEventLogger;
mMotionHelper = motionHelper;
- mHandler = handler;
+ mMainExecutor = mainExecutor;
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Resources res = context.getResources();
mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height);
- mTargetView = new DismissCircleView(context);
- mTargetViewContainer = new FrameLayout(context);
- mTargetViewContainer.setBackgroundDrawable(
- context.getDrawable(R.drawable.floating_dismiss_gradient_transition));
- mTargetViewContainer.setClipChildren(false);
- mTargetViewContainer.addView(mTargetView);
+ mMainExecutor.execute(() -> {
+ mTargetView = new DismissCircleView(context);
+ mTargetViewContainer = new FrameLayout(context);
+ mTargetViewContainer.setBackgroundDrawable(
+ context.getDrawable(R.drawable.floating_dismiss_gradient_transition));
+ mTargetViewContainer.setClipChildren(false);
+ mTargetViewContainer.addView(mTargetView);
- mMagnetizedPip = mMotionHelper.getMagnetizedPip();
- mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0);
- updateMagneticTargetSize();
+ mMagnetizedPip = mMotionHelper.getMagnetizedPip();
+ mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0);
+ updateMagneticTargetSize();
- mMagnetizedPip.setAnimateStuckToTarget(
- (target, velX, velY, flung, after) -> {
+ mMagnetizedPip.setAnimateStuckToTarget(
+ (target, velX, velY, flung, after) -> {
+ if (mEnableDismissDragToEdge) {
+ mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung,
+ after);
+ }
+ return Unit.INSTANCE;
+ });
+ mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() {
+ @Override
+ public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
+ // Show the dismiss target, in case the initial touch event occurred within
+ // the magnetic field radius.
if (mEnableDismissDragToEdge) {
- mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung, after);
+ showDismissTargetMaybe();
}
- return Unit.INSTANCE;
- });
- mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() {
- @Override
- public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
- // Show the dismiss target, in case the initial touch event occurred within the
- // magnetic field radius.
- if (mEnableDismissDragToEdge) {
- showDismissTargetMaybe();
}
- }
- @Override
- public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
- float velX, float velY, boolean wasFlungOut) {
- if (wasFlungOut) {
- mMotionHelper.flingToSnapTarget(velX, velY, null /* endAction */);
- hideDismissTargetMaybe();
- } else {
- mMotionHelper.setSpringingToTouch(true);
+ @Override
+ public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
+ float velX, float velY, boolean wasFlungOut) {
+ if (wasFlungOut) {
+ mMotionHelper.flingToSnapTarget(velX, velY, null /* endAction */);
+ hideDismissTargetMaybe();
+ } else {
+ mMotionHelper.setSpringingToTouch(true);
+ }
}
- }
- @Override
- public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
- mMotionHelper.notifyDismissalPending();
+ @Override
+ public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
+ mMainExecutor.executeDelayed(() -> {
+ mMotionHelper.notifyDismissalPending();
+ mMotionHelper.animateDismiss();
+ hideDismissTargetMaybe();
- handler.post(() -> {
- mMotionHelper.animateDismiss();
- hideDismissTargetMaybe();
- });
+ mPipUiEventLogger.log(
+ PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_DRAG_TO_REMOVE);
+ }, 0);
+ }
+ });
- mPipUiEventLogger.log(
- PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_DRAG_TO_REMOVE);
- }
+ mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView);
});
-
- mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView);
}
/**
@@ -200,7 +201,6 @@
/** Adds the magnetic target view to the WindowManager so it's ready to be animated in. */
public void createOrUpdateDismissTarget() {
if (!mTargetViewContainer.isAttachedToWindow()) {
- mHandler.removeCallbacks(mShowTargetAction);
mMagneticTargetAnimator.cancel();
mTargetViewContainer.setVisibility(View.INVISIBLE);
@@ -270,7 +270,6 @@
return;
}
- mHandler.removeCallbacks(mShowTargetAction);
mMagneticTargetAnimator
.spring(DynamicAnimation.TRANSLATION_Y,
mTargetViewContainer.getHeight(),
@@ -286,8 +285,6 @@
* Removes the dismiss target and cancels any pending callbacks to show it.
*/
public void cleanUpDismissTarget() {
- mHandler.removeCallbacks(mShowTargetAction);
-
if (mTargetViewContainer.isAttachedToWindow()) {
mWindowManager.removeViewImmediate(mTargetViewContainer);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
index 0c64c8c..7a634c3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
@@ -29,6 +29,8 @@
import android.view.InputChannel;
import android.view.InputEvent;
+import com.android.wm.shell.common.ShellExecutor;
+
import java.io.PrintWriter;
/**
@@ -81,6 +83,7 @@
private final IWindowManager mWindowManager;
private final IBinder mToken;
private final String mName;
+ private final ShellExecutor mMainExecutor;
private InputEventReceiver mInputEventReceiver;
private InputListener mListener;
@@ -89,10 +92,12 @@
/**
* @param name the name corresponding to the input consumer that is defined in the system.
*/
- public PipInputConsumer(IWindowManager windowManager, String name) {
+ public PipInputConsumer(IWindowManager windowManager, String name,
+ ShellExecutor mainExecutor) {
mWindowManager = windowManager;
mToken = new Binder();
mName = name;
+ mMainExecutor = mainExecutor;
}
/**
@@ -107,9 +112,11 @@
*/
public void setRegistrationListener(RegistrationListener listener) {
mRegistrationListener = listener;
- if (mRegistrationListener != null) {
- mRegistrationListener.onRegistrationChanged(mInputEventReceiver != null);
- }
+ mMainExecutor.execute(() -> {
+ if (mRegistrationListener != null) {
+ mRegistrationListener.onRegistrationChanged(mInputEventReceiver != null);
+ }
+ });
}
/**
@@ -125,14 +132,6 @@
* Registers the input consumer.
*/
public void registerInputConsumer() {
- registerInputConsumer(false);
- }
-
- /**
- * Registers the input consumer.
- * @param withSfVsync the flag set using sf vsync signal or no
- */
- public void registerInputConsumer(boolean withSfVsync) {
if (mInputEventReceiver != null) {
return;
}
@@ -144,11 +143,15 @@
} catch (RemoteException e) {
Log.e(TAG, "Failed to create input consumer", e);
}
- mInputEventReceiver = new InputEventReceiver(inputChannel, Looper.myLooper(),
- withSfVsync ? Choreographer.getSfInstance() : Choreographer.getInstance());
- if (mRegistrationListener != null) {
- mRegistrationListener.onRegistrationChanged(true /* isRegistered */);
- }
+ mMainExecutor.execute(() -> {
+ // Choreographer.getSfInstance() must be called on the thread that the input event
+ // receiver should be receiving events
+ mInputEventReceiver = new InputEventReceiver(inputChannel,
+ mMainExecutor.getLooper(), Choreographer.getSfInstance());
+ if (mRegistrationListener != null) {
+ mRegistrationListener.onRegistrationChanged(true /* isRegistered */);
+ }
+ });
}
/**
@@ -166,9 +169,11 @@
}
mInputEventReceiver.dispose();
mInputEventReceiver = null;
- if (mRegistrationListener != null) {
- mRegistrationListener.onRegistrationChanged(false /* isRegistered */);
- }
+ mMainExecutor.execute(() -> {
+ if (mRegistrationListener != null) {
+ mRegistrationListener.onRegistrationChanged(false /* isRegistered */);
+ }
+ });
}
public void dump(PrintWriter pw, String prefix) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 2e10fc9..2e515ee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -63,6 +63,7 @@
import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.PipUtils;
import java.util.ArrayList;
@@ -116,7 +117,8 @@
}
};
- private Handler mHandler = new Handler();
+ private ShellExecutor mMainExecutor;
+ private Handler mMainHandler;
private final Runnable mHideMenuRunnable = this::hideMenu;
@@ -127,10 +129,13 @@
protected View mTopEndContainer;
protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
- public PipMenuView(Context context, PhonePipMenuController controller) {
+ public PipMenuView(Context context, PhonePipMenuController controller,
+ ShellExecutor mainExecutor, Handler mainHandler) {
super(context, null, 0);
mContext = context;
mController = controller;
+ mMainExecutor = mainExecutor;
+ mMainHandler = mainHandler;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
inflate(context, R.layout.pip_menu, this);
@@ -412,17 +417,15 @@
d.setTint(Color.WHITE);
actionView.setImageDrawable(d);
}
- }, mHandler);
+ }, mMainHandler);
actionView.setContentDescription(action.getContentDescription());
if (action.isEnabled()) {
actionView.setOnClickListener(v -> {
- mHandler.post(() -> {
- try {
- action.getActionIntent().send();
- } catch (CanceledException e) {
- Log.w(TAG, "Failed to send action", e);
- }
- });
+ try {
+ action.getActionIntent().send();
+ } catch (CanceledException e) {
+ Log.w(TAG, "Failed to send action", e);
+ }
});
}
actionView.setEnabled(action.isEnabled());
@@ -480,13 +483,13 @@
}
private void cancelDelayedHide() {
- mHandler.removeCallbacks(mHideMenuRunnable);
+ mMainExecutor.removeCallbacks(mHideMenuRunnable);
}
private void repostDelayedHide(int delay) {
int recommendedTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(delay,
FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS);
- mHandler.removeCallbacks(mHideMenuRunnable);
- mHandler.postDelayed(mHideMenuRunnable, recommendedTimeout);
+ mMainExecutor.removeCallbacks(mHideMenuRunnable);
+ mMainExecutor.executeDelayed(mHideMenuRunnable, recommendedTimeout);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index 8c8f5c6..4df7cef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -40,6 +40,7 @@
import com.android.wm.shell.animation.FloatProperties;
import com.android.wm.shell.animation.PhysicsAnimator;
import com.android.wm.shell.common.FloatingContentCoordinator;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipSnapAlgorithm;
@@ -74,8 +75,6 @@
private PhonePipMenuController mMenuController;
private PipSnapAlgorithm mSnapAlgorithm;
- private final Handler mMainHandler = new Handler(Looper.getMainLooper());
-
/** The region that all of PIP must stay within. */
private final Rect mFloatingAllowedArea = new Rect();
@@ -130,10 +129,8 @@
SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
private final Consumer<Rect> mUpdateBoundsCallback = (Rect newBounds) -> {
- mMainHandler.post(() -> {
- mMenuController.updateMenuLayout(newBounds);
- mPipBoundsState.setBounds(newBounds);
- });
+ mMenuController.updateMenuLayout(newBounds);
+ mPipBoundsState.setBounds(newBounds);
};
/**
@@ -174,7 +171,8 @@
public PipMotionHelper(Context context, @NonNull PipBoundsState pipBoundsState,
PipTaskOrganizer pipTaskOrganizer, PhonePipMenuController menuController,
- PipSnapAlgorithm snapAlgorithm, FloatingContentCoordinator floatingContentCoordinator) {
+ PipSnapAlgorithm snapAlgorithm, FloatingContentCoordinator floatingContentCoordinator,
+ ShellExecutor mainExecutor) {
mContext = context;
mPipTaskOrganizer = pipTaskOrganizer;
mPipBoundsState = pipBoundsState;
@@ -184,8 +182,12 @@
mPipTaskOrganizer.registerPipTransitionCallback(mPipTransitionCallback);
mTemporaryBoundsPhysicsAnimator = PhysicsAnimator.getInstance(
mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
- mTemporaryBoundsPhysicsAnimator.setCustomAnimationHandler(
- mSfAnimationHandlerThreadLocal.get());
+
+ // Need to get the shell main thread sf vsync animation handler
+ mainExecutor.execute(() -> {
+ mTemporaryBoundsPhysicsAnimator.setCustomAnimationHandler(
+ mSfAnimationHandlerThreadLocal.get());
+ });
mResizePipUpdateListener = (target, values) -> {
if (mPipBoundsState.getMotionBoundsState().isInMotion()) {
@@ -256,10 +258,8 @@
mPipBoundsState.getMotionBoundsState().setBoundsInMotion(toBounds);
mPipTaskOrganizer.scheduleUserResizePip(getBounds(), toBounds,
(Rect newBounds) -> {
- mMainHandler.post(() -> {
mMenuController.updateMenuLayout(newBounds);
- });
- });
+ });
}
} else {
// If PIP is 'catching up' after being stuck in the dismiss target, update the animation
@@ -326,11 +326,7 @@
}
cancelPhysicsAnimation();
mMenuController.hideMenuWithoutResize();
- mPipTaskOrganizer.getUpdateHandler().post(() -> {
- mPipTaskOrganizer.exitPip(skipAnimation
- ? 0
- : LEAVE_PIP_DURATION);
- });
+ mPipTaskOrganizer.exitPip(skipAnimation ? 0 : LEAVE_PIP_DURATION);
}
/**
@@ -393,7 +389,8 @@
.spring(FloatProperties.RECT_WIDTH, getBounds().width(), mSpringConfig)
.spring(FloatProperties.RECT_HEIGHT, getBounds().height(), mSpringConfig)
.flingThenSpring(
- FloatProperties.RECT_X, velocityX, isStash ? mStashConfigX : mFlingConfigX,
+ FloatProperties.RECT_X, velocityX,
+ isStash ? mStashConfigX : mFlingConfigX,
mSpringConfig, true /* flingMustReachMinOrMax */)
.flingThenSpring(
FloatProperties.RECT_Y, velocityY, mFlingConfigY, mSpringConfig);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 762b738..41cc59d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -29,7 +29,6 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.input.InputManager;
-import android.os.Handler;
import android.os.Looper;
import android.provider.DeviceConfig;
import android.view.BatchedInputEventReceiver;
@@ -45,6 +44,7 @@
import com.android.internal.policy.TaskResizingAlgorithm;
import com.android.wm.shell.R;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
@@ -52,7 +52,7 @@
import com.android.wm.shell.pip.PipUiEventLogger;
import java.io.PrintWriter;
-import java.util.concurrent.Executor;
+import java.util.function.Consumer;
import java.util.function.Function;
/**
@@ -73,7 +73,7 @@
private final PhonePipMenuController mPhonePipMenuController;
private final PipUiEventLogger mPipUiEventLogger;
private final int mDisplayId;
- private final Executor mMainExecutor;
+ private final ShellExecutor mMainExecutor;
private final Region mTmpRegion = new Region();
private final PointF mDownPoint = new PointF();
@@ -91,7 +91,6 @@
private final Rect mDisplayBounds = new Rect();
private final Function<Rect, Rect> mMovementBoundsSupplier;
private final Runnable mUpdateMovementBoundsRunnable;
- private final Handler mHandler;
private int mDelta;
private float mTouchSlop;
@@ -119,10 +118,10 @@
PipBoundsState pipBoundsState, PipMotionHelper motionHelper,
PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier,
Runnable updateMovementBoundsRunnable, PipUiEventLogger pipUiEventLogger,
- PhonePipMenuController menuActivityController) {
+ PhonePipMenuController menuActivityController, ShellExecutor mainExecutor) {
mContext = context;
mDisplayId = context.getDisplayId();
- mMainExecutor = context.getMainExecutor();
+ mMainExecutor = mainExecutor;
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipBoundsState = pipBoundsState;
mMotionHelper = motionHelper;
@@ -131,7 +130,6 @@
mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
mPhonePipMenuController = menuActivityController;
mPipUiEventLogger = pipUiEventLogger;
- mHandler = new Handler(Looper.getMainLooper());
context.getDisplay().getRealSize(mMaxSize);
reloadResources();
@@ -140,7 +138,8 @@
DeviceConfig.NAMESPACE_SYSTEMUI,
PIP_PINCH_RESIZE,
/* defaultValue = */ false);
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mMainExecutor,
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
+ mMainExecutor,
new DeviceConfig.OnPropertiesChangedListener() {
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
@@ -213,8 +212,8 @@
// Register input event receiver
mInputMonitor = InputManager.getInstance().monitorGestureInput(
"pip-resize", mDisplayId);
- mInputEventReceiver = new SysUiInputEventReceiver(
- mInputMonitor.getInputChannel(), Looper.getMainLooper());
+ mInputEventReceiver = new PipResizeInputEventReceiver(
+ mInputMonitor.getInputChannel(), mMainExecutor.getLooper());
}
}
@@ -523,7 +522,7 @@
private void finishResize() {
if (!mLastResizeBounds.isEmpty()) {
- final Runnable callback = () -> {
+ final Consumer<Rect> callback = (rect) -> {
mUserResizeBounds.set(mLastResizeBounds);
mMotionHelper.synchronizePinnedStackBounds();
mUpdateMovementBoundsRunnable.run();
@@ -537,16 +536,10 @@
mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds,
mPipBoundsAlgorithm.getSnapFraction(mPipBoundsState.getBounds()));
mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds,
- PINCH_RESIZE_SNAP_DURATION, mAngle,
- (Rect rect) -> {
- mHandler.post(callback);
- });
+ PINCH_RESIZE_SNAP_DURATION, -mAngle, callback);
} else {
mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
- PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE,
- (Rect bounds) -> {
- mHandler.post(callback);
- });
+ PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE, callback);
}
mPipUiEventLogger.log(
PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE);
@@ -593,8 +586,8 @@
pw.println(innerPrefix + "mThresholdCrossed=" + mThresholdCrossed);
}
- class SysUiInputEventReceiver extends BatchedInputEventReceiver {
- SysUiInputEventReceiver(InputChannel channel, Looper looper) {
+ class PipResizeInputEventReceiver extends BatchedInputEventReceiver {
+ PipResizeInputEventReceiver(InputChannel channel, Looper looper) {
super(channel, looper, Choreographer.getSfInstance());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 75d674e..c9f5ae2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -167,18 +167,20 @@
mGesture = new DefaultPipTouchGesture();
mMotionHelper = new PipMotionHelper(mContext, pipBoundsState, pipTaskOrganizer,
mMenuController, mPipBoundsAlgorithm.getSnapAlgorithm(),
- floatingContentCoordinator);
+ floatingContentCoordinator, mainExecutor);
mPipResizeGestureHandler =
new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState,
mMotionHelper, pipTaskOrganizer, this::getMovementBounds,
- this::updateMovementBounds, pipUiEventLogger, menuController);
+ this::updateMovementBounds, pipUiEventLogger, menuController,
+ mainExecutor);
mPipDismissTargetHandler = new PipDismissTargetHandler(context, pipUiEventLogger,
- mMotionHelper, mHandler);
- mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
+ mMotionHelper, mainExecutor);
+ mTouchState = new PipTouchState(ViewConfiguration.get(context),
() -> mMenuController.showMenuWithDelay(MENU_STATE_FULL,
mPipBoundsState.getBounds(), true /* allowMenuTimeout */, willResizeMenu(),
shouldShowResizeHandle()),
- menuController::hideMenu);
+ menuController::hideMenu,
+ mainExecutor);
Resources res = context.getResources();
mEnableResize = res.getBoolean(R.bool.config_pipEnableResizeForMenu);
@@ -196,7 +198,7 @@
PIP_STASHING,
/* defaultValue = */ true);
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
- context.getMainExecutor(),
+ mainExecutor,
properties -> {
if (properties.getKeyset().contains(PIP_STASHING)) {
mEnableStash = properties.getBoolean(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java
index 5f2327c..53303ff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java
@@ -25,6 +25,7 @@
import android.view.ViewConfiguration;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.common.ShellExecutor;
import java.io.PrintWriter;
@@ -39,7 +40,7 @@
public static final long DOUBLE_TAP_TIMEOUT = 200;
static final long HOVER_EXIT_TIMEOUT = 50;
- private final Handler mHandler;
+ private final ShellExecutor mMainExecutor;
private final ViewConfiguration mViewConfig;
private final Runnable mDoubleTapTimeoutCallback;
private final Runnable mHoverExitTimeoutCallback;
@@ -67,12 +68,12 @@
private int mActivePointerId;
private int mLastTouchDisplayId = Display.INVALID_DISPLAY;
- public PipTouchState(ViewConfiguration viewConfig, Handler handler,
- Runnable doubleTapTimeoutCallback, Runnable hoverExitTimeoutCallback) {
+ public PipTouchState(ViewConfiguration viewConfig, Runnable doubleTapTimeoutCallback,
+ Runnable hoverExitTimeoutCallback, ShellExecutor mainExecutor) {
mViewConfig = viewConfig;
- mHandler = handler;
mDoubleTapTimeoutCallback = doubleTapTimeoutCallback;
mHoverExitTimeoutCallback = hoverExitTimeoutCallback;
+ mMainExecutor = mainExecutor;
}
/**
@@ -116,7 +117,7 @@
mIsDragging = false;
mLastDownTouchTime = mDownTouchTime;
if (mDoubleTapTimeoutCallback != null) {
- mHandler.removeCallbacks(mDoubleTapTimeoutCallback);
+ mMainExecutor.removeCallbacks(mDoubleTapTimeoutCallback);
}
break;
}
@@ -324,8 +325,8 @@
public void scheduleDoubleTapTimeoutCallback() {
if (mIsWaitingForDoubleTap) {
long delay = getDoubleTapTimeoutCallbackDelay();
- mHandler.removeCallbacks(mDoubleTapTimeoutCallback);
- mHandler.postDelayed(mDoubleTapTimeoutCallback, delay);
+ mMainExecutor.removeCallbacks(mDoubleTapTimeoutCallback);
+ mMainExecutor.executeDelayed(mDoubleTapTimeoutCallback, delay);
}
}
@@ -342,17 +343,17 @@
*/
public void removeDoubleTapTimeoutCallback() {
mIsWaitingForDoubleTap = false;
- mHandler.removeCallbacks(mDoubleTapTimeoutCallback);
+ mMainExecutor.removeCallbacks(mDoubleTapTimeoutCallback);
}
@VisibleForTesting
public void scheduleHoverExitTimeoutCallback() {
- mHandler.removeCallbacks(mHoverExitTimeoutCallback);
- mHandler.postDelayed(mHoverExitTimeoutCallback, HOVER_EXIT_TIMEOUT);
+ mMainExecutor.removeCallbacks(mHoverExitTimeoutCallback);
+ mMainExecutor.executeDelayed(mHoverExitTimeoutCallback, HOVER_EXIT_TIMEOUT);
}
void removeHoverExitTimeoutCallback() {
- mHandler.removeCallbacks(mHoverExitTimeoutCallback);
+ mMainExecutor.removeCallbacks(mHoverExitTimeoutCallback);
}
void addMovementToVelocityTracker(MotionEvent event) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipUpdateThread.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipUpdateThread.java
deleted file mode 100644
index d686cac..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipUpdateThread.java
+++ /dev/null
@@ -1,60 +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.wm.shell.pip.phone;
-
-import android.os.Handler;
-import android.os.HandlerThread;
-
-/**
- * Similar to {@link com.android.internal.os.BackgroundThread}, this is a shared singleton
- * foreground thread for each process for updating PIP.
- */
-public final class PipUpdateThread extends HandlerThread {
- private static PipUpdateThread sInstance;
- private static Handler sHandler;
-
- private PipUpdateThread() {
- super("pip");
- }
-
- private static void ensureThreadLocked() {
- if (sInstance == null) {
- sInstance = new PipUpdateThread();
- sInstance.start();
- sHandler = new Handler(sInstance.getLooper());
- }
- }
-
- /**
- * @return the static update thread instance
- */
- public static PipUpdateThread get() {
- synchronized (PipUpdateThread.class) {
- ensureThreadLocked();
- return sInstance;
- }
- }
- /**
- * @return the static update thread handler instance
- */
- public static Handler getHandler() {
- synchronized (PipUpdateThread.class) {
- ensureThreadLocked();
- return sHandler;
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 8bc60f9..61cf22b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -36,6 +36,7 @@
import com.android.wm.shell.R;
import com.android.wm.shell.WindowManagerShellWrapper;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
@@ -51,7 +52,7 @@
/**
* Manages the picture-in-picture (PIP) UI and states.
*/
-public class TvPipController implements Pip, PipTaskOrganizer.PipTransitionCallback,
+public class TvPipController implements PipTaskOrganizer.PipTransitionCallback,
TvPipMenuController.Delegate, TvPipNotificationController.Delegate {
private static final String TAG = "TvPipController";
static final boolean DEBUG = true;
@@ -90,13 +91,15 @@
private final PipMediaController mPipMediaController;
private final TvPipNotificationController mPipNotificationController;
private final TvPipMenuController mTvPipMenuController;
+ private final ShellExecutor mMainExecutor;
+ private final TvPipImpl mImpl = new TvPipImpl();
private @State int mState = STATE_NO_PIP;
private int mPinnedTaskId = NONEXISTENT_TASK_ID;
private int mResizeAnimationDuration;
- public TvPipController(
+ public static Pip create(
Context context,
PipBoundsState pipBoundsState,
PipBoundsAlgorithm pipBoundsAlgorithm,
@@ -105,8 +108,34 @@
PipMediaController pipMediaController,
TvPipNotificationController pipNotificationController,
TaskStackListenerImpl taskStackListener,
- WindowManagerShellWrapper wmShell) {
+ WindowManagerShellWrapper wmShell,
+ ShellExecutor mainExecutor) {
+ return new TvPipController(
+ context,
+ pipBoundsState,
+ pipBoundsAlgorithm,
+ pipTaskOrganizer,
+ tvPipMenuController,
+ pipMediaController,
+ pipNotificationController,
+ taskStackListener,
+ wmShell,
+ mainExecutor).mImpl;
+ }
+
+ private TvPipController(
+ Context context,
+ PipBoundsState pipBoundsState,
+ PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipTaskOrganizer pipTaskOrganizer,
+ TvPipMenuController tvPipMenuController,
+ PipMediaController pipMediaController,
+ TvPipNotificationController pipNotificationController,
+ TaskStackListenerImpl taskStackListener,
+ WindowManagerShellWrapper wmShell,
+ ShellExecutor mainExecutor) {
mContext = context;
+ mMainExecutor = mainExecutor;
mPipBoundsState = pipBoundsState;
mPipBoundsState.setDisplayInfo(getDisplayInfo());
@@ -129,8 +158,7 @@
registerWmShellPinnedStackListener(wmShell);
}
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
+ private void onConfigurationChanged(Configuration newConfig) {
if (DEBUG) Log.d(TAG, "onConfigurationChanged(), state=" + stateToName(mState));
if (isPipShown()) {
@@ -145,8 +173,7 @@
/**
* Returns {@code true} if Pip is shown.
*/
- @Override
- public boolean isPipShown() {
+ private boolean isPipShown() {
return mState != STATE_NO_PIP;
}
@@ -211,8 +238,7 @@
* @param state the to determine the Pip bounds. IMPORTANT: should always match the current
* state of the Controller.
*/
- @Override
- public void resizePinnedStack(@State int state) {
+ private void resizePinnedStack(@State int state) {
if (state != mState) {
throw new IllegalArgumentException("The passed state should match the current state!");
}
@@ -240,8 +266,7 @@
mPipTaskOrganizer.scheduleAnimateResizePip(newBounds, mResizeAnimationDuration, null);
}
- @Override
- public void registerSessionListenerForCurrentUser() {
+ private void registerSessionListenerForCurrentUser() {
mPipMediaController.registerSessionListenerForCurrentUser();
}
@@ -418,4 +443,20 @@
throw new IllegalArgumentException("Unknown state " + state);
}
}
+
+ private class TvPipImpl implements Pip {
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ mMainExecutor.execute(() -> {
+ TvPipController.this.onConfigurationChanged(newConfig);
+ });
+ }
+
+ @Override
+ public void registerSessionListenerForCurrentUser() {
+ mMainExecutor.execute(() -> {
+ TvPipController.this.registerSessionListenerForCurrentUser();
+ });
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index 470ab0c..ee41b41 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -24,6 +24,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ParceledListSlice;
+import android.os.Handler;
import android.util.Log;
import android.view.SurfaceControl;
@@ -47,6 +48,7 @@
private final Context mContext;
private final SystemWindows mSystemWindows;
private final PipBoundsState mPipBoundsState;
+ private final Handler mMainHandler;
private Delegate mDelegate;
private SurfaceControl mLeash;
@@ -56,10 +58,12 @@
private final List<RemoteAction> mAppActions = new ArrayList<>();
public TvPipMenuController(Context context, PipBoundsState pipBoundsState,
- SystemWindows systemWindows, PipMediaController pipMediaController) {
+ SystemWindows systemWindows, PipMediaController pipMediaController,
+ Handler mainHandler) {
mContext = context;
mPipBoundsState = pipBoundsState;
mSystemWindows = systemWindows;
+ mMainHandler = mainHandler;
// We need to "close" the menu the platform call for all the system dialogs to close (for
// example, on the Home button press).
@@ -69,8 +73,9 @@
hideMenu();
}
};
- context.registerReceiver(closeSystemDialogsBroadcastReceiver,
- new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+ context.registerReceiverForAllUsers(closeSystemDialogsBroadcastReceiver,
+ new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), null /* permission */,
+ mainHandler);
pipMediaController.addActionListener(this::onMediaActionsChanged);
}
@@ -199,9 +204,9 @@
return;
}
if (!mAppActions.isEmpty()) {
- mMenuView.setAdditionalActions(mAppActions);
+ mMenuView.setAdditionalActions(mAppActions, mMainHandler);
} else {
- mMenuView.setAdditionalActions(mMediaActions);
+ mMenuView.setAdditionalActions(mMediaActions, mMainHandler);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index e08ca52..d6cd9ea 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -49,7 +49,7 @@
/**
* A View that represents Pip Menu on TV. It's responsible for displaying 2 ever-present Pip Menu
* actions: Fullscreen and Close, but could also display "additional" actions, that may be set via
- * a {@link #setAdditionalActions(List)} call.
+ * a {@link #setAdditionalActions(List, Handler)} call.
*/
public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
private static final String TAG = "TvPipMenuView";
@@ -57,7 +57,6 @@
private static final float DISABLED_ACTION_ALPHA = 0.54f;
- private final Handler mUiThreadHandler;
private final Animator mFadeInAnimation;
private final Animator mFadeOutAnimation;
@Nullable private Listener mListener;
@@ -80,7 +79,6 @@
public TvPipMenuView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mUiThreadHandler = new Handler(Looper.getMainLooper());
inflate(context, R.layout.tv_pip_menu, this);
@@ -132,7 +130,7 @@
}
}
- void setAdditionalActions(List<RemoteAction> actions) {
+ void setAdditionalActions(List<RemoteAction> actions, Handler mainHandler) {
if (DEBUG) Log.d(TAG, "setAdditionalActions()");
// Make sure we exactly as many additional buttons as we have actions to display.
@@ -176,7 +174,7 @@
action.getIcon().loadDrawableAsync(mContext, drawable -> {
drawable.setTint(Color.WHITE);
button.setImageDrawable(drawable);
- }, mUiThreadHandler);
+ }, mainHandler);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
index ce4b608..a474831 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
@@ -27,6 +27,7 @@
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.media.MediaMetadata;
+import android.os.Handler;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
@@ -60,6 +61,7 @@
private final NotificationManager mNotificationManager;
private final Notification.Builder mNotificationBuilder;
private final ActionBroadcastReceiver mActionBroadcastReceiver;
+ private final Handler mMainHandler;
private Delegate mDelegate;
private String mDefaultTitle;
@@ -70,10 +72,12 @@
private String mMediaTitle;
private Bitmap mArt;
- public TvPipNotificationController(Context context, PipMediaController pipMediaController) {
+ public TvPipNotificationController(Context context, PipMediaController pipMediaController,
+ Handler mainHandler) {
mContext = context;
mPackageManager = context.getPackageManager();
mNotificationManager = context.getSystemService(NotificationManager.class);
+ mMainHandler = mainHandler;
mNotificationBuilder = new Notification.Builder(context, NOTIFICATION_CHANNEL)
.setLocalOnly(true)
@@ -219,7 +223,8 @@
void register() {
if (mRegistered) return;
- mContext.registerReceiver(this, mIntentFilter, UserHandle.USER_ALL);
+ mContext.registerReceiverForAllUsers(this, mIntentFilter, null /* permission */,
+ mMainHandler);
mRegistered = true;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
index 9eb13fb..5f5c30b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
@@ -46,7 +46,7 @@
@Override
public boolean hasCallback(Runnable r) {
- return !mRunnables.isEmpty();
+ return mRunnables.contains(r);
}
@Override
@@ -55,8 +55,8 @@
}
public void flushAll() {
- for (int i = mRunnables.size() - 1; i >= 0; --i) {
- mRunnables.get(i).run();
+ for (Runnable r : mRunnables) {
+ r.run();
}
mRunnables.clear();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index b5d10d7..245858d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -43,7 +43,9 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
@@ -71,6 +73,7 @@
@Mock private PipUiEventLogger mMockPipUiEventLogger;
@Mock private Optional<LegacySplitScreen> mMockOptionalSplitScreen;
@Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
+ private TestShellExecutor mMainExecutor;
private PipBoundsState mPipBoundsState;
private ComponentName mComponent1;
@@ -82,10 +85,12 @@
mComponent1 = new ComponentName(mContext, "component1");
mComponent2 = new ComponentName(mContext, "component2");
mPipBoundsState = new PipBoundsState(mContext);
+ mMainExecutor = new TestShellExecutor();
mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext, mPipBoundsState,
mMockPipBoundsAlgorithm, mMockPhonePipMenuController,
mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen, mMockdDisplayController,
- mMockPipUiEventLogger, mMockShellTaskOrganizer));
+ mMockPipUiEventLogger, mMockShellTaskOrganizer, mMainExecutor));
+ mMainExecutor.flushAll();
preparePipTaskOrg();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java
index 000f7e8..0d4d126 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java
@@ -24,18 +24,15 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import android.os.Handler;
-import android.os.Looper;
import android.os.SystemClock;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestShellExecutor;
import org.junit.Before;
import org.junit.Test;
@@ -45,23 +42,22 @@
@RunWith(AndroidTestingRunner.class)
@SmallTest
-@RunWithLooper
public class PipTouchStateTest extends ShellTestCase {
private PipTouchState mTouchState;
private CountDownLatch mDoubleTapCallbackTriggeredLatch;
private CountDownLatch mHoverExitCallbackTriggeredLatch;
+ private TestShellExecutor mShellMainExecutor;
@Before
public void setUp() throws Exception {
+ mShellMainExecutor = new TestShellExecutor();
mDoubleTapCallbackTriggeredLatch = new CountDownLatch(1);
mHoverExitCallbackTriggeredLatch = new CountDownLatch(1);
mTouchState = new PipTouchState(ViewConfiguration.get(getContext()),
- Handler.createAsync(Looper.myLooper()), () -> {
- mDoubleTapCallbackTriggeredLatch.countDown();
- }, () -> {
- mHoverExitCallbackTriggeredLatch.countDown();
- });
+ mDoubleTapCallbackTriggeredLatch::countDown,
+ mHoverExitCallbackTriggeredLatch::countDown,
+ mShellMainExecutor);
assertFalse(mTouchState.isDoubleTap());
assertFalse(mTouchState.isWaitingForDoubleTap());
}
@@ -91,9 +87,7 @@
assertTrue(mTouchState.getDoubleTapTimeoutCallbackDelay() == 10);
mTouchState.scheduleDoubleTapTimeoutCallback();
- // TODO: Remove this sleep. Its only being added because it speeds up this test a bit.
- Thread.sleep(15);
- TestableLooper.get(this).processAllMessages();
+ mShellMainExecutor.flushAll();
assertTrue(mDoubleTapCallbackTriggeredLatch.getCount() == 0);
}
@@ -128,17 +122,13 @@
@Test
public void testHoverExitTimeout_timeoutCallbackCalled() throws Exception {
mTouchState.scheduleHoverExitTimeoutCallback();
-
- // TODO: Remove this sleep. Its only being added because it speeds up this test a bit.
- Thread.sleep(50);
- TestableLooper.get(this).processAllMessages();
+ mShellMainExecutor.flushAll();
assertTrue(mHoverExitCallbackTriggeredLatch.getCount() == 0);
}
@Test
public void testHoverExitTimeout_timeoutCallbackNotCalled() throws Exception {
mTouchState.scheduleHoverExitTimeoutCallback();
- TestableLooper.get(this).processAllMessages();
assertTrue(mHoverExitCallbackTriggeredLatch.getCount() == 1);
}
@@ -147,14 +137,12 @@
mTouchState.scheduleHoverExitTimeoutCallback();
mTouchState.onTouchEvent(createMotionEvent(ACTION_BUTTON_PRESS, SystemClock.uptimeMillis(),
0, 0));
-
- // TODO: Remove this sleep. Its only being added because it speeds up this test a bit.
- Thread.sleep(50);
- TestableLooper.get(this).processAllMessages();
+ mShellMainExecutor.flushAll();
assertTrue(mHoverExitCallbackTriggeredLatch.getCount() == 1);
}
private MotionEvent createMotionEvent(int action, long eventTime, float x, float y) {
return MotionEvent.obtain(0, eventTime, action, x, y, 0);
}
+
}
diff --git a/packages/Connectivity/framework/Android.bp b/packages/Connectivity/framework/Android.bp
new file mode 100644
index 0000000..8db8d76
--- /dev/null
+++ b/packages/Connectivity/framework/Android.bp
@@ -0,0 +1,29 @@
+//
+// 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.
+//
+
+// TODO: use a java_library in the bootclasspath instead
+filegroup {
+ name: "framework-connectivity-sources",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.aidl",
+ ],
+ path: "src",
+ visibility: [
+ "//frameworks/base",
+ "//packages/modules/Connectivity:__subpackages__",
+ ],
+}
\ No newline at end of file
diff --git a/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgent.aidl b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgent.aidl
new file mode 100644
index 0000000..64b5567
--- /dev/null
+++ b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgent.aidl
@@ -0,0 +1,49 @@
+/**
+ * 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 perNmissions and
+ * limitations under the License.
+ */
+package com.android.connectivity.aidl;
+
+import android.net.NattKeepalivePacketData;
+import android.net.QosFilterParcelable;
+import android.net.TcpKeepalivePacketData;
+
+import com.android.connectivity.aidl.INetworkAgentRegistry;
+
+/**
+ * Interface to notify NetworkAgent of connectivity events.
+ * @hide
+ */
+oneway interface INetworkAgent {
+ void onRegistered(in INetworkAgentRegistry registry);
+ void onDisconnected();
+ void onBandwidthUpdateRequested();
+ void onValidationStatusChanged(int validationStatus,
+ in @nullable String captivePortalUrl);
+ void onSaveAcceptUnvalidated(boolean acceptUnvalidated);
+ void onStartNattSocketKeepalive(int slot, int intervalDurationMs,
+ in NattKeepalivePacketData packetData);
+ void onStartTcpSocketKeepalive(int slot, int intervalDurationMs,
+ in TcpKeepalivePacketData packetData);
+ void onStopSocketKeepalive(int slot);
+ void onSignalStrengthThresholdsUpdated(in int[] thresholds);
+ void onPreventAutomaticReconnect();
+ void onAddNattKeepalivePacketFilter(int slot,
+ in NattKeepalivePacketData packetData);
+ void onAddTcpKeepalivePacketFilter(int slot,
+ in TcpKeepalivePacketData packetData);
+ void onRemoveKeepalivePacketFilter(int slot);
+ void onQosFilterCallbackRegistered(int qosCallbackId, in QosFilterParcelable filterParcel);
+ void onQosCallbackUnregistered(int qosCallbackId);
+}
diff --git a/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl
new file mode 100644
index 0000000..f0193db
--- /dev/null
+++ b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl
@@ -0,0 +1,41 @@
+/**
+ * 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 perNmissions and
+ * limitations under the License.
+ */
+package com.android.connectivity.aidl;
+
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.QosSession;
+import android.telephony.data.EpsBearerQosSessionAttributes;
+
+/**
+ * Interface for NetworkAgents to send network network properties.
+ * @hide
+ */
+oneway interface INetworkAgentRegistry {
+ void sendNetworkCapabilities(in NetworkCapabilities nc);
+ void sendLinkProperties(in LinkProperties lp);
+ // TODO: consider replacing this by "markConnected()" and removing
+ void sendNetworkInfo(in NetworkInfo info);
+ void sendScore(int score);
+ void sendExplicitlySelected(boolean explicitlySelected, boolean acceptPartial);
+ void sendSocketKeepaliveEvent(int slot, int reason);
+ void sendUnderlyingNetworks(in @nullable List<Network> networks);
+ void sendEpsQosSessionAvailable(int callbackId, in QosSession session, in EpsBearerQosSessionAttributes attributes);
+ void sendQosSessionLost(int qosCallbackId, in QosSession session);
+ void sendQosCallbackError(int qosCallbackId, int exceptionType);
+}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java
index 35bc490..fcee98d 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java
@@ -21,7 +21,6 @@
import android.content.Intent;
import android.os.UserHandle;
import android.os.image.DynamicSystemClient;
-import android.os.image.DynamicSystemManager;
/**
@@ -41,15 +40,6 @@
return;
}
- DynamicSystemManager dynSystem =
- (DynamicSystemManager) context.getSystemService(Context.DYNAMIC_SYSTEM_SERVICE);
-
- boolean isInUse = (dynSystem != null) && dynSystem.isInUse();
-
- if (!isInUse) {
- return;
- }
-
Intent startServiceIntent = new Intent(
context, DynamicSystemInstallationService.class);
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 6c51f2f..84dacfd 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -239,7 +239,7 @@
<bool name="def_aware_lock_enabled">false</bool>
<!-- Default for setting for Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED -->
- <bool name="def_hdmiControlAutoDeviceOff">false</bool>
+ <bool name="def_hdmiControlAutoDeviceOff">true</bool>
<!-- Default for Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED -->
<bool name="def_swipe_bottom_to_notification_enabled">true</bool>
@@ -249,4 +249,7 @@
<!-- Default for Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY -->
<integer name="def_accessibility_magnification_capabilities">3</integer>
+
+ <!-- Default for Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW -->
+ <bool name="def_enable_non_resizable_multi_window">false</bool>
</resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index edb5506..c7790fd 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3342,7 +3342,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 197;
+ private static final int SETTINGS_VERSION = 198;
private final int mUserId;
@@ -4815,6 +4815,24 @@
currentVersion = 197;
}
+ if (currentVersion == 197) {
+ // Version 197: Set the default value for Global Settings:
+ // DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW
+ final SettingsState globalSettings = getGlobalSettingsLocked();
+ final Setting enableNonResizableMultiWindow = globalSettings.getSettingLocked(
+ Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW);
+ if (enableNonResizableMultiWindow.isNull()) {
+ final boolean defEnableNonResizableMultiWindow = getContext().getResources()
+ .getBoolean(R.bool.def_enable_non_resizable_multi_window);
+ globalSettings.insertSettingLocked(
+ Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
+ defEnableNonResizableMultiWindow ? "1" : "0", null, true,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+
+ currentVersion = 198;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 2d972e0..6eb54c2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -192,7 +192,7 @@
statusAreaLP.removeRule(RelativeLayout.ALIGN_PARENT_START);
statusAreaLP.removeRule(RelativeLayout.START_OF);
statusAreaLP.addRule(RelativeLayout.BELOW, R.id.clock_view);
- statusAreaLP.width = ViewGroup.LayoutParams.WRAP_CONTENT;
+ statusAreaLP.width = ViewGroup.LayoutParams.MATCH_PARENT;
}
requestLayout();
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index 8ebcb20..756d610 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -156,9 +156,14 @@
}
@Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- mAnimator.updateMargin((ViewGroup.MarginLayoutParams) getLayoutParams());
+ public void setLayoutParams(ViewGroup.LayoutParams params) {
+ mAnimator.updateMargin((ViewGroup.MarginLayoutParams) params);
+ super.setLayoutParams(params);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChildren(widthMeasureSpec, heightMeasureSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
index 5fc2e53..40fe7b1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
@@ -47,4 +47,9 @@
(int) sensorRect.right - margin,
(int) sensorRect.bottom - margin);
}
+
+ @Override
+ public void setAlpha(int alpha) {
+ mFingerprintDrawable.setAlpha(alpha);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
index 1a2a492..52662ae 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
@@ -23,10 +23,13 @@
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.RectF;
+import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.graphics.ColorUtils;
+import com.android.settingslib.Utils;
import com.android.systemui.R;
/**
@@ -35,8 +38,11 @@
public class UdfpsAnimationEnroll extends UdfpsAnimation {
private static final String TAG = "UdfpsAnimationEnroll";
+ private static final float SHADOW_RADIUS = 5.f;
+
@Nullable private RectF mSensorRect;
@NonNull private final Paint mSensorPaint;
+ private final int mNotificationShadeColor;
UdfpsAnimationEnroll(@NonNull Context context) {
super(context);
@@ -44,8 +50,11 @@
mSensorPaint = new Paint(0 /* flags */);
mSensorPaint.setAntiAlias(true);
mSensorPaint.setColor(Color.WHITE);
- mSensorPaint.setShadowLayer(UdfpsView.SENSOR_SHADOW_RADIUS, 0, 0, Color.BLACK);
+ mSensorPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, Color.BLACK);
mSensorPaint.setStyle(Paint.Style.FILL);
+
+ mNotificationShadeColor = Utils.getColorAttr(context,
+ android.R.attr.colorBackgroundFloating).getDefaultColor();
}
@Override
@@ -73,7 +82,14 @@
@Override
public void setAlpha(int alpha) {
+ super.setAlpha(alpha);
+ // Gradually fade into the notification shade color. This needs to be done because the
+ // UDFPS view is drawn on a layer on top of the notification shade
+ final float percent = alpha / 255.f;
+ mSensorPaint.setColor(ColorUtils.blendARGB(mNotificationShadeColor, Color.WHITE, percent));
+ mSensorPaint.setShadowLayer(SHADOW_RADIUS, 0, 0,
+ ColorUtils.blendARGB(mNotificationShadeColor, Color.BLACK, percent));
}
@Override
@@ -85,4 +101,12 @@
public int getOpacity() {
return 0;
}
+
+ public void onEnrollmentProgress(int remaining) {
+ Log.d(TAG, "Remaining: " + remaining);
+ }
+
+ public void onEnrollmentHelp() {
+ Log.d(TAG, "onEnrollmentHelp");
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java
index 7563e73..efc864a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java
@@ -44,11 +44,6 @@
}
@Override
- public void setAlpha(int alpha) {
-
- }
-
- @Override
public void setColorFilter(@Nullable ColorFilter colorFilter) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java
index 3e46a65..501de9d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java
@@ -99,11 +99,6 @@
}
@Override
- public void setAlpha(int alpha) {
-
- }
-
- @Override
public void setColorFilter(@Nullable ColorFilter colorFilter) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index a2b2bea8..dc09fa7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -50,6 +50,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.settings.SystemSettings;
@@ -131,6 +132,16 @@
}
@Override
+ public void onEnrollmentProgress(int sensorId, int remaining) {
+ mView.onEnrollmentProgress(remaining);
+ }
+
+ @Override
+ public void onEnrollmentHelp(int sensorId) {
+ mView.onEnrollmentHelp();
+ }
+
+ @Override
public void setDebugMessage(int sensorId, String message) {
mView.setDebugMessage(message);
}
@@ -174,7 +185,8 @@
WindowManager windowManager,
SystemSettings systemSettings,
@NonNull StatusBarStateController statusBarStateController,
- @Main DelayableExecutor fgExecutor) {
+ @Main DelayableExecutor fgExecutor,
+ @NonNull ScrimController scrimController) {
mContext = context;
// The fingerprint manager is queried for UDFPS before this class is constructed, so the
// fingerprint manager should never be null.
@@ -210,6 +222,8 @@
mHbmSupported = !TextUtils.isEmpty(mHbmPath);
mView.setHbmSupported(mHbmSupported);
+ scrimController.addScrimChangedListener(mView);
+ statusBarStateController.addCallback(mView);
// This range only consists of the minimum and maximum values, which only cover
// non-high-brightness mode.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index 4cb8101..265703e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -16,6 +16,10 @@
package com.android.systemui.biometrics;
+import static com.android.systemui.statusbar.StatusBarState.FULLSCREEN_USER_SWITCHER;
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -35,15 +39,19 @@
import com.android.systemui.R;
import com.android.systemui.doze.DozeReceiver;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.ScrimController;
/**
* A full screen view with a configurable illumination dot and scrim.
*/
-public class UdfpsView extends View implements DozeReceiver {
+public class UdfpsView extends View implements DozeReceiver,
+ StatusBarStateController.StateListener, ScrimController.ScrimChangedListener{
private static final String TAG = "UdfpsView";
// Values in pixels.
- public static final float SENSOR_SHADOW_RADIUS = 2.0f;
+ private static final float SENSOR_SHADOW_RADIUS = 2.0f;
private static final int DEBUG_TEXT_SIZE_PX = 32;
@@ -55,7 +63,6 @@
@NonNull private final Paint mSensorPaint;
private final float mSensorTouchAreaCoefficient;
-
// Stores rounded up values from mSensorRect. Necessary for APIs that only take Rect (not RecF).
@NonNull private final Rect mTouchableRegion;
// mInsetsListener is used to set the touchable region for our window. Our window covers the
@@ -71,6 +78,9 @@
private boolean mShowScrimAndDot;
private boolean mIsHbmSupported;
@Nullable private String mDebugMessage;
+ private int mStatusBarState;
+ private boolean mNotificationShadeExpanded;
+ private int mNotificationPanelAlpha;
// Runnable that will be run after the illumination dot and scrim are shown.
// The runnable is reset to null after it's executed once.
@@ -137,6 +147,22 @@
}
}
+ @Override
+ public void onExpandedChanged(boolean isExpanded) {
+ mNotificationShadeExpanded = isExpanded;
+ }
+
+ @Override
+ public void onStateChanged(int newState) {
+ mStatusBarState = newState;
+ }
+
+ @Override
+ public void onAlphaChanged(float alpha) {
+ mNotificationPanelAlpha = (int) (alpha * 255);
+ postInvalidate();
+ }
+
// The "h" and "w" are the display's height and width relative to its current rotation.
protected void updateSensorRect(int h, int w) {
// mSensorProps coordinates assume portrait mode.
@@ -148,10 +174,12 @@
// Transform mSensorRect if the device is in landscape mode.
switch (mContext.getDisplay().getRotation()) {
case Surface.ROTATION_90:
+ //noinspection SuspiciousNameCombination
mSensorRect.set(mSensorRect.top, h - mSensorRect.right, mSensorRect.bottom,
h - mSensorRect.left);
break;
case Surface.ROTATION_270:
+ //noinspection SuspiciousNameCombination
mSensorRect.set(w - mSensorRect.bottom, mSensorRect.left, w - mSensorRect.top,
mSensorRect.right);
break;
@@ -223,6 +251,8 @@
canvas.drawOval(mSensorRect, mSensorPaint);
} else {
if (mUdfpsAnimation != null) {
+ final int alpha = shouldPauseAuth() ? 255 - mNotificationPanelAlpha : 255;
+ mUdfpsAnimation.setAlpha(alpha);
mUdfpsAnimation.draw(canvas);
}
}
@@ -261,7 +291,19 @@
return x > (cx - rx * mSensorTouchAreaCoefficient)
&& x < (cx + rx * mSensorTouchAreaCoefficient)
&& y > (cy - ry * mSensorTouchAreaCoefficient)
- && y < (cy + ry * mSensorTouchAreaCoefficient);
+ && y < (cy + ry * mSensorTouchAreaCoefficient)
+ && !shouldPauseAuth();
+ }
+
+ /**
+ * States where UDFPS should temporarily not be authenticating. Instead of completely stopping
+ * authentication which would cause the UDFPS icons to abruptly disappear, do it here by not
+ * sending onFingerDown and smoothly animating away.
+ */
+ private boolean shouldPauseAuth() {
+ return (mNotificationShadeExpanded && mStatusBarState != KEYGUARD)
+ || mStatusBarState == SHADE_LOCKED
+ || mStatusBarState == FULLSCREEN_USER_SWITCHER;
}
void setScrimAlpha(int alpha) {
@@ -281,4 +323,16 @@
mShowScrimAndDot = false;
invalidate();
}
+
+ void onEnrollmentProgress(int remaining) {
+ if (mUdfpsAnimation instanceof UdfpsAnimationEnroll) {
+ ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentProgress(remaining);
+ }
+ }
+
+ void onEnrollmentHelp() {
+ if (mUdfpsAnimation instanceof UdfpsAnimationEnroll) {
+ ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentHelp();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index b71036c..395729c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -49,7 +49,6 @@
import android.hardware.display.DisplayManager;
import android.media.MediaActionSound;
import android.net.Uri;
-import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -185,7 +184,6 @@
private final View mDecorView;
private final DisplayManager mDisplayManager;
- private final Binder mWindowToken;
private ScreenshotView mScreenshotView;
private Bitmap mScreenBitmap;
private SaveImageInBackgroundTask mSaveInBgTask;
@@ -247,9 +245,6 @@
mAccessibilityManager = AccessibilityManager.getInstance(mContext);
mConfigProxy = configProxy;
- mWindowToken = new Binder("ScreenshotController");
- mScrollCaptureClient.setHostWindowToken(mWindowToken);
-
// Setup the window that we are going to use
mWindowLayoutParams = new WindowManager.LayoutParams(
MATCH_PARENT, MATCH_PARENT, /* xpos */ 0, /* ypos */ 0, TYPE_SCREENSHOT,
@@ -263,7 +258,6 @@
mWindowLayoutParams.setTitle("ScreenshotAnimation");
mWindowLayoutParams.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- mWindowLayoutParams.token = mWindowToken;
// This is needed to let touches pass through outside the touchable areas
mWindowLayoutParams.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
@@ -526,21 +520,6 @@
// Start the post-screenshot animation
startAnimation(finisher, screenRect, screenInsets, showFlash);
-
- if (mConfigProxy.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SCREENSHOT_SCROLLING_ENABLED, false)) {
- mScrollCaptureClient.request(DEFAULT_DISPLAY, (connection) ->
- mScreenshotView.showScrollChip(() ->
- runScrollCapture(connection,
- () -> mScreenshotHandler.post(
- () -> dismissScreenshot(false)))));
- }
- }
-
- private void runScrollCapture(ScrollCaptureClient.Connection connection, Runnable andThen) {
- ScrollCaptureController controller = new ScrollCaptureController(mContext, connection,
- mMainExecutor, mBgExecutor, mImageExporter);
- controller.run(andThen);
}
/**
@@ -571,17 +550,48 @@
}
/**
+ * If scrolling is enabled, check whether the current view is scrollable and if so, show the
+ * scroll chip.
+ */
+ private void maybeRequestScrollCapture() {
+ if (mConfigProxy.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.SCREENSHOT_SCROLLING_ENABLED, false)) {
+ mScrollCaptureClient.setHostWindowToken(mDecorView.getWindowToken());
+ mScrollCaptureClient.request(DEFAULT_DISPLAY, (connection) ->
+ mScreenshotView.showScrollChip(() -> {
+ ScrollCaptureController controller = new ScrollCaptureController(
+ mContext, connection, mMainExecutor, mBgExecutor, mImageExporter);
+ controller.run(() -> mScreenshotHandler.post(
+ () -> dismissScreenshot(false)));
+ }));
+ }
+ }
+
+ /**
* Starts the animation after taking the screenshot
*/
private void startAnimation(final Consumer<Uri> finisher, Rect screenRect, Insets screenInsets,
boolean showFlash) {
mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
mScreenshotHandler.post(() -> {
- if (!mScreenshotView.isAttachedToWindow()) {
+ if (!mDecorView.isAttachedToWindow()) {
if (DEBUG_WINDOW) {
Log.d(TAG, "Adding screenshot window");
}
+ mDecorView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ mDecorView.removeOnAttachStateChangeListener(this);
+ maybeRequestScrollCapture();
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ }
+ });
mWindowManager.addView(mWindow.getDecorView(), mWindowLayoutParams);
+ } else {
+ maybeRequestScrollCapture();
}
mScreenshotView.prepareForAnimation(mScreenBitmap, screenInsets);
@@ -618,10 +628,10 @@
if (DEBUG_WINDOW) {
Log.d(TAG, "Removing screenshot window");
}
- mWindowManager.removeView(mDecorView);
+ mWindowManager.removeViewImmediate(mDecorView);
}
- mScreenshotView.reset();
mOnCompleteRunnable.run();
+ mScreenshotView.reset();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 03d17e5..19c0b6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -34,6 +34,7 @@
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
@@ -62,6 +63,8 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -195,6 +198,16 @@
private boolean mWakeLockHeld;
private boolean mKeyguardOccluded;
+ /**
+ * Notifies listeners of animation-related changes (currently just opacity changes).
+ */
+ public interface ScrimChangedListener {
+ void onAlphaChanged(float alpha);
+ }
+
+ @NonNull
+ private final List<ScrimChangedListener> mScrimChangedListeners;
+
@Inject
public ScrimController(LightBarController lightBarController, DozeParameters dozeParameters,
AlarmManager alarmManager, KeyguardStateController keyguardStateController,
@@ -208,6 +221,7 @@
ScrimState.BUBBLE_EXPANDED.setBubbleAlpha(featureFlags.isShadeOpaque()
? BUSY_SCRIM_ALPHA : GAR_SCRIM_ALPHA);
mBlurUtils = blurUtils;
+ mScrimChangedListeners = new ArrayList<>();
mKeyguardStateController = keyguardStateController;
mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen();
@@ -284,6 +298,10 @@
mScrimVisibleListener = listener;
}
+ public void addScrimChangedListener(@NonNull ScrimChangedListener listener) {
+ mScrimChangedListeners.add(listener);
+ }
+
public void transitionTo(ScrimState state) {
transitionTo(state, null);
}
@@ -559,6 +577,10 @@
throw new IllegalStateException("Scrim opacity is NaN for state: " + mState
+ ", front: " + mInFrontAlpha + ", back: " + mBehindAlpha);
}
+
+ for (ScrimChangedListener listener : mScrimChangedListeners) {
+ listener.onAlphaChanged(mBehindAlpha);
+ }
}
private void applyAndDispatchExpansion() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index d097cfa..499d1e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -316,6 +316,7 @@
@Override
public void onDarkChanged(Rect area, float darkIntensity, int tint) {
mNonAdaptedColor = DarkIconDispatcher.getTint(area, this, tint);
+ setTextColor(mNonAdaptedColor);
}
// Update text color based when shade scrim changes color.
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
index 8a79ace..416de04 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
@@ -17,13 +17,16 @@
package com.android.systemui.wmshell;
import android.content.Context;
+import android.os.Handler;
import com.android.systemui.dagger.WMSingleton;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
@@ -57,9 +60,10 @@
PipMediaController pipMediaController,
TvPipNotificationController tvPipNotificationController,
TaskStackListenerImpl taskStackListener,
- WindowManagerShellWrapper windowManagerShellWrapper) {
+ WindowManagerShellWrapper windowManagerShellWrapper,
+ @ShellMainThread ShellExecutor mainExecutor) {
return Optional.of(
- new TvPipController(
+ TvPipController.create(
context,
pipBoundsState,
pipBoundsAlgorithm,
@@ -68,7 +72,8 @@
pipMediaController,
tvPipNotificationController,
taskStackListener,
- windowManagerShellWrapper));
+ windowManagerShellWrapper,
+ mainExecutor));
}
@WMSingleton
@@ -84,21 +89,26 @@
return new PipBoundsState(context);
}
+ // Handler needed for loadDrawableAsync() in PipControlsViewController
@WMSingleton
@Provides
static TvPipMenuController providesTvPipMenuController(
Context context,
PipBoundsState pipBoundsState,
SystemWindows systemWindows,
- PipMediaController pipMediaController) {
- return new TvPipMenuController(context, pipBoundsState, systemWindows, pipMediaController);
+ PipMediaController pipMediaController,
+ @ShellMainThread Handler mainHandler) {
+ return new TvPipMenuController(context, pipBoundsState, systemWindows, pipMediaController,
+ mainHandler);
}
+ // Handler needed for registerReceiverForAllUsers()
@WMSingleton
@Provides
static TvPipNotificationController provideTvPipNotificationController(Context context,
- PipMediaController pipMediaController) {
- return new TvPipNotificationController(context, pipMediaController);
+ PipMediaController pipMediaController,
+ @ShellMainThread Handler mainHandler) {
+ return new TvPipNotificationController(context, pipMediaController, mainHandler);
}
@WMSingleton
@@ -109,9 +119,10 @@
PipBoundsAlgorithm pipBoundsAlgorithm,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<LegacySplitScreen> splitScreenOptional, DisplayController displayController,
- PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) {
+ PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
+ @ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm,
tvPipMenuController, pipSurfaceTransactionHelper, splitScreenOptional,
- displayController, pipUiEventLogger, shellTaskOrganizer);
+ displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index bbc238a..103e6bb 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -250,14 +250,17 @@
@Provides
static PipAppOpsListener providePipAppOpsListener(Context context,
IActivityManager activityManager,
- PipTouchHandler pipTouchHandler) {
- return new PipAppOpsListener(context, activityManager, pipTouchHandler.getMotionHelper());
+ PipTouchHandler pipTouchHandler,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return new PipAppOpsListener(context, pipTouchHandler.getMotionHelper(), mainExecutor);
}
+ // Needs handler for registering broadcast receivers
@WMSingleton
@Provides
- static PipMediaController providePipMediaController(Context context) {
- return new PipMediaController(context);
+ static PipMediaController providePipMediaController(Context context,
+ @ShellMainThread Handler mainHandler) {
+ return new PipMediaController(context, mainHandler);
}
@WMSingleton
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 8105250..12a3b5d 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -19,6 +19,7 @@
import android.animation.AnimationHandler;
import android.app.ActivityTaskManager;
import android.content.Context;
+import android.os.Handler;
import android.view.IWindowManager;
import com.android.systemui.dagger.WMSingleton;
@@ -125,11 +126,15 @@
return new PipBoundsAlgorithm(context, pipBoundsState);
}
+ // Handler is used by Icon.loadDrawableAsync
@WMSingleton
@Provides
static PhonePipMenuController providesPipPhoneMenuController(Context context,
- PipMediaController pipMediaController, SystemWindows systemWindows) {
- return new PhonePipMenuController(context, pipMediaController, systemWindows);
+ PipMediaController pipMediaController, SystemWindows systemWindows,
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread Handler mainHandler) {
+ return new PhonePipMenuController(context, pipMediaController, systemWindows,
+ mainExecutor, mainHandler);
}
@WMSingleton
@@ -154,9 +159,10 @@
PhonePipMenuController menuPhoneController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<LegacySplitScreen> splitScreenOptional, DisplayController displayController,
- PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) {
+ PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
+ @ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm,
menuPhoneController, pipSurfaceTransactionHelper, splitScreenOptional,
- displayController, pipUiEventLogger, shellTaskOrganizer);
+ displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index ed3cf9a..dd145e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -45,6 +45,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.time.FakeSystemClock;
@@ -90,6 +91,8 @@
private WindowManager mWindowManager;
@Mock
private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private ScrimController mScrimController;
private FakeSettings mSystemSettings;
private FakeExecutor mFgExecutor;
@@ -130,7 +133,8 @@
mWindowManager,
mSystemSettings,
mStatusBarStateController,
- mFgExecutor);
+ mFgExecutor,
+ mScrimController);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
@@ -245,4 +249,10 @@
// THEN the scrim and dot is hidden
verify(mUdfpsView).hideScrimAndDot();
}
+
+ @Test
+ public void registersViewForCallbacks() throws RemoteException {
+ verify(mStatusBarStateController).addCallback(mUdfpsView);
+ verify(mScrimController).addScrimChangedListener(mUdfpsView);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 8b86403..4078d4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -63,7 +63,6 @@
@Mock ScreenLifecycle mScreenLifecycle;
@Mock SysUiState mSysUiState;
@Mock Pip mPip;
- @Mock PipTouchHandler mPipTouchHandler;
@Mock LegacySplitScreen mLegacySplitScreen;
@Mock OneHanded mOneHanded;
@Mock HideDisplayCutout mHideDisplayCutout;
@@ -80,8 +79,6 @@
Optional.of(mShellCommandHandler), mCommandQueue, mConfigurationController,
mKeyguardUpdateMonitor, mNavigationModeController,
mScreenLifecycle, mSysUiState, mProtoTracer, mSysUiMainExecutor);
-
- when(mPip.getPipTouchHandler()).thenReturn(mPipTouchHandler);
}
@Test
diff --git a/services/OWNERS b/services/OWNERS
index f1fa542..03e0807 100644
--- a/services/OWNERS
+++ b/services/OWNERS
@@ -2,3 +2,5 @@
# art-team@ manages the system server profile
per-file art-profile* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
+
+per-file java/com/android/server/* = toddke@google.com
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 2f3ad19..6de227e 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -87,6 +87,7 @@
":storaged_aidl",
":vold_aidl",
":platform-compat-config",
+ ":platform-compat-overrides",
":display-device-config",
":cec-config",
":device-state-config",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index b6232a0..74a6e07 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -133,6 +133,7 @@
import android.net.UidRange;
import android.net.UidRangeParcel;
import android.net.Uri;
+import android.net.VpnInfo;
import android.net.VpnManager;
import android.net.VpnService;
import android.net.metrics.INetdEventListener;
@@ -184,7 +185,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
-import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.AsyncChannel;
@@ -325,6 +325,8 @@
private boolean mRestrictBackground;
private final Context mContext;
+ // The Context is created for UserHandle.ALL.
+ private final Context mUserAllContext;
private final Dependencies mDeps;
// 0 is full bad, 100 is full good
private int mDefaultInetConditionPublished = 0;
@@ -1160,8 +1162,8 @@
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
- final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
- userAllContext.registerReceiver(
+ mUserAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
+ mUserAllContext.registerReceiver(
mIntentReceiver,
intentFilter,
null /* broadcastPermission */,
@@ -1177,7 +1179,7 @@
intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
intentFilter.addDataScheme("package");
- userAllContext.registerReceiver(
+ mUserAllContext.registerReceiver(
mIntentReceiver,
intentFilter,
null /* broadcastPermission */,
@@ -1186,7 +1188,7 @@
// Listen to lockdown VPN reset.
intentFilter = new IntentFilter();
intentFilter.addAction(LockdownVpnTracker.ACTION_LOCKDOWN_RESET);
- userAllContext.registerReceiver(
+ mUserAllContext.registerReceiver(
mIntentReceiver, intentFilter, NETWORK_STACK, mHandler);
mNetworkActivityTracker = new LegacyNetworkActivityTracker(mContext, mNMS);
@@ -1456,9 +1458,8 @@
return;
}
final String action = blocked ? "BLOCKED" : "UNBLOCKED";
- final NetworkRequest satisfiedRequest = nri.getSatisfiedRequest();
- final int requestId = satisfiedRequest != null
- ? satisfiedRequest.requestId : nri.mRequests.get(0).requestId;
+ final int requestId = nri.getActiveRequest() != null
+ ? nri.getActiveRequest().requestId : nri.mRequests.get(0).requestId;
mNetworkInfoBlockingLogs.log(String.format(
"%s %d(%d) on netId %d", action, nri.mUid, requestId, net.getNetId()));
}
@@ -2350,7 +2351,7 @@
intent.addFlags(Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
}
try {
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL, options);
+ mUserAllContext.sendStickyBroadcast(intent, options);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2728,7 +2729,7 @@
@VisibleForTesting
NetworkRequestInfo[] requestsSortedById() {
NetworkRequestInfo[] requests = new NetworkRequestInfo[0];
- requests = mNetworkRequests.values().toArray(requests);
+ requests = getNrisFromGlobalRequests().toArray(requests);
// Sort the array based off the NRI containing the min requestId in its requests.
Arrays.sort(requests,
Comparator.comparingInt(nri -> Collections.min(nri.mRequests,
@@ -3433,10 +3434,10 @@
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest request = nai.requestAt(i);
final NetworkRequestInfo nri = mNetworkRequests.get(request);
- final NetworkAgentInfo currentNetwork = nri.mSatisfier;
+ final NetworkAgentInfo currentNetwork = nri.getSatisfier();
if (currentNetwork != null
&& currentNetwork.network.getNetId() == nai.network.getNetId()) {
- nri.mSatisfier = null;
+ nri.setSatisfier(null, null);
sendUpdatedScoreToFactories(request, null);
}
}
@@ -3514,42 +3515,63 @@
return null;
}
- private void handleRegisterNetworkRequestWithIntent(Message msg) {
+ private void handleRegisterNetworkRequestWithIntent(@NonNull final Message msg) {
final NetworkRequestInfo nri = (NetworkRequestInfo) (msg.obj);
-
- NetworkRequestInfo existingRequest = findExistingNetworkRequestInfo(nri.mPendingIntent);
+ // handleRegisterNetworkRequestWithIntent() doesn't apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "handleRegisterNetworkRequestWithIntent");
+ final NetworkRequestInfo existingRequest =
+ findExistingNetworkRequestInfo(nri.mPendingIntent);
if (existingRequest != null) { // remove the existing request.
- if (DBG) log("Replacing " + existingRequest.request + " with "
- + nri.request + " because their intents matched.");
- handleReleaseNetworkRequest(existingRequest.request, getCallingUid(),
+ if (DBG) {
+ log("Replacing " + existingRequest.mRequests.get(0) + " with "
+ + nri.mRequests.get(0) + " because their intents matched.");
+ }
+ handleReleaseNetworkRequest(existingRequest.mRequests.get(0), getCallingUid(),
/* callOnUnavailable */ false);
}
handleRegisterNetworkRequest(nri);
}
- private void handleRegisterNetworkRequest(NetworkRequestInfo nri) {
+ private void handleRegisterNetworkRequest(@NonNull final NetworkRequestInfo nri) {
ensureRunningOnConnectivityServiceThread();
- mNetworkRequests.put(nri.request, nri);
mNetworkRequestInfoLogs.log("REGISTER " + nri);
- if (nri.request.isListen()) {
- for (NetworkAgentInfo network : mNetworkAgentInfos) {
- if (nri.request.networkCapabilities.hasSignalStrength() &&
- network.satisfiesImmutableCapabilitiesOf(nri.request)) {
- updateSignalStrengthThresholds(network, "REGISTER", nri.request);
+ for (final NetworkRequest req : nri.mRequests) {
+ mNetworkRequests.put(req, nri);
+ if (req.isListen()) {
+ for (final NetworkAgentInfo network : mNetworkAgentInfos) {
+ if (req.networkCapabilities.hasSignalStrength()
+ && network.satisfiesImmutableCapabilitiesOf(req)) {
+ updateSignalStrengthThresholds(network, "REGISTER", req);
+ }
}
}
}
rematchAllNetworksAndRequests();
- if (nri.request.isRequest() && nri.mSatisfier == null) {
- sendUpdatedScoreToFactories(nri.request, null);
+ // If an active request exists, return as its score has already been sent if needed.
+ if (null != nri.getActiveRequest()) {
+ return;
+ }
+
+ // As this request was not satisfied on rematch and thus never had any scores sent to the
+ // factories, send null now for each request of type REQUEST.
+ for (final NetworkRequest req : nri.mRequests) {
+ if (!req.isRequest()) {
+ continue;
+ }
+ sendUpdatedScoreToFactories(req, null);
}
}
- private void handleReleaseNetworkRequestWithIntent(PendingIntent pendingIntent,
- int callingUid) {
- NetworkRequestInfo nri = findExistingNetworkRequestInfo(pendingIntent);
+ private void handleReleaseNetworkRequestWithIntent(@NonNull final PendingIntent pendingIntent,
+ final int callingUid) {
+ final NetworkRequestInfo nri = findExistingNetworkRequestInfo(pendingIntent);
if (nri != null) {
- handleReleaseNetworkRequest(nri.request, callingUid, /* callOnUnavailable */ false);
+ // handleReleaseNetworkRequestWithIntent() paths don't apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "handleReleaseNetworkRequestWithIntent");
+ handleReleaseNetworkRequest(
+ nri.mRequests.get(0),
+ callingUid,
+ /* callOnUnavailable */ false);
}
}
@@ -3603,6 +3625,11 @@
return false;
}
for (final NetworkRequest req : nri.mRequests) {
+ // This multilayer listen request is satisfied therefore no further requests need to be
+ // evaluated deeming this network not a potential satisfier.
+ if (req.isListen() && nri.getActiveRequest() == req) {
+ return false;
+ }
// As non-multilayer listen requests have already returned, the below would only happen
// for a multilayer request therefore continue to the next request if available.
if (req.isListen()) {
@@ -3623,7 +3650,7 @@
// 2. Unvalidated WiFi will not be reaped when validated cellular
// is currently satisfying the request. This is desirable when
// WiFi ends up validating and out scoring cellular.
- || nri.mSatisfier.getCurrentScore()
+ || nri.getSatisfier().getCurrentScore()
< candidate.getCurrentScoreAsValidated();
return isNetworkNeeded;
}
@@ -3648,30 +3675,45 @@
return nri;
}
- private void handleTimedOutNetworkRequest(final NetworkRequestInfo nri) {
- ensureRunningOnConnectivityServiceThread();
- if (mNetworkRequests.get(nri.request) == null) {
- return;
+ private void ensureNotMultilayerRequest(@NonNull final NetworkRequestInfo nri,
+ final String callingMethod) {
+ if (nri.isMultilayerRequest()) {
+ throw new IllegalStateException(
+ callingMethod + " does not support multilayer requests.");
}
- if (nri.mSatisfier != null) {
- return;
- }
- if (VDBG || (DBG && nri.request.isRequest())) {
- log("releasing " + nri.request + " (timeout)");
- }
- handleRemoveNetworkRequest(nri);
- callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_UNAVAIL, 0);
}
- private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid,
- boolean callOnUnavailable) {
+ private void handleTimedOutNetworkRequest(@NonNull final NetworkRequestInfo nri) {
+ ensureRunningOnConnectivityServiceThread();
+ // handleTimedOutNetworkRequest() is part of the requestNetwork() flow which works off of a
+ // single NetworkRequest and thus does not apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "handleTimedOutNetworkRequest");
+ if (mNetworkRequests.get(nri.mRequests.get(0)) == null) {
+ return;
+ }
+ if (nri.getSatisfier() != null) {
+ return;
+ }
+ if (VDBG || (DBG && nri.mRequests.get(0).isRequest())) {
+ log("releasing " + nri.mRequests.get(0) + " (timeout)");
+ }
+ handleRemoveNetworkRequest(nri);
+ callCallbackForRequest(
+ nri, null, ConnectivityManager.CALLBACK_UNAVAIL, 0);
+ }
+
+ private void handleReleaseNetworkRequest(@NonNull final NetworkRequest request,
+ final int callingUid,
+ final boolean callOnUnavailable) {
final NetworkRequestInfo nri =
getNriForAppRequest(request, callingUid, "release NetworkRequest");
if (nri == null) {
return;
}
- if (VDBG || (DBG && nri.request.isRequest())) {
- log("releasing " + nri.request + " (release request)");
+ // handleReleaseNetworkRequest() paths don't apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "handleReleaseNetworkRequest");
+ if (VDBG || (DBG && request.isRequest())) {
+ log("releasing " + request + " (release request)");
}
handleRemoveNetworkRequest(nri);
if (callOnUnavailable) {
@@ -3679,42 +3721,88 @@
}
}
- private void handleRemoveNetworkRequest(final NetworkRequestInfo nri) {
+ private void handleRemoveNetworkRequest(@NonNull final NetworkRequestInfo nri) {
ensureRunningOnConnectivityServiceThread();
nri.unlinkDeathRecipient();
- mNetworkRequests.remove(nri.request);
-
+ for (final NetworkRequest req : nri.mRequests) {
+ mNetworkRequests.remove(req);
+ if (req.isListen()) {
+ removeListenRequestFromNetworks(req);
+ }
+ }
mNetworkRequestCounter.decrementCount(nri.mUid);
-
mNetworkRequestInfoLogs.log("RELEASE " + nri);
- if (nri.request.isRequest()) {
- boolean wasKept = false;
- final NetworkAgentInfo nai = nri.mSatisfier;
- if (nai != null) {
- boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
- nai.removeRequest(nri.request.requestId);
- if (VDBG || DDBG) {
- log(" Removing from current network " + nai.toShortString()
- + ", leaving " + nai.numNetworkRequests() + " requests.");
- }
- // If there are still lingered requests on this network, don't tear it down,
- // but resume lingering instead.
- final long now = SystemClock.elapsedRealtime();
- if (updateLingerState(nai, now)) {
- notifyNetworkLosing(nai, now);
- }
- if (unneeded(nai, UnneededFor.TEARDOWN)) {
- if (DBG) log("no live requests for " + nai.toShortString() + "; disconnecting");
- teardownUnneededNetwork(nai);
- } else {
- wasKept = true;
- }
- nri.mSatisfier = null;
- if (!wasBackgroundNetwork && nai.isBackgroundNetwork()) {
- // Went from foreground to background.
- updateCapabilitiesForNetwork(nai);
- }
+
+ if (null != nri.getActiveRequest()) {
+ if (nri.getActiveRequest().isRequest()) {
+ removeSatisfiedNetworkRequestFromNetwork(nri);
+ } else {
+ nri.setSatisfier(null, null);
+ }
+ }
+
+ cancelNpiRequests(nri);
+ }
+
+ private void cancelNpiRequests(@NonNull final NetworkRequestInfo nri) {
+ for (final NetworkRequest req : nri.mRequests) {
+ cancelNpiRequest(req);
+ }
+ }
+
+ private void cancelNpiRequest(@NonNull final NetworkRequest req) {
+ if (req.isRequest()) {
+ for (final NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
+ npi.cancelRequest(req);
+ }
+ }
+ }
+
+ private void removeListenRequestFromNetworks(@NonNull final NetworkRequest req) {
+ // listens don't have a singular affected Network. Check all networks to see
+ // if this listen request applies and remove it.
+ for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
+ nai.removeRequest(req.requestId);
+ if (req.networkCapabilities.hasSignalStrength()
+ && nai.satisfiesImmutableCapabilitiesOf(req)) {
+ updateSignalStrengthThresholds(nai, "RELEASE", req);
+ }
+ }
+ }
+
+ /**
+ * Remove a NetworkRequestInfo's satisfied request from its 'satisfier' (NetworkAgentInfo) and
+ * manage the necessary upkeep (linger, teardown networks, etc.) when doing so.
+ * @param nri the NetworkRequestInfo to disassociate from its current NetworkAgentInfo
+ */
+ private void removeSatisfiedNetworkRequestFromNetwork(@NonNull final NetworkRequestInfo nri) {
+ boolean wasKept = false;
+ final NetworkAgentInfo nai = nri.getSatisfier();
+ if (nai != null) {
+ final int requestLegacyType = nri.getActiveRequest().legacyType;
+ final boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
+ nai.removeRequest(nri.getActiveRequest().requestId);
+ if (VDBG || DDBG) {
+ log(" Removing from current network " + nai.toShortString()
+ + ", leaving " + nai.numNetworkRequests() + " requests.");
+ }
+ // If there are still lingered requests on this network, don't tear it down,
+ // but resume lingering instead.
+ final long now = SystemClock.elapsedRealtime();
+ if (updateLingerState(nai, now)) {
+ notifyNetworkLosing(nai, now);
+ }
+ if (unneeded(nai, UnneededFor.TEARDOWN)) {
+ if (DBG) log("no live requests for " + nai.toShortString() + "; disconnecting");
+ teardownUnneededNetwork(nai);
+ } else {
+ wasKept = true;
+ }
+ nri.setSatisfier(null, null);
+ if (!wasBackgroundNetwork && nai.isBackgroundNetwork()) {
+ // Went from foreground to background.
+ updateCapabilitiesForNetwork(nai);
}
// Maintain the illusion. When this request arrived, we might have pretended
@@ -3722,15 +3810,15 @@
// connected. Now that this request has gone away, we might have to pretend
// that the network disconnected. LegacyTypeTracker will generate that
// phantom disconnect for this type.
- if (nri.request.legacyType != TYPE_NONE && nai != null) {
+ if (requestLegacyType != TYPE_NONE) {
boolean doRemove = true;
if (wasKept) {
// check if any of the remaining requests for this network are for the
// same legacy type - if so, don't remove the nai
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest otherRequest = nai.requestAt(i);
- if (otherRequest.legacyType == nri.request.legacyType &&
- otherRequest.isRequest()) {
+ if (otherRequest.legacyType == requestLegacyType
+ && otherRequest.isRequest()) {
if (DBG) log(" still have other legacy request - leaving");
doRemove = false;
}
@@ -3738,21 +3826,7 @@
}
if (doRemove) {
- mLegacyTypeTracker.remove(nri.request.legacyType, nai, false);
- }
- }
-
- for (NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
- npi.cancelRequest(nri.request);
- }
- } else {
- // listens don't have a singular affectedNetwork. Check all networks to see
- // if this listen request applies and remove it.
- for (NetworkAgentInfo nai : mNetworkAgentInfos) {
- nai.removeRequest(nri.request.requestId);
- if (nri.request.networkCapabilities.hasSignalStrength() &&
- nai.satisfiesImmutableCapabilitiesOf(nri.request)) {
- updateSignalStrengthThresholds(nai, "RELEASE", nri.request);
+ mLegacyTypeTracker.remove(requestLegacyType, nai, false);
}
}
}
@@ -4830,16 +4904,14 @@
if (interfaces.isEmpty()) return null;
- VpnInfo info = new VpnInfo();
- info.ownerUid = nai.networkCapabilities.getOwnerUid();
- info.vpnIface = nai.linkProperties.getInterfaceName();
// Must be non-null or NetworkStatsService will crash.
// Cannot happen in production code because Vpn only registers the NetworkAgent after the
// tun or ipsec interface is created.
- if (info.vpnIface == null) return null;
- info.underlyingIfaces = interfaces.toArray(new String[0]);
+ if (nai.linkProperties.getInterfaceName() == null) return null;
- return info;
+ return new VpnInfo(nai.networkCapabilities.getOwnerUid(),
+ nai.linkProperties.getInterfaceName(),
+ interfaces.toArray(new String[0]));
}
/**
@@ -5415,18 +5487,38 @@
/**
* Tracks info about the requester.
- * Also used to notice when the calling process dies so we can self-expire
+ * Also used to notice when the calling process dies so as to self-expire
*/
@VisibleForTesting
protected class NetworkRequestInfo implements IBinder.DeathRecipient {
final List<NetworkRequest> mRequests;
- final NetworkRequest request;
+
+ // mSatisfier and mActiveRequest rely on one another therefore set them together.
+ void setSatisfier(
+ @Nullable final NetworkAgentInfo satisfier,
+ @Nullable final NetworkRequest activeRequest) {
+ mSatisfier = satisfier;
+ mActiveRequest = activeRequest;
+ }
// The network currently satisfying this request, or null if none. Must only be touched
// on the handler thread. This only makes sense for network requests and not for listens,
// as defined by NetworkRequest#isRequest(). For listens, this is always null.
@Nullable
- NetworkAgentInfo mSatisfier;
+ private NetworkAgentInfo mSatisfier;
+ NetworkAgentInfo getSatisfier() {
+ return mSatisfier;
+ }
+
+ // The request in mRequests assigned to a network agent. This is null if none of the
+ // requests in mRequests can be satisfied. This member has the constraint of only being
+ // accessible on the handler thread.
+ @Nullable
+ private NetworkRequest mActiveRequest;
+ NetworkRequest getActiveRequest() {
+ return mActiveRequest;
+ }
+
final PendingIntent mPendingIntent;
boolean mPendingIntentSent;
private final IBinder mBinder;
@@ -5435,7 +5527,6 @@
final Messenger messenger;
NetworkRequestInfo(NetworkRequest r, PendingIntent pi) {
- request = r;
mRequests = initializeRequests(r);
ensureAllNetworkRequestsHaveType(mRequests);
mPendingIntent = pi;
@@ -5449,7 +5540,6 @@
NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder) {
super();
messenger = m;
- request = r;
mRequests = initializeRequests(r);
ensureAllNetworkRequestsHaveType(mRequests);
mBinder = binder;
@@ -5479,20 +5569,6 @@
return Collections.unmodifiableList(tempRequests);
}
- private NetworkRequest getSatisfiedRequest() {
- if (mSatisfier == null) {
- return null;
- }
-
- for (NetworkRequest req : mRequests) {
- if (mSatisfier.isSatisfyingRequest(req.requestId)) {
- return req;
- }
- }
-
- return null;
- }
-
void unlinkDeathRecipient() {
if (mBinder != null) {
mBinder.unlinkToDeath(this, 0);
@@ -5539,6 +5615,10 @@
private int[] getSignalStrengthThresholds(@NonNull final NetworkAgentInfo nai) {
final SortedSet<Integer> thresholds = new TreeSet<>();
synchronized (nai) {
+ // mNetworkRequests may contain the same value multiple times in case of
+ // multilayer requests. It won't matter in this case because the thresholds
+ // will then be the same and be deduplicated as they enter the `thresholds` set.
+ // TODO : have mNetworkRequests be a Set<NetworkRequestInfo> or the like.
for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
for (final NetworkRequest req : nri.mRequests) {
if (req.networkCapabilities.hasSignalStrength()
@@ -5914,13 +5994,19 @@
}
@Override
- public void declareNetworkRequestUnfulfillable(NetworkRequest request) {
+ public void declareNetworkRequestUnfulfillable(@NonNull final NetworkRequest request) {
if (request.hasTransport(TRANSPORT_TEST)) {
enforceNetworkFactoryOrTestNetworksPermission();
} else {
enforceNetworkFactoryPermission();
}
- mHandler.post(() -> handleReleaseNetworkRequest(request, mDeps.getCallingUid(), true));
+ final NetworkRequestInfo nri = mNetworkRequests.get(request);
+ if (nri != null) {
+ // declareNetworkRequestUnfulfillable() paths don't apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "declareNetworkRequestUnfulfillable");
+ mHandler.post(() -> handleReleaseNetworkRequest(
+ nri.mRequests.get(0), mDeps.getCallingUid(), true));
+ }
}
// NOTE: Accessed on multiple threads, must be synchronized on itself.
@@ -6845,6 +6931,39 @@
}
}
+ private void sendUpdatedScoreToFactories(
+ @NonNull final NetworkReassignment.RequestReassignment event) {
+ // If a request of type REQUEST is now being satisfied by a new network.
+ if (null != event.mNewNetworkRequest && event.mNewNetworkRequest.isRequest()) {
+ sendUpdatedScoreToFactories(event.mNewNetworkRequest, event.mNewNetwork);
+ }
+
+ // If a previously satisfied request of type REQUEST is no longer being satisfied.
+ if (null != event.mOldNetworkRequest && event.mOldNetworkRequest.isRequest()
+ && event.mOldNetworkRequest != event.mNewNetworkRequest) {
+ sendUpdatedScoreToFactories(event.mOldNetworkRequest, null);
+ }
+
+ cancelMultilayerLowerPriorityNpiRequests(event.mNetworkRequestInfo);
+ }
+
+ /**
+ * Cancel with all NPIs the given NRI's multilayer requests that are a lower priority than
+ * its currently satisfied active request.
+ * @param nri the NRI to cancel lower priority requests for.
+ */
+ private void cancelMultilayerLowerPriorityNpiRequests(
+ @NonNull final NetworkRequestInfo nri) {
+ if (!nri.isMultilayerRequest() || null == nri.mActiveRequest) {
+ return;
+ }
+
+ final int indexOfNewRequest = nri.mRequests.indexOf(nri.mActiveRequest);
+ for (int i = indexOfNewRequest + 1; i < nri.mRequests.size(); i++) {
+ cancelNpiRequest(nri.mRequests.get(i));
+ }
+ }
+
private void sendUpdatedScoreToFactories(@NonNull NetworkRequest networkRequest,
@Nullable NetworkAgentInfo nai) {
final int score;
@@ -6865,21 +6984,35 @@
}
/** Sends all current NetworkRequests to the specified factory. */
- private void sendAllRequestsToProvider(NetworkProviderInfo npi) {
+ private void sendAllRequestsToProvider(@NonNull final NetworkProviderInfo npi) {
ensureRunningOnConnectivityServiceThread();
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- if (nri.request.isListen()) continue;
- NetworkAgentInfo nai = nri.mSatisfier;
- final int score;
- final int serial;
- if (nai != null) {
- score = nai.getCurrentScore();
- serial = nai.factorySerialNumber;
- } else {
- score = 0;
- serial = NetworkProvider.ID_NONE;
+ for (final NetworkRequestInfo nri : getNrisFromGlobalRequests()) {
+ for (final NetworkRequest req : nri.mRequests) {
+ if (req.isListen() && nri.getActiveRequest() == req) {
+ break;
+ }
+ if (req.isListen()) {
+ continue;
+ }
+ // Only set the nai for the request it is satisfying.
+ final NetworkAgentInfo nai =
+ nri.getActiveRequest() == req ? nri.getSatisfier() : null;
+ final int score;
+ final int serial;
+ if (null != nai) {
+ score = nai.getCurrentScore();
+ serial = nai.factorySerialNumber;
+ } else {
+ score = 0;
+ serial = NetworkProvider.ID_NONE;
+ }
+ npi.requestNetwork(req, score, serial);
+ // For multilayer requests, don't send lower priority requests if a higher priority
+ // request is already satisfied.
+ if (null != nai) {
+ break;
+ }
}
- npi.requestNetwork(nri.request, score, serial);
}
}
@@ -6888,7 +7021,12 @@
if (notificationType == ConnectivityManager.CALLBACK_AVAILABLE && !nri.mPendingIntentSent) {
Intent intent = new Intent();
intent.putExtra(ConnectivityManager.EXTRA_NETWORK, networkAgent.network);
- intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST, nri.request);
+ // If apps could file multi-layer requests with PendingIntents, they'd need to know
+ // which of the layer is satisfied alongside with some ID for the request. Hence, if
+ // such an API is ever implemented, there is no doubt the right request to send in
+ // EXTRA_NETWORK_REQUEST is mActiveRequest, and whatever ID would be added would need to
+ // be sent as a separate extra.
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST, nri.getActiveRequest());
nri.mPendingIntentSent = true;
sendIntent(nri.mPendingIntent, intent);
}
@@ -6918,8 +7056,9 @@
releasePendingNetworkRequestWithDelay(pendingIntent);
}
- private void callCallbackForRequest(NetworkRequestInfo nri,
- NetworkAgentInfo networkAgent, int notificationType, int arg1) {
+ private void callCallbackForRequest(@NonNull final NetworkRequestInfo nri,
+ @NonNull final NetworkAgentInfo networkAgent, final int notificationType,
+ final int arg1) {
if (nri.messenger == null) {
// Default request has no msgr. Also prevents callbacks from being invoked for
// NetworkRequestInfos registered with ConnectivityDiagnostics requests. Those callbacks
@@ -6927,8 +7066,14 @@
return;
}
Bundle bundle = new Bundle();
+ // In the case of multi-layer NRIs, the first request is not necessarily the one that
+ // is satisfied. This is vexing, but the ConnectivityManager code that receives this
+ // callback is only using the request as a token to identify the callback, so it doesn't
+ // matter too much at this point as long as the callback can be found.
+ // TODO b/177608132: make sure callbacks are indexed by NRIs and not NetworkRequest objects.
// TODO: check if defensive copies of data is needed.
- putParcelable(bundle, new NetworkRequest(nri.request));
+ final NetworkRequest nrForCallback = new NetworkRequest(nri.mRequests.get(0));
+ putParcelable(bundle, nrForCallback);
Message msg = Message.obtain();
if (notificationType != ConnectivityManager.CALLBACK_UNAVAIL) {
putParcelable(bundle, networkAgent.network);
@@ -6941,7 +7086,7 @@
putParcelable(
bundle,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- nc, nri.mUid, nri.request.getRequestorPackageName()));
+ nc, nri.mUid, nrForCallback.getRequestorPackageName()));
putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
networkAgent.linkProperties, nri.mPid, nri.mUid));
// For this notification, arg1 contains the blocked status.
@@ -6960,7 +7105,7 @@
putParcelable(
bundle,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- netCap, nri.mUid, nri.request.getRequestorPackageName()));
+ netCap, nri.mUid, nrForCallback.getRequestorPackageName()));
break;
}
case ConnectivityManager.CALLBACK_IP_CHANGED: {
@@ -6979,12 +7124,12 @@
try {
if (VDBG) {
String notification = ConnectivityManager.getCallbackName(notificationType);
- log("sending notification " + notification + " for " + nri.request);
+ log("sending notification " + notification + " for " + nrForCallback);
}
nri.messenger.send(msg);
} catch (RemoteException e) {
// may occur naturally in the race of binder death.
- loge("RemoteException caught trying to send a callback msg for " + nri.request);
+ loge("RemoteException caught trying to send a callback msg for " + nrForCallback);
}
}
@@ -7060,19 +7205,25 @@
}
private void processNewlyLostListenRequests(@NonNull final NetworkAgentInfo nai) {
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- NetworkRequest nr = nri.request;
+ for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
+ if (nri.isMultilayerRequest()) {
+ continue;
+ }
+ final NetworkRequest nr = nri.mRequests.get(0);
if (!nr.isListen()) continue;
if (nai.isSatisfyingRequest(nr.requestId) && !nai.satisfies(nr)) {
- nai.removeRequest(nri.request.requestId);
+ nai.removeRequest(nr.requestId);
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_LOST, 0);
}
}
}
private void processNewlySatisfiedListenRequests(@NonNull final NetworkAgentInfo nai) {
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- NetworkRequest nr = nri.request;
+ for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
+ if (nri.isMultilayerRequest()) {
+ continue;
+ }
+ final NetworkRequest nr = nri.mRequests.get(0);
if (!nr.isListen()) continue;
if (nai.satisfies(nr) && !nai.isSatisfyingRequest(nr.requestId)) {
nai.addRequest(nr);
@@ -7084,19 +7235,25 @@
// An accumulator class to gather the list of changes that result from a rematch.
private static class NetworkReassignment {
static class RequestReassignment {
- @NonNull public final NetworkRequestInfo mRequest;
+ @NonNull public final NetworkRequestInfo mNetworkRequestInfo;
+ @NonNull public final NetworkRequest mOldNetworkRequest;
+ @NonNull public final NetworkRequest mNewNetworkRequest;
@Nullable public final NetworkAgentInfo mOldNetwork;
@Nullable public final NetworkAgentInfo mNewNetwork;
- RequestReassignment(@NonNull final NetworkRequestInfo request,
+ RequestReassignment(@NonNull final NetworkRequestInfo networkRequestInfo,
+ @NonNull final NetworkRequest oldNetworkRequest,
+ @NonNull final NetworkRequest newNetworkRequest,
@Nullable final NetworkAgentInfo oldNetwork,
@Nullable final NetworkAgentInfo newNetwork) {
- mRequest = request;
+ mNetworkRequestInfo = networkRequestInfo;
+ mOldNetworkRequest = oldNetworkRequest;
+ mNewNetworkRequest = newNetworkRequest;
mOldNetwork = oldNetwork;
mNewNetwork = newNetwork;
}
public String toString() {
- return mRequest.mRequests.get(0).requestId + " : "
+ return mNetworkRequestInfo.mRequests.get(0).requestId + " : "
+ (null != mOldNetwork ? mOldNetwork.network.getNetId() : "null")
+ " → " + (null != mNewNetwork ? mNewNetwork.network.getNetId() : "null");
}
@@ -7114,7 +7271,7 @@
// sure this stays true, but without imposing this expensive check on all
// reassignments on all user devices.
for (final RequestReassignment existing : mReassignments) {
- if (existing.mRequest.equals(reassignment.mRequest)) {
+ if (existing.mNetworkRequestInfo.equals(reassignment.mNetworkRequestInfo)) {
throw new IllegalStateException("Trying to reassign ["
+ reassignment + "] but already have ["
+ existing + "]");
@@ -7129,7 +7286,7 @@
@Nullable
private RequestReassignment getReassignment(@NonNull final NetworkRequestInfo nri) {
for (final RequestReassignment event : getRequestReassignments()) {
- if (nri == event.mRequest) return event;
+ if (nri == event.mNetworkRequestInfo) return event;
}
return null;
}
@@ -7156,6 +7313,8 @@
}
private void updateSatisfiersForRematchRequest(@NonNull final NetworkRequestInfo nri,
+ @NonNull final NetworkRequest previousRequest,
+ @NonNull final NetworkRequest newRequest,
@Nullable final NetworkAgentInfo previousSatisfier,
@Nullable final NetworkAgentInfo newSatisfier,
final long now) {
@@ -7165,58 +7324,98 @@
if (VDBG || DDBG) {
log(" accepting network in place of " + previousSatisfier.toShortString());
}
- previousSatisfier.removeRequest(nri.request.requestId);
- previousSatisfier.lingerRequest(nri.request.requestId, now, mLingerDelayMs);
+ previousSatisfier.removeRequest(previousRequest.requestId);
+ previousSatisfier.lingerRequest(previousRequest.requestId, now, mLingerDelayMs);
} else {
if (VDBG || DDBG) log(" accepting network in place of null");
}
- newSatisfier.unlingerRequest(nri.request.requestId);
- if (!newSatisfier.addRequest(nri.request)) {
+ newSatisfier.unlingerRequest(newRequest.requestId);
+ if (!newSatisfier.addRequest(newRequest)) {
Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
- + nri.request);
+ + newRequest);
}
} else {
if (DBG) {
log("Network " + previousSatisfier.toShortString() + " stopped satisfying"
- + " request " + nri.request.requestId);
+ + " request " + previousRequest.requestId);
}
- previousSatisfier.removeRequest(nri.request.requestId);
+ previousSatisfier.removeRequest(previousRequest.requestId);
}
- nri.mSatisfier = newSatisfier;
+ nri.setSatisfier(newSatisfier, newRequest);
}
+ /**
+ * This function is triggered when something can affect what network should satisfy what
+ * request, and it computes the network reassignment from the passed collection of requests to
+ * network match to the one that the system should now have. That data is encoded in an
+ * object that is a list of changes, each of them having an NRI, and old satisfier, and a new
+ * satisfier.
+ *
+ * After the reassignment is computed, it is applied to the state objects.
+ *
+ * @param networkRequests the nri objects to evaluate for possible network reassignment
+ * @return NetworkReassignment listing of proposed network assignment changes
+ */
@NonNull
- private NetworkReassignment computeNetworkReassignment() {
- ensureRunningOnConnectivityServiceThread();
+ private NetworkReassignment computeNetworkReassignment(
+ @NonNull final Collection<NetworkRequestInfo> networkRequests) {
final NetworkReassignment changes = new NetworkReassignment();
// Gather the list of all relevant agents and sort them by score.
final ArrayList<NetworkAgentInfo> nais = new ArrayList<>();
for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
- if (!nai.everConnected) continue;
+ if (!nai.everConnected) {
+ continue;
+ }
nais.add(nai);
}
- for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
- if (nri.request.isListen()) continue;
- final NetworkAgentInfo bestNetwork = mNetworkRanker.getBestNetwork(nri.request, nais);
+ for (final NetworkRequestInfo nri : networkRequests) {
+ // Non-multilayer listen requests can be ignored.
+ if (!nri.isMultilayerRequest() && nri.mRequests.get(0).isListen()) {
+ continue;
+ }
+ NetworkAgentInfo bestNetwork = null;
+ NetworkRequest bestRequest = null;
+ for (final NetworkRequest req : nri.mRequests) {
+ bestNetwork = mNetworkRanker.getBestNetwork(req, nais);
+ // Stop evaluating as the highest possible priority request is satisfied.
+ if (null != bestNetwork) {
+ bestRequest = req;
+ break;
+ }
+ }
if (bestNetwork != nri.mSatisfier) {
// bestNetwork may be null if no network can satisfy this request.
changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
- nri, nri.mSatisfier, bestNetwork));
+ nri, nri.mActiveRequest, bestRequest, nri.getSatisfier(), bestNetwork));
}
}
return changes;
}
+ private Set<NetworkRequestInfo> getNrisFromGlobalRequests() {
+ return new HashSet<>(mNetworkRequests.values());
+ }
+
/**
- * Attempt to rematch all Networks with NetworkRequests. This may result in Networks
+ * Attempt to rematch all Networks with all NetworkRequests. This may result in Networks
* being disconnected.
*/
private void rematchAllNetworksAndRequests() {
+ rematchNetworksAndRequests(getNrisFromGlobalRequests());
+ }
+
+ /**
+ * Attempt to rematch all Networks with given NetworkRequests. This may result in Networks
+ * being disconnected.
+ */
+ private void rematchNetworksAndRequests(
+ @NonNull final Set<NetworkRequestInfo> networkRequests) {
+ ensureRunningOnConnectivityServiceThread();
// TODO: This may be slow, and should be optimized.
final long now = SystemClock.elapsedRealtime();
- final NetworkReassignment changes = computeNetworkReassignment();
+ final NetworkReassignment changes = computeNetworkReassignment(networkRequests);
if (VDBG || DDBG) {
log(changes.debugString());
} else if (DBG) {
@@ -7241,8 +7440,10 @@
// the linger status.
for (final NetworkReassignment.RequestReassignment event :
changes.getRequestReassignments()) {
- updateSatisfiersForRematchRequest(event.mRequest, event.mOldNetwork,
- event.mNewNetwork, now);
+ updateSatisfiersForRematchRequest(event.mNetworkRequestInfo,
+ event.mOldNetworkRequest, event.mNewNetworkRequest,
+ event.mOldNetwork, event.mNewNetwork,
+ now);
}
final NetworkAgentInfo oldDefaultNetwork = getDefaultNetwork();
@@ -7294,12 +7495,12 @@
// trying to connect if they know they cannot match it.
// TODO - this could get expensive if there are a lot of outstanding requests for this
// network. Think of a way to reduce this. Push netid->request mapping to each factory?
- sendUpdatedScoreToFactories(event.mRequest.request, event.mNewNetwork);
+ sendUpdatedScoreToFactories(event);
if (null != event.mNewNetwork) {
- notifyNetworkAvailable(event.mNewNetwork, event.mRequest);
+ notifyNetworkAvailable(event.mNewNetwork, event.mNetworkRequestInfo);
} else {
- callCallbackForRequest(event.mRequest, event.mOldNetwork,
+ callCallbackForRequest(event.mNetworkRequestInfo, event.mOldNetwork,
ConnectivityManager.CALLBACK_LOST, 0);
}
}
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index f2b63a6..88ce220 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -22,7 +22,6 @@
import android.gsi.GsiProgress;
import android.gsi.IGsiService;
import android.gsi.IGsiServiceCallback;
-import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -30,7 +29,7 @@
import android.os.UserHandle;
import android.os.image.IDynamicSystemService;
import android.os.storage.StorageManager;
-import android.os.storage.StorageVolume;
+import android.os.storage.VolumeInfo;
import android.util.Slog;
import java.io.File;
@@ -88,16 +87,17 @@
String path = SystemProperties.get("os.aot.path");
if (path.isEmpty()) {
final int userId = UserHandle.myUserId();
- final StorageVolume[] volumes =
- StorageManager.getVolumeList(userId, StorageManager.FLAG_FOR_WRITE);
- for (StorageVolume volume : volumes) {
- if (volume.isEmulated()) continue;
- if (!volume.isRemovable()) continue;
- if (!Environment.MEDIA_MOUNTED.equals(volume.getState())) continue;
- File sdCard = volume.getPathFile();
- if (sdCard.isDirectory()) {
- path = new File(sdCard, dsuSlot).getPath();
- break;
+ final StorageManager sm = mContext.getSystemService(StorageManager.class);
+ for (VolumeInfo volume : sm.getVolumes()) {
+ if (volume.getType() != volume.TYPE_PUBLIC) {
+ continue;
+ }
+ if (!volume.isMountedWritable()) {
+ continue;
+ }
+ File sd_internal = volume.getInternalPathForUser(userId);
+ if (sd_internal != null) {
+ path = new File(sd_internal, dsuSlot).getPath();
}
}
if (path.isEmpty()) {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index cd6a9fb..0412f08 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3136,6 +3136,12 @@
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
if (isFsEncrypted) {
+ // When a user has secure lock screen, require secret to actually unlock.
+ // This check is mostly in place for emulation mode.
+ if (StorageManager.isFileEncryptedEmulatedOnly() &&
+ mLockPatternUtils.isSecure(userId) && ArrayUtils.isEmpty(secret)) {
+ throw new IllegalStateException("Secret required to unlock secure user " + userId);
+ }
try {
mVold.unlockUserKey(userId, serialNumber, encodeBytes(token),
encodeBytes(secret));
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index ba1eda9..efe82df 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -715,7 +715,7 @@
WatchdogDiagnostics.diagnoseCheckers(blockedCheckers);
Slog.w(TAG, "*** GOODBYE!");
if (!Build.IS_USER && isCrashLoopFound()
- && !WatchdogProperties.is_fatal_ignore().orElse(false)) {
+ && !WatchdogProperties.should_ignore_fatal_count().orElse(false)) {
breakCrashLoop();
}
Process.killProcess(Process.myPid());
@@ -794,7 +794,7 @@
private boolean isCrashLoopFound() {
int fatalCount = WatchdogProperties.fatal_count().orElse(0);
long fatalWindowMs = TimeUnit.SECONDS.toMillis(
- WatchdogProperties.fatal_window_second().orElse(0));
+ WatchdogProperties.fatal_window_seconds().orElse(0));
if (fatalCount == 0 || fatalWindowMs == 0) {
if (fatalCount != fatalWindowMs) {
Slog.w(TAG, String.format("sysprops '%s' and '%s' should be set or unset together",
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
new file mode 100644
index 0000000..508bb01
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -0,0 +1,349 @@
+/*
+ * 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.apphibernation;
+
+import static android.content.Intent.ACTION_PACKAGE_ADDED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+import static android.content.Intent.ACTION_USER_ADDED;
+import static android.content.Intent.ACTION_USER_REMOVED;
+import static android.content.Intent.EXTRA_REPLACING;
+import static android.content.pm.PackageManager.MATCH_ALL;
+import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.IActivityManager;
+import android.apphibernation.IAppHibernationService;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.UserInfo;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ServiceManager;
+import android.os.ShellCallback;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.DeviceConfig;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.SystemService;
+
+import java.io.FileDescriptor;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * System service that manages app hibernation state, a state apps can enter that means they are
+ * not being actively used and can be optimized for storage. The actual policy for determining
+ * if an app should hibernate is managed by PermissionController code.
+ */
+public final class AppHibernationService extends SystemService {
+ private static final String TAG = "AppHibernationService";
+
+ /**
+ * Lock for accessing any in-memory hibernation state
+ */
+ private final Object mLock = new Object();
+ private final Context mContext;
+ private final IPackageManager mIPackageManager;
+ private final IActivityManager mIActivityManager;
+ private final UserManager mUserManager;
+ @GuardedBy("mLock")
+ private final SparseArray<Map<String, UserPackageState>> mUserStates = new SparseArray<>();
+
+ /**
+ * Initializes the system service.
+ * <p>
+ * Subclasses must define a single argument constructor that accepts the context
+ * and passes it to super.
+ * </p>
+ *
+ * @param context The system server context.
+ */
+ public AppHibernationService(@NonNull Context context) {
+ this(context, IPackageManager.Stub.asInterface(ServiceManager.getService("package")),
+ ActivityManager.getService(),
+ context.getSystemService(UserManager.class));
+ }
+
+ @VisibleForTesting
+ AppHibernationService(@NonNull Context context, IPackageManager packageManager,
+ IActivityManager activityManager, UserManager userManager) {
+ super(context);
+ mContext = context;
+ mIPackageManager = packageManager;
+ mIActivityManager = activityManager;
+ mUserManager = userManager;
+
+ final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ACTION_USER_ADDED);
+ intentFilter.addAction(ACTION_USER_REMOVED);
+ userAllContext.registerReceiver(mBroadcastReceiver, intentFilter);
+
+ intentFilter = new IntentFilter();
+ intentFilter.addAction(ACTION_PACKAGE_ADDED);
+ intentFilter.addAction(ACTION_PACKAGE_REMOVED);
+ intentFilter.addDataScheme("package");
+ userAllContext.registerReceiver(mBroadcastReceiver, intentFilter);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.APP_HIBERNATION_SERVICE, mServiceStub);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_BOOT_COMPLETED) {
+ synchronized (mLock) {
+ final List<UserInfo> users = mUserManager.getUsers();
+ // TODO: Pull from persistent disk storage. For now, just make from scratch.
+ for (UserInfo user : users) {
+ addUserPackageStatesL(user.id);
+ }
+ }
+ }
+ }
+
+ /**
+ * Whether a package is hibernating for a given user.
+ *
+ * @param packageName the package to check
+ * @param userId the user to check
+ * @return true if package is hibernating for the user
+ */
+ public boolean isHibernating(String packageName, int userId) {
+ userId = handleIncomingUser(userId, "isHibernating");
+ synchronized (mLock) {
+ final Map<String, UserPackageState> packageStates = mUserStates.get(userId);
+ if (packageStates == null) {
+ throw new IllegalArgumentException("No user associated with user id " + userId);
+ }
+ final UserPackageState pkgState = packageStates.get(packageName);
+ if (pkgState == null) {
+ throw new IllegalArgumentException(
+ String.format("Package %s is not installed for user %s",
+ packageName, userId));
+ }
+ return pkgState != null ? pkgState.hibernated : null;
+ }
+ }
+
+ /**
+ * Set whether the package is hibernating for the given user.
+ *
+ * @param packageName package to modify state
+ * @param userId user
+ * @param isHibernating new hibernation state
+ */
+ public void setHibernating(String packageName, int userId, boolean isHibernating) {
+ userId = handleIncomingUser(userId, "setHibernating");
+ synchronized (mLock) {
+ if (!mUserStates.contains(userId)) {
+ throw new IllegalArgumentException("No user associated with user id " + userId);
+ }
+ Map<String, UserPackageState> packageStates = mUserStates.get(userId);
+ UserPackageState pkgState = packageStates.get(packageName);
+ if (pkgState == null) {
+ throw new IllegalArgumentException(
+ String.format("Package %s is not installed for user %s",
+ packageName, userId));
+ }
+
+ if (pkgState.hibernated == isHibernating) {
+ return;
+ }
+
+
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ if (isHibernating) {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackage");
+ mIActivityManager.forceStopPackage(packageName, userId);
+ mIPackageManager.deleteApplicationCacheFilesAsUser(packageName, userId,
+ null /* observer */);
+ } else {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackage");
+ mIPackageManager.setPackageStoppedState(packageName, false, userId);
+ }
+ pkgState.hibernated = isHibernating;
+ } catch (RemoteException e) {
+ throw new IllegalStateException(
+ "Failed to hibernate due to manager not being available", e);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ Binder.restoreCallingIdentity(caller);
+ }
+
+ // TODO: Support package level hibernation when package is hibernating for all users
+ }
+ }
+
+ /**
+ * Populates {@link #mUserStates} with the users installed packages. The caller should hold
+ * {@link #mLock}.
+ *
+ * @param userId user id to add installed packages for
+ */
+ private void addUserPackageStatesL(int userId) {
+ Map<String, UserPackageState> packages = new ArrayMap<>();
+ List<PackageInfo> packageList;
+ try {
+ packageList = mIPackageManager.getInstalledPackages(MATCH_ALL, userId).getList();
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Package manager not available.", e);
+ }
+
+ for (PackageInfo pkg : packageList) {
+ packages.put(pkg.packageName, new UserPackageState());
+ }
+ mUserStates.put(userId, packages);
+ }
+
+ private void onUserAdded(int userId) {
+ synchronized (mLock) {
+ addUserPackageStatesL(userId);
+ }
+ }
+
+ private void onUserRemoved(int userId) {
+ synchronized (mLock) {
+ mUserStates.remove(userId);
+ }
+ }
+
+ private void onPackageAdded(@NonNull String packageName, int userId) {
+ synchronized (mLock) {
+ mUserStates.get(userId).put(packageName, new UserPackageState());
+ }
+ }
+
+ private void onPackageRemoved(@NonNull String packageName, int userId) {
+ synchronized (mLock) {
+ mUserStates.get(userId).remove(packageName);
+ }
+ }
+
+ /**
+ * Private helper method to get the real user id and enforce permission checks.
+ *
+ * @param userId user id to handle
+ * @param name name to use for exceptions
+ * @return real user id
+ */
+ private int handleIncomingUser(int userId, @NonNull String name) {
+ int callingUid = Binder.getCallingUid();
+ try {
+ return mIActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
+ false /* allowAll */, true /* requireFull */, name, null);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ private final AppHibernationServiceStub mServiceStub = new AppHibernationServiceStub(this);
+
+ static final class AppHibernationServiceStub extends IAppHibernationService.Stub {
+ final AppHibernationService mService;
+
+ AppHibernationServiceStub(AppHibernationService service) {
+ mService = service;
+ }
+
+ @Override
+ public boolean isHibernating(String packageName, int userId) {
+ return mService.isHibernating(packageName, userId);
+ }
+
+ @Override
+ public void setHibernating(String packageName, int userId, boolean isHibernating) {
+ mService.setHibernating(packageName, userId, isHibernating);
+ }
+
+ @Override
+ public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err, @NonNull String[] args,
+ @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver) {
+ new AppHibernationShellCommand(mService).exec(this, in, out, err, args, callback,
+ resultReceiver);
+ }
+ }
+
+ // Broadcast receiver for user and package add/removal events
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId == UserHandle.USER_NULL) {
+ return;
+ }
+
+ final String action = intent.getAction();
+ if (ACTION_USER_ADDED.equals(action)) {
+ onUserAdded(userId);
+ }
+ if (ACTION_USER_REMOVED.equals(action)) {
+ onUserRemoved(userId);
+ }
+ if (ACTION_PACKAGE_ADDED.equals(action) || ACTION_PACKAGE_REMOVED.equals(action)) {
+ final String packageName = intent.getData().getSchemeSpecificPart();
+ if (intent.getBooleanExtra(EXTRA_REPLACING, false)) {
+ // Package removal/add is part of an update, so no need to modify package state.
+ return;
+ }
+
+ if (ACTION_PACKAGE_ADDED.equals(action)) {
+ onPackageAdded(packageName, userId);
+ } else if (ACTION_PACKAGE_REMOVED.equals(action)) {
+ onPackageRemoved(packageName, userId);
+ }
+ }
+ }
+ };
+
+ /**
+ * Whether app hibernation is enabled on this device.
+ *
+ * @return true if enabled, false otherwise
+ */
+ public static boolean isAppHibernationEnabled() {
+ return DeviceConfig.getBoolean(
+ NAMESPACE_APP_HIBERNATION,
+ AppHibernationConstants.KEY_APP_HIBERNATION_ENABLED,
+ false /* defaultValue */);
+ }
+
+ /**
+ * Data class that contains hibernation state info of a package for a user.
+ */
+ private static final class UserPackageState {
+ public boolean hibernated;
+ // TODO: Track whether hibernation is exempted by the user
+ }
+}
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java b/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java
new file mode 100644
index 0000000..869885e
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java
@@ -0,0 +1,109 @@
+/*
+ * 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.apphibernation;
+
+import android.os.ShellCommand;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import java.io.PrintWriter;
+
+/**
+ * Shell command implementation for {@link AppHibernationService}.
+ */
+final class AppHibernationShellCommand extends ShellCommand {
+ private static final String USER_OPT = "--user";
+ private static final int SUCCESS = 0;
+ private static final int ERROR = -1;
+ private final AppHibernationService mService;
+
+ AppHibernationShellCommand(AppHibernationService service) {
+ mService = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ switch (cmd) {
+ case "set-state":
+ return runSetState();
+ case "get-state":
+ return runGetState();
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ private int runSetState() {
+ int userId = parseUserOption();
+
+ String pkg = getNextArgRequired();
+ if (pkg == null) {
+ getErrPrintWriter().println("Error: no package specified");
+ return ERROR;
+ }
+
+ String newStateRaw = getNextArgRequired();
+ if (newStateRaw == null) {
+ getErrPrintWriter().println("Error: No state to set specified");
+ return ERROR;
+ }
+ boolean newState = Boolean.parseBoolean(newStateRaw);
+
+ mService.setHibernating(pkg, userId, newState);
+ return SUCCESS;
+ }
+
+ private int runGetState() {
+ int userId = parseUserOption();
+
+ String pkg = getNextArgRequired();
+ if (pkg == null) {
+ getErrPrintWriter().println("Error: No package specified");
+ return ERROR;
+ }
+ boolean isHibernating = mService.isHibernating(pkg, userId);
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println(isHibernating);
+ return SUCCESS;
+ }
+
+ private int parseUserOption() {
+ String option = getNextOption();
+ if (TextUtils.equals(option, USER_OPT)) {
+ return UserHandle.parseUserArg(getNextArgRequired());
+ }
+ return UserHandle.USER_CURRENT;
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("App hibernation (app_hibernation) commands: ");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println("");
+ pw.println(" set-state [--user USER_ID] PACKAGE true|false");
+ pw.println(" Sets the hibernation state of the package to value specified");
+ pw.println("");
+ pw.println(" get-state [--user USER_ID] PACKAGE");
+ pw.println(" Gets the hibernation state of the package");
+ pw.println("");
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index c86bfcb..271537a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -563,14 +563,26 @@
final boolean isAuthenticating =
mCurrentOperation.mClientMonitor instanceof AuthenticationConsumer;
final boolean tokenMatches = mCurrentOperation.mClientMonitor.getToken() == token;
- if (!isAuthenticating || !tokenMatches) {
- Slog.w(getTag(), "Not cancelling authentication"
- + ", current operation : " + mCurrentOperation
- + ", tokenMatches: " + tokenMatches);
- return;
- }
- cancelInternal(mCurrentOperation);
+ if (isAuthenticating && tokenMatches) {
+ Slog.d(getTag(), "Cancelling authentication: " + mCurrentOperation);
+ cancelInternal(mCurrentOperation);
+ } else if (!isAuthenticating) {
+ // Look through the current queue for all authentication clients for the specified
+ // token, and mark them as STATE_WAITING_IN_QUEUE_CANCELING. Note that we're marking
+ // all of them, instead of just the first one, since the API surface currently doesn't
+ // allow us to distinguish between multiple authentication requests from the same
+ // process. However, this generally does not happen anyway, and would be a class of
+ // bugs on its own.
+ for (Operation operation : mPendingOperations) {
+ if (operation.mClientMonitor instanceof AuthenticationConsumer
+ && operation.mClientMonitor.getToken() == token) {
+ Slog.d(getTag(), "Marking " + operation
+ + " as STATE_WAITING_IN_QUEUE_CANCELING");
+ operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING;
+ }
+ }
+ }
}
/**
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
index 01a620f..d092e86 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
@@ -16,8 +16,11 @@
package com.android.server.biometrics.sensors.fingerprint;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.RemoteException;
import android.util.Slog;
@@ -85,4 +88,33 @@
Slog.e(TAG, "Remote exception when hiding the UDFPS overlay", e);
}
}
+
+ public static void onEnrollmentProgress(int sensorId, int remaining,
+ @Nullable IUdfpsOverlayController udfpsOverlayController) {
+ if (udfpsOverlayController == null) {
+ return;
+ }
+ try {
+ udfpsOverlayController.onEnrollmentProgress(sensorId, remaining);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when sending onEnrollmentProgress", e);
+ }
+ }
+
+ public static void onEnrollmentHelp(int sensorId,
+ @Nullable IUdfpsOverlayController udfpsOverlayController) {
+ if (udfpsOverlayController == null) {
+ return;
+ }
+ try {
+ udfpsOverlayController.onEnrollmentHelp(sensorId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when sending onEnrollmentHelp", e);
+ }
+ }
+
+ public static boolean isValidAcquisitionMessage(@NonNull Context context,
+ int acquireInfo, int vendorCode) {
+ return FingerprintManager.getAcquiredString(context, acquireInfo, vendorCode) != null;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index 0864c1a..08cc464 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -65,12 +65,23 @@
public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) {
super.onEnrollResult(identifier, remaining);
+ UdfpsHelper.onEnrollmentProgress(getSensorId(), remaining, mUdfpsOverlayController);
+
if (remaining == 0) {
UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
}
}
@Override
+ public void onAcquired(int acquiredInfo, int vendorCode) {
+ super.onAcquired(acquiredInfo, vendorCode);
+
+ if (UdfpsHelper.isValidAcquisitionMessage(getContext(), acquiredInfo, vendorCode)) {
+ UdfpsHelper.onEnrollmentHelp(getSensorId(), mUdfpsOverlayController);
+ }
+ }
+
+ @Override
public void onError(int errorCode, int vendorCode) {
super.onError(errorCode, vendorCode);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index acc575f..a4a8401 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -47,6 +47,7 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
@@ -397,7 +398,8 @@
});
}
- private synchronized IBiometricsFingerprint getDaemon() {
+ @VisibleForTesting
+ synchronized IBiometricsFingerprint getDaemon() {
if (mTestHalEnabled) {
final TestHal testHal = new TestHal();
testHal.setNotify(mHalResultController);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index 8493af1..d927aa7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -107,12 +107,23 @@
public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) {
super.onEnrollResult(identifier, remaining);
+ UdfpsHelper.onEnrollmentProgress(getSensorId(), remaining, mUdfpsOverlayController);
+
if (remaining == 0) {
UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
}
}
@Override
+ public void onAcquired(int acquiredInfo, int vendorCode) {
+ super.onAcquired(acquiredInfo, vendorCode);
+
+ if (UdfpsHelper.isValidAcquisitionMessage(getContext(), acquiredInfo, vendorCode)) {
+ UdfpsHelper.onEnrollmentHelp(getSensorId(), mUdfpsOverlayController);
+ }
+ }
+
+ @Override
public void onError(int errorCode, int vendorCode) {
super.onError(errorCode, vendorCode);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
index 11ffbb2..db7f4bc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
@@ -62,13 +62,7 @@
super.start(callback);
if (mCurrentUserId == getTargetUserId()) {
- Slog.d(TAG, "Already user: " + mCurrentUserId + ", refreshing authenticatorId");
- try {
- mAuthenticatorIds.put(getTargetUserId(), mHasEnrolledBiometrics
- ? getFreshDaemon().getAuthenticatorId() : 0L);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to refresh authenticatorId", e);
- }
+ Slog.d(TAG, "Already user: " + mCurrentUserId + ", returning");
callback.onClientFinished(this, true /* success */);
return;
}
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 9ba957e..e3757df 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -23,8 +23,11 @@
import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.server.compat.config.Change;
+import com.android.server.compat.overrides.ChangeOverrides;
+import com.android.server.compat.overrides.OverrideValue;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/**
@@ -253,6 +256,71 @@
return mDeferredOverrides != null && mDeferredOverrides.containsKey(packageName);
}
+ /**
+ * Checks whether a change has any package overrides.
+ * @return true if the change has at least one deferred override
+ */
+ boolean hasAnyPackageOverride() {
+ return mDeferredOverrides != null && !mDeferredOverrides.isEmpty();
+ }
+
+ /**
+ * Checks whether a change has any deferred overrides.
+ * @return true if the change has at least one deferred override
+ */
+ boolean hasAnyDeferredOverride() {
+ return mPackageOverrides != null && !mPackageOverrides.isEmpty();
+ }
+
+ void loadOverrides(ChangeOverrides changeOverrides) {
+ if (mDeferredOverrides == null) {
+ mDeferredOverrides = new HashMap<>();
+ }
+ mDeferredOverrides.clear();
+ for (OverrideValue override : changeOverrides.getDeferred().getOverrideValue()) {
+ mDeferredOverrides.put(override.getPackageName(), override.getEnabled());
+ }
+
+ if (mPackageOverrides == null) {
+ mPackageOverrides = new HashMap<>();
+ }
+ mPackageOverrides.clear();
+ for (OverrideValue override : changeOverrides.getValidated().getOverrideValue()) {
+ mPackageOverrides.put(override.getPackageName(), override.getEnabled());
+ }
+ }
+
+ ChangeOverrides saveOverrides() {
+ if (!hasAnyDeferredOverride() && !hasAnyPackageOverride()) {
+ return null;
+ }
+ ChangeOverrides changeOverrides = new ChangeOverrides();
+ changeOverrides.setChangeId(getId());
+ ChangeOverrides.Deferred deferredOverrides = new ChangeOverrides.Deferred();
+ List<OverrideValue> deferredList = deferredOverrides.getOverrideValue();
+ if (mDeferredOverrides != null) {
+ for (Map.Entry<String, Boolean> entry : mDeferredOverrides.entrySet()) {
+ OverrideValue override = new OverrideValue();
+ override.setPackageName(entry.getKey());
+ override.setEnabled(entry.getValue());
+ deferredList.add(override);
+ }
+ }
+ changeOverrides.setDeferred(deferredOverrides);
+ ChangeOverrides.Validated validatedOverrides = new ChangeOverrides.Validated();
+ List<OverrideValue> validatedList = validatedOverrides.getOverrideValue();
+ if (mPackageOverrides != null) {
+ for (Map.Entry<String, Boolean> entry : mPackageOverrides.entrySet()) {
+ OverrideValue override = new OverrideValue();
+ override.setPackageName(entry.getKey());
+ override.setEnabled(entry.getValue());
+ validatedList.add(override);
+ }
+ }
+ changeOverrides.setValidated(validatedOverrides);
+ return changeOverrides;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder("ChangeId(")
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 69686a2..6b77b9d 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -34,7 +34,10 @@
import com.android.internal.compat.IOverrideValidator;
import com.android.internal.compat.OverrideAllowedState;
import com.android.server.compat.config.Change;
-import com.android.server.compat.config.XmlParser;
+import com.android.server.compat.config.Config;
+import com.android.server.compat.overrides.ChangeOverrides;
+import com.android.server.compat.overrides.Overrides;
+import com.android.server.compat.overrides.XmlWriter;
import com.android.server.pm.ApexManager;
import org.xmlpull.v1.XmlPullParserException;
@@ -60,11 +63,14 @@
final class CompatConfig {
private static final String TAG = "CompatConfig";
+ private static final String APP_COMPAT_DATA_DIR = "/data/misc/appcompat";
+ private static final String OVERRIDES_FILE = "compat_framework_overrides.xml";
@GuardedBy("mChanges")
private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
private final OverrideValidatorImpl mOverrideValidator;
+ private File mOverridesFile;
@VisibleForTesting
CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) {
@@ -83,6 +89,8 @@
config.initConfigFromLib(Environment.buildPath(
apex.apexDirectory, "etc", "compatconfig"));
}
+ File overridesFile = new File(APP_COMPAT_DATA_DIR, OVERRIDES_FILE);
+ config.initOverrides(overridesFile);
config.invalidateCache();
return config;
}
@@ -202,6 +210,17 @@
* @throws IllegalStateException if overriding is not allowed
*/
boolean addOverride(long changeId, String packageName, boolean enabled) {
+ boolean alreadyKnown = addOverrideUnsafe(changeId, packageName, enabled);
+ saveOverrides();
+ invalidateCache();
+ return alreadyKnown;
+ }
+
+ /**
+ * Unsafe version of {@link #addOverride(long, String, boolean)}.
+ * It does not invalidate the cache nor save the overrides.
+ */
+ private boolean addOverrideUnsafe(long changeId, String packageName, boolean enabled) {
boolean alreadyKnown = true;
OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedState(changeId, packageName);
@@ -224,7 +243,6 @@
throw new IllegalStateException("Should only be able to override changes that "
+ "are allowed or can be deferred.");
}
- invalidateCache();
}
return alreadyKnown;
}
@@ -282,6 +300,17 @@
* @return {@code true} if an override existed;
*/
boolean removeOverride(long changeId, String packageName) {
+ boolean overrideExists = removeOverrideUnsafe(changeId, packageName);
+ saveOverrides();
+ invalidateCache();
+ return overrideExists;
+ }
+
+ /**
+ * Unsafe version of {@link #removeOverride(long, String)}.
+ * It does not invalidate the cache nor save the overrides.
+ */
+ private boolean removeOverrideUnsafe(long changeId, String packageName) {
boolean overrideExists = false;
synchronized (mChanges) {
CompatChange c = mChanges.get(changeId);
@@ -300,7 +329,6 @@
}
}
}
- invalidateCache();
return overrideExists;
}
@@ -315,12 +343,13 @@
void addOverrides(CompatibilityChangeConfig overrides, String packageName) {
synchronized (mChanges) {
for (Long changeId : overrides.enabledChanges()) {
- addOverride(changeId, packageName, true);
+ addOverrideUnsafe(changeId, packageName, true);
}
for (Long changeId : overrides.disabledChanges()) {
- addOverride(changeId, packageName, false);
+ addOverrideUnsafe(changeId, packageName, false);
}
+ saveOverrides();
invalidateCache();
}
}
@@ -337,8 +366,9 @@
synchronized (mChanges) {
for (int i = 0; i < mChanges.size(); ++i) {
CompatChange change = mChanges.valueAt(i);
- removeOverride(change.getId(), packageName);
+ removeOverrideUnsafe(change.getId(), packageName);
}
+ saveOverrides();
invalidateCache();
}
}
@@ -372,8 +402,10 @@
int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
for (long changeId : changes) {
- addOverride(changeId, packageName, true);
+ addOverrideUnsafe(changeId, packageName, true);
}
+ saveOverrides();
+ invalidateCache();
return changes.length;
}
@@ -386,8 +418,10 @@
int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
for (long changeId : changes) {
- addOverride(changeId, packageName, false);
+ addOverrideUnsafe(changeId, packageName, false);
}
+ saveOverrides();
+ invalidateCache();
return changes.length;
}
@@ -494,7 +528,8 @@
private void readConfig(File configFile) {
try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
- for (Change change : XmlParser.read(in).getCompatChange()) {
+ Config config = com.android.server.compat.config.XmlParser.read(in);
+ for (Change change : config.getCompatChange()) {
Slog.d(TAG, "Adding: " + change.toString());
addChange(new CompatChange(change));
}
@@ -503,6 +538,65 @@
}
}
+ void initOverrides(File overridesFile) {
+ if (!overridesFile.exists()) {
+ mOverridesFile = overridesFile;
+ // There have not been any overrides added yet.
+ return;
+ }
+
+ try (InputStream in = new BufferedInputStream(new FileInputStream(overridesFile))) {
+ Overrides overrides = com.android.server.compat.overrides.XmlParser.read(in);
+ for (ChangeOverrides changeOverrides : overrides.getChangeOverrides()) {
+ long changeId = changeOverrides.getChangeId();
+ CompatChange compatChange = mChanges.get(changeId);
+ if (compatChange == null) {
+ Slog.w(TAG, "Change ID " + changeId + " not found. "
+ + "Skipping overrides for it.");
+ continue;
+ }
+ compatChange.loadOverrides(changeOverrides);
+ }
+ } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+ Slog.w(TAG, "Error processing " + overridesFile + " " + e.toString());
+ return;
+ }
+ mOverridesFile = overridesFile;
+ }
+
+ /**
+ * Persist compat framework overrides to /data/misc/appcompat/compat_framework_overrides.xml
+ */
+ void saveOverrides() {
+ if (mOverridesFile == null) {
+ return;
+ }
+ synchronized (mChanges) {
+ // Create the file if it doesn't already exist
+ try {
+ mOverridesFile.createNewFile();
+ } catch (IOException e) {
+ Slog.e(TAG, "Could not create override config file: " + e.toString());
+ return;
+ }
+ try (PrintWriter out = new PrintWriter(mOverridesFile)) {
+ XmlWriter writer = new XmlWriter(out);
+ Overrides overrides = new Overrides();
+ List<ChangeOverrides> changeOverridesList = overrides.getChangeOverrides();
+ for (int idx = 0; idx < mChanges.size(); ++idx) {
+ CompatChange c = mChanges.valueAt(idx);
+ ChangeOverrides changeOverrides = c.saveOverrides();
+ if (changeOverrides != null) {
+ changeOverridesList.add(changeOverrides);
+ }
+ }
+ XmlWriter.write(writer, overrides);
+ } catch (IOException e) {
+ Slog.e(TAG, e.toString());
+ }
+ }
+ }
+
IOverrideValidator getOverrideValidator() {
return mOverrideValidator;
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index fb1e819..8ce6746 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -70,6 +70,7 @@
import android.net.RouteInfo;
import android.net.UidRange;
import android.net.UidRangeParcel;
+import android.net.VpnInfo;
import android.net.VpnManager;
import android.net.VpnService;
import android.net.ipsec.ike.ChildSessionCallback;
@@ -109,7 +110,6 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
-import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
@@ -1816,18 +1816,15 @@
}
/**
- * This method should only be called by ConnectivityService because it doesn't
- * have enough data to fill VpnInfo.primaryUnderlyingIface field.
+ * This method should not be called if underlying interfaces field is needed, because it doesn't
+ * have enough data to fill VpnInfo.underlyingIfaces field.
*/
public synchronized VpnInfo getVpnInfo() {
if (!isRunningLocked()) {
return null;
}
- VpnInfo info = new VpnInfo();
- info.ownerUid = mOwnerUID;
- info.vpnIface = mInterface;
- return info;
+ return new VpnInfo(mOwnerUID, mInterface, null);
}
public synchronized boolean appliesToUid(int uid) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
index d66bf63..6918064 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -303,6 +303,8 @@
return STORAGE_SHARED_PREFS;
case HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY:
return STORAGE_GLOBAL_SETTINGS;
+ case HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP:
+ return STORAGE_GLOBAL_SETTINGS;
case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV:
return STORAGE_SHARED_PREFS;
case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU:
@@ -338,6 +340,8 @@
return setting.getName();
case HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY:
return Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED;
+ case HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP:
+ return Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED;
case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV:
return setting.getName();
case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU:
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index ccce9dc..382f0f9 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -956,8 +956,6 @@
}
}
- void setAutoDeviceOff(boolean enabled) {}
-
/**
* Called when a hot-plug event issued.
*
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index b909b16..bf5bf8b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -695,7 +695,6 @@
@ServiceThreadOnly
void setArcStatus(boolean enabled) {
- // TODO(shubang): add tests
assertRunOnServiceThread();
HdmiLogger.debug("Set Arc Status[old:%b new:%b]", mArcEstablished, enabled);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index e6cf18b..75b52f9 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -24,7 +24,6 @@
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.SystemProperties;
-import android.provider.Settings.Global;
import android.sysprop.HdmiProperties;
import android.util.Slog;
@@ -56,9 +55,6 @@
// Lazily initialized - should call getWakeLock() to get the instance.
private ActiveWakeLock mWakeLock;
- // If true, turn off TV upon standby. False by default.
- private boolean mAutoTvOff;
-
// Determines what action should be taken upon receiving Routing Control messages.
@VisibleForTesting
protected HdmiProperties.playback_device_action_on_routing_control_values
@@ -68,12 +64,6 @@
HdmiCecLocalDevicePlayback(HdmiControlService service) {
super(service, HdmiDeviceInfo.DEVICE_PLAYBACK);
-
- mAutoTvOff = mService.readBooleanSetting(Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, false);
-
- // The option is false by default. Update settings db as well to have the right
- // initial setting on UI.
- mService.writeBooleanSetting(Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, mAutoTvOff);
}
@Override
@@ -154,7 +144,10 @@
// Invalidate the internal active source record when goes to standby
mService.setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS,
"HdmiCecLocalDevicePlayback#onStandby()");
- if (initiatedByCec || !mAutoTvOff || !wasActiveSource) {
+ boolean mTvSendStandbyOnSleep = mService.getHdmiCecConfig().getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP)
+ == HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED;
+ if (initiatedByCec || !mTvSendStandbyOnSleep || !wasActiveSource) {
return;
}
switch (standbyAction) {
@@ -201,13 +194,6 @@
}
@Override
- @ServiceThreadOnly
- void setAutoDeviceOff(boolean enabled) {
- assertRunOnServiceThread();
- mAutoTvOff = enabled;
- }
-
- @Override
@CallSuper
@ServiceThreadOnly
@VisibleForTesting
@@ -425,7 +411,6 @@
protected void dump(final IndentingPrintWriter pw) {
super.dump(pw);
pw.println("isActiveSource(): " + isActiveSource());
- pw.println("mAutoTvOff:" + mAutoTvOff);
}
// Wrapper interface over PowerManager.WakeLock
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 5ef3738..a3e18d1 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -89,9 +89,6 @@
@GuardedBy("mLock")
private boolean mSystemAudioMute = false;
- // If true, TV going to standby mode puts other devices also to standby.
- private boolean mAutoDeviceOff;
-
private final HdmiCecStandbyModeHandler mStandbyHandler;
// If true, do not do routing control/send active source for internal source.
@@ -156,8 +153,6 @@
HdmiCecLocalDeviceTv(HdmiControlService service) {
super(service, HdmiDeviceInfo.DEVICE_TV);
mPrevPortId = Constants.INVALID_PORT_ID;
- mAutoDeviceOff = mService.readBooleanSetting(Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
- true);
mSystemAudioControlFeatureEnabled =
mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);
mStandbyHandler = new HdmiCecStandbyModeHandler(service, this);
@@ -1202,13 +1197,6 @@
}
}
- @Override
- @ServiceThreadOnly
- void setAutoDeviceOff(boolean enabled) {
- assertRunOnServiceThread();
- mAutoDeviceOff = enabled;
- }
-
@ServiceThreadOnly
boolean getAutoWakeup() {
assertRunOnServiceThread();
@@ -1286,7 +1274,11 @@
if (!mService.isControlEnabled()) {
return;
}
- if (!initiatedByCec && mAutoDeviceOff) {
+ boolean sendStandbyOnSleep =
+ mService.getHdmiCecConfig().getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP)
+ == HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED;
+ if (!initiatedByCec && sendStandbyOnSleep) {
mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(
mAddress, Constants.ADDR_BROADCAST));
}
@@ -1545,7 +1537,6 @@
pw.println("mArcFeatureEnabled: " + mArcFeatureEnabled);
pw.println("mSystemAudioMute: " + mSystemAudioMute);
pw.println("mSystemAudioControlFeatureEnabled: " + mSystemAudioControlFeatureEnabled);
- pw.println("mAutoDeviceOff: " + mAutoDeviceOff);
pw.println("mSkipRoutingControl: " + mSkipRoutingControl);
pw.println("mPrevPortId: " + mPrevPortId);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 8febd4f..0ae1994 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -661,7 +661,6 @@
ContentResolver resolver = getContext().getContentResolver();
String[] settings = new String[] {
Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED,
- Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
Global.MHL_INPUT_SWITCHING_ENABLED,
Global.MHL_POWER_CHARGE_ENABLED,
@@ -689,15 +688,6 @@
setHdmiCecVolumeControlEnabledInternal(getHdmiCecConfig().getIntValue(
HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE));
break;
- case Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED:
- for (int type : mLocalDevices) {
- HdmiCecLocalDevice localDevice = mHdmiCecNetwork.getLocalDevice(type);
- if (localDevice != null) {
- localDevice.setAutoDeviceOff(enabled);
- }
- }
- // No need to propagate to HAL.
- break;
case Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED:
if (isTvDeviceEnabled()) {
tv().setSystemAudioControlFeatureEnabled(enabled);
diff --git a/services/core/java/com/android/server/hdmi/cec_config.xml b/services/core/java/com/android/server/hdmi/cec_config.xml
index a751fd7..191e725 100644
--- a/services/core/java/com/android/server/hdmi/cec_config.xml
+++ b/services/core/java/com/android/server/hdmi/cec_config.xml
@@ -64,6 +64,15 @@
</allowed-values>
<default-value int-value="1" />
</setting>
+ <setting name="tv_send_standby_on_sleep"
+ value-type="int"
+ user-configurable="true">
+ <allowed-values>
+ <value int-value="0" />
+ <value int-value="1" />
+ </allowed-values>
+ <default-value int-value="1" />
+ </setting>
<setting name="rc_profile_tv"
value-type="int"
user-configurable="false">
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
index e9868fd..4faa790 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -27,6 +27,7 @@
import android.annotation.Nullable;
import android.net.INetd;
import android.net.NetworkStats;
+import android.net.VpnInfo;
import android.net.util.NetdService;
import android.os.RemoteException;
import android.os.StrictMode;
@@ -34,7 +35,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ProcFileReader;
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 81a6641..4be7b48 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -105,6 +105,7 @@
import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.net.Uri;
+import android.net.VpnInfo;
import android.net.netstats.provider.INetworkStatsProvider;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.net.netstats.provider.NetworkStatsProvider;
@@ -143,7 +144,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FileRotator;
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 314510b..f43240b 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -47,6 +47,7 @@
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
import android.content.pm.UserInfo.UserInfoFlag;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.os.Binder;
@@ -92,6 +93,7 @@
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
+import android.util.TypedValue;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
@@ -139,6 +141,7 @@
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
/**
* Service for {@link UserManager}.
@@ -461,6 +464,26 @@
}
};
+ /**
+ * Cache the owner name string, since it could be read repeatedly on a critical code path
+ * but hit by slow IO. This could be eliminated once we have the cached UserInfo in place.
+ */
+ private final AtomicReference<String> mOwnerName = new AtomicReference<>();
+
+ private final TypedValue mOwnerNameTypedValue = new TypedValue();
+
+ private final Configuration mLastConfiguration = new Configuration();
+
+ private final BroadcastReceiver mConfigurationChangeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!Intent.ACTION_CONFIGURATION_CHANGED.equals(intent.getAction())) {
+ return;
+ }
+ invalidateOwnerNameIfNecessary(context.getResources(), false /* forceUpdate */);
+ }
+ };
+
// TODO(b/161915546): remove once userWithName() is fixed / removed
// Use to debug / dump when user 0 is allocated at userWithName()
public static final boolean DBG_ALLOCATION = false; // DO NOT SUBMIT WITH TRUE
@@ -636,6 +659,7 @@
mHandler = new MainHandler();
mUserDataPreparer = userDataPreparer;
mUserTypes = UserTypeFactory.getUserTypes();
+ invalidateOwnerNameIfNecessary(context.getResources(), true /* forceUpdate */);
synchronized (mPackagesLock) {
mUsersDir = new File(dataDir, USER_INFO_DIR);
mUsersDir.mkdirs();
@@ -669,6 +693,10 @@
mContext.registerReceiver(mDisableQuietModeCallback,
new IntentFilter(ACTION_DISABLE_QUIET_MODE_AFTER_UNLOCK),
null, mHandler);
+
+ mContext.registerReceiver(mConfigurationChangeReceiver,
+ new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED),
+ null, mHandler);
}
/**
@@ -2851,7 +2879,16 @@
}
private String getOwnerName() {
- return mContext.getResources().getString(com.android.internal.R.string.owner_name);
+ return mOwnerName.get();
+ }
+
+ private void invalidateOwnerNameIfNecessary(@NonNull Resources res, boolean forceUpdate) {
+ final int configChanges = mLastConfiguration.updateFrom(res.getConfiguration());
+ if (forceUpdate || (configChanges & mOwnerNameTypedValue.changingConfigurations) != 0) {
+ res.getValue(com.android.internal.R.string.owner_name, mOwnerNameTypedValue, true);
+ final CharSequence ownerName = mOwnerNameTypedValue.coerceToString();
+ mOwnerName.set(ownerName != null ? ownerName.toString() : null);
+ }
}
private void scheduleWriteUser(UserData userData) {
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index bd66aa3..a4459d0 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -33,9 +33,9 @@
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.ParceledListSlice;
import android.content.pm.VersionedPackage;
+import android.content.pm.parsing.ApkLite;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
@@ -831,24 +831,24 @@
}
// Get information about the package to be installed.
- ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
- ParseResult<PackageParser.ApkLite> parseResult = ApkLiteParseUtils.parseApkLite(
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<ApkLite> parseResult = ApkLiteParseUtils.parseApkLite(
input.reset(), new File(session.resolvedBaseCodePath), 0);
if (parseResult.isError()) {
Slog.e(TAG, "Unable to parse new package: " + parseResult.getErrorMessage(),
parseResult.getException());
return false;
}
- PackageParser.ApkLite newPackage = parseResult.getResult();
+ final ApkLite newPackage = parseResult.getResult();
- String packageName = newPackage.packageName;
- int rollbackDataPolicy = computeRollbackDataPolicy(
- session.rollbackDataPolicy, newPackage.rollbackDataPolicy);
+ final String packageName = newPackage.getPackageName();
+ final int rollbackDataPolicy = computeRollbackDataPolicy(
+ session.rollbackDataPolicy, newPackage.getRollbackDataPolicy());
Slog.i(TAG, "Enabling rollback for install of " + packageName
+ ", session:" + session.sessionId
+ ", rollbackDataPolicy=" + rollbackDataPolicy);
- String installerPackageName = session.getInstallerPackageName();
+ final String installerPackageName = session.getInstallerPackageName();
if (!enableRollbackAllowed(installerPackageName, packageName)) {
Slog.e(TAG, "Installer " + installerPackageName
+ " is not allowed to enable rollback on " + packageName);
@@ -900,7 +900,7 @@
* a rollback object is inconsistent because it doesn't count apk-in-apex.
*/
ApplicationInfo appInfo = pkgInfo.applicationInfo;
- return rollback.enableForPackage(packageName, newPackage.versionCode,
+ return rollback.enableForPackage(packageName, newPackage.getVersionCode(),
pkgInfo.getLongVersionCode(), isApex, appInfo.sourceDir,
appInfo.splitSourceDirs, rollbackDataPolicy);
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
index 90ac69a..cf7460b 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
@@ -42,7 +42,7 @@
static final String TAG = "SoundTriggerHw2Enforcer";
final ISoundTriggerHw2 mUnderlying;
- final Map<Integer, Boolean> mModelStates = new HashMap<>();
+ Map<Integer, Boolean> mModelStates = new HashMap<>();
public SoundTriggerHw2Enforcer(
ISoundTriggerHw2 underlying) {
@@ -62,12 +62,12 @@
public int loadSoundModel(ISoundTriggerHw.SoundModel soundModel, Callback callback,
int cookie) {
try {
+ int handle = mUnderlying.loadSoundModel(soundModel, new CallbackEnforcer(callback),
+ cookie);
synchronized (mModelStates) {
- int handle = mUnderlying.loadSoundModel(soundModel, new CallbackEnforcer(callback),
- cookie);
mModelStates.put(handle, false);
- return handle;
}
+ return handle;
} catch (RuntimeException e) {
throw handleException(e);
}
@@ -77,13 +77,13 @@
public int loadPhraseSoundModel(ISoundTriggerHw.PhraseSoundModel soundModel, Callback callback,
int cookie) {
try {
+ int handle = mUnderlying.loadPhraseSoundModel(soundModel,
+ new CallbackEnforcer(callback),
+ cookie);
synchronized (mModelStates) {
- int handle = mUnderlying.loadPhraseSoundModel(soundModel,
- new CallbackEnforcer(callback),
- cookie);
mModelStates.put(handle, false);
- return handle;
}
+ return handle;
} catch (RuntimeException e) {
throw handleException(e);
}
@@ -92,8 +92,8 @@
@Override
public void unloadSoundModel(int modelHandle) {
try {
+ mUnderlying.unloadSoundModel(modelHandle);
synchronized (mModelStates) {
- mUnderlying.unloadSoundModel(modelHandle);
mModelStates.remove(modelHandle);
}
} catch (RuntimeException e) {
@@ -104,8 +104,8 @@
@Override
public void stopRecognition(int modelHandle) {
try {
+ mUnderlying.stopRecognition(modelHandle);
synchronized (mModelStates) {
- mUnderlying.stopRecognition(modelHandle);
mModelStates.replace(modelHandle, false);
}
} catch (RuntimeException e) {
@@ -116,8 +116,8 @@
@Override
public void stopAllRecognitions() {
try {
+ mUnderlying.stopAllRecognitions();
synchronized (mModelStates) {
- mUnderlying.stopAllRecognitions();
for (Map.Entry<Integer, Boolean> entry : mModelStates.entrySet()) {
entry.setValue(false);
}
@@ -130,12 +130,14 @@
@Override
public void startRecognition(int modelHandle, RecognitionConfig config, Callback callback,
int cookie) {
+ // It is possible that an event will be sent before the HAL returns from the
+ // startRecognition call, thus it is important to set the state to active before the call.
+ synchronized (mModelStates) {
+ mModelStates.replace(modelHandle, true);
+ }
try {
- synchronized (mModelStates) {
- mUnderlying.startRecognition(modelHandle, config, new CallbackEnforcer(callback),
- cookie);
- mModelStates.replace(modelHandle, true);
- }
+ mUnderlying.startRecognition(modelHandle, config, new CallbackEnforcer(callback),
+ cookie);
} catch (RuntimeException e) {
throw handleException(e);
}
diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
index e1feb5a..6427ae2 100644
--- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
+++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
@@ -24,6 +24,9 @@
import android.os.Handler;
import android.os.ParcelUuid;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+
import java.util.Objects;
/**
@@ -72,7 +75,8 @@
@NonNull public final LinkProperties linkProperties;
public final boolean blocked;
- private UnderlyingNetworkRecord(
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ UnderlyingNetworkRecord(
@NonNull Network network,
@NonNull NetworkCapabilities networkCapabilities,
@NonNull LinkProperties linkProperties,
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 4e0c0c5..8805fa2 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -24,7 +24,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.net.ConnectivityManager;
import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.IpSecManager;
@@ -36,8 +35,6 @@
import android.net.Network;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
import android.net.RouteInfo;
import android.net.annotations.PolicyDirection;
import android.net.ipsec.ike.ChildSessionCallback;
@@ -54,7 +51,6 @@
import android.os.HandlerExecutor;
import android.os.Message;
import android.os.ParcelUuid;
-import android.telephony.TelephonyManager;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -126,7 +122,9 @@
private static final int TOKEN_ALL = Integer.MIN_VALUE;
private static final int NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS = 30;
- private static final int TEARDOWN_TIMEOUT_SECONDS = 5;
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int TEARDOWN_TIMEOUT_SECONDS = 5;
private interface EventInfo {}
@@ -360,11 +358,25 @@
*/
private static final int EVENT_TEARDOWN_TIMEOUT_EXPIRED = 8;
- @NonNull private final DisconnectedState mDisconnectedState = new DisconnectedState();
- @NonNull private final DisconnectingState mDisconnectingState = new DisconnectingState();
- @NonNull private final ConnectingState mConnectingState = new ConnectingState();
- @NonNull private final ConnectedState mConnectedState = new ConnectedState();
- @NonNull private final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState();
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ @NonNull
+ final DisconnectedState mDisconnectedState = new DisconnectedState();
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ @NonNull
+ final DisconnectingState mDisconnectingState = new DisconnectingState();
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ @NonNull
+ final ConnectingState mConnectingState = new ConnectingState();
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ @NonNull
+ final ConnectedState mConnectedState = new ConnectedState();
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ @NonNull
+ final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState();
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
@@ -403,13 +415,6 @@
private int mCurrentToken = -1;
/**
- * The next usable token.
- *
- * <p>A new token MUST be used for all new IKE sessions.
- */
- private int mNextToken = 0;
-
- /**
* The number of unsuccessful attempts since the last successful connection.
*
* <p>This number MUST be incremented each time the RetryTimeout state is entered, and cleared
@@ -430,7 +435,7 @@
* <p>Set in Connecting or Migrating States, always @NonNull in Connecting, Connected, and
* Migrating states, null otherwise.
*/
- private IkeSession mIkeSession;
+ private VcnIkeSession mIkeSession;
/**
* The last known child configuration.
@@ -455,7 +460,8 @@
this(vcnContext, subscriptionGroup, connectionConfig, new Dependencies());
}
- private VcnGatewayConnection(
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ VcnGatewayConnection(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnGatewayConnectionConfig connectionConfig,
@@ -508,7 +514,6 @@
EVENT_DISCONNECT_REQUESTED,
TOKEN_ALL,
new EventDisconnectRequestedInfo(DISCONNECT_REASON_TEARDOWN));
- quit();
// TODO: Notify VcnInstance (via callbacks) of permanent teardown of this tunnel, since this
// is also called asynchronously when a NetworkAgent becomes unwanted
@@ -654,7 +659,7 @@
protected void teardownNetwork() {
if (mNetworkAgent != null) {
- mNetworkAgent.sendNetworkInfo(buildNetworkInfo(false /* isConnected */));
+ mNetworkAgent.unregister();
mNetworkAgent = null;
}
}
@@ -667,6 +672,8 @@
protected void handleDisconnectRequested(String msg) {
Slog.v(TAG, "Tearing down. Cause: " + msg);
+ mIsRunning = false;
+
teardownNetwork();
teardownIke();
@@ -697,7 +704,37 @@
*/
private class DisconnectedState extends BaseState {
@Override
- protected void processStateMsg(Message msg) {}
+ protected void enterState() {
+ if (!mIsRunning) {
+ quitNow(); // Ignore all queued events; cleanup is complete.
+ }
+
+ if (mIkeSession != null || mNetworkAgent != null) {
+ Slog.wtf(TAG, "Active IKE Session or NetworkAgent in DisconnectedState");
+ }
+ }
+
+ @Override
+ protected void processStateMsg(Message msg) {
+ switch (msg.what) {
+ case EVENT_UNDERLYING_NETWORK_CHANGED:
+ // First network found; start tunnel
+ mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+ if (mUnderlying != null) {
+ transitionTo(mConnectingState);
+ }
+ break;
+ case EVENT_DISCONNECT_REQUESTED:
+ mIsRunning = false;
+
+ quitNow();
+ break;
+ default:
+ logUnhandledMessage(msg);
+ break;
+ }
+ }
}
private abstract class ActiveBaseState extends BaseState {
@@ -732,7 +769,70 @@
*/
private class DisconnectingState extends ActiveBaseState {
@Override
- protected void processStateMsg(Message msg) {}
+ protected void enterState() throws Exception {
+ if (mIkeSession == null) {
+ Slog.wtf(TAG, "IKE session was already closed when entering Disconnecting state.");
+ sendMessage(EVENT_SESSION_CLOSED, mCurrentToken);
+ return;
+ }
+
+ // If underlying network has already been lost, save some time and just kill the session
+ if (mUnderlying == null) {
+ // Will trigger a EVENT_SESSION_CLOSED as IkeSession shuts down.
+ mIkeSession.kill();
+ return;
+ }
+
+ sendMessageDelayed(
+ EVENT_TEARDOWN_TIMEOUT_EXPIRED,
+ mCurrentToken,
+ TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS));
+ }
+
+ @Override
+ protected void processStateMsg(Message msg) {
+ switch (msg.what) {
+ case EVENT_UNDERLYING_NETWORK_CHANGED: // Fallthrough
+ mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+ // If we received a new underlying network, continue.
+ if (mUnderlying != null) {
+ break;
+ }
+
+ // Fallthrough; no network exists to send IKE close session requests.
+ case EVENT_TEARDOWN_TIMEOUT_EXPIRED:
+ // Grace period ended. Kill session, triggering EVENT_SESSION_CLOSED
+ mIkeSession.kill();
+
+ break;
+ case EVENT_DISCONNECT_REQUESTED:
+ teardownNetwork();
+
+ String reason = ((EventDisconnectRequestedInfo) msg.obj).reason;
+ if (reason.equals(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)) {
+ // Will trigger EVENT_SESSION_CLOSED immediately.
+ mIkeSession.kill();
+ break;
+ }
+
+ // Otherwise we are already in the process of shutting down.
+ break;
+ case EVENT_SESSION_CLOSED:
+ mIkeSession = null;
+
+ if (mIsRunning && mUnderlying != null) {
+ transitionTo(mRetryTimeoutState);
+ } else {
+ teardownNetwork();
+ transitionTo(mDisconnectedState);
+ }
+ break;
+ default:
+ logUnhandledMessage(msg);
+ break;
+ }
+ }
}
/**
@@ -769,20 +869,6 @@
protected void processStateMsg(Message msg) {}
}
- // TODO: Remove this when migrating to new NetworkAgent API
- private static NetworkInfo buildNetworkInfo(boolean isConnected) {
- NetworkInfo info =
- new NetworkInfo(
- ConnectivityManager.TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN,
- "MOBILE",
- "VCN");
- info.setDetailedState(
- isConnected ? DetailedState.CONNECTED : DetailedState.DISCONNECTED, null, null);
-
- return info;
- }
-
@VisibleForTesting(visibility = Visibility.PRIVATE)
static NetworkCapabilities buildNetworkCapabilities(
@NonNull VcnGatewayConnectionConfig gatewayConnectionConfig) {
@@ -893,7 +979,64 @@
}
}
- /** External dependencies used by VcnGatewayConnection, for injection in tests. */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ UnderlyingNetworkTrackerCallback getUnderlyingNetworkTrackerCallback() {
+ return mUnderlyingNetworkTrackerCallback;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ UnderlyingNetworkRecord getUnderlyingNetwork() {
+ return mUnderlying;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ void setUnderlyingNetwork(@Nullable UnderlyingNetworkRecord record) {
+ mUnderlying = record;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ boolean isRunning() {
+ return mIsRunning;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ void setIsRunning(boolean isRunning) {
+ mIsRunning = isRunning;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ VcnIkeSession getIkeSession() {
+ return mIkeSession;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ void setIkeSession(@Nullable VcnIkeSession session) {
+ mIkeSession = session;
+ }
+
+ private IkeSessionParams buildIkeParams() {
+ // TODO: Implement this with ConnectingState
+ return null;
+ }
+
+ private ChildSessionParams buildChildParams() {
+ // TODO: Implement this with ConnectingState
+ return null;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ VcnIkeSession buildIkeSession() {
+ final int token = ++mCurrentToken;
+
+ return mDeps.newIkeSession(
+ mVcnContext,
+ buildIkeParams(),
+ buildChildParams(),
+ new IkeSessionCallbackImpl(token),
+ new ChildSessionCallbackImpl(token));
+ }
+
+ /** External dependencies used by VcnGatewayConnection, for injection in tests */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static class Dependencies {
/** Builds a new UnderlyingNetworkTracker. */
@@ -905,19 +1048,67 @@
}
/** Builds a new IkeSession. */
- public IkeSession newIkeSession(
+ public VcnIkeSession newIkeSession(
VcnContext vcnContext,
IkeSessionParams ikeSessionParams,
ChildSessionParams childSessionParams,
IkeSessionCallback ikeSessionCallback,
ChildSessionCallback childSessionCallback) {
- return new IkeSession(
- vcnContext.getContext(),
+ return new VcnIkeSession(
+ vcnContext,
ikeSessionParams,
childSessionParams,
- new HandlerExecutor(new Handler(vcnContext.getLooper())),
ikeSessionCallback,
childSessionCallback);
}
}
+
+ /** Proxy implementation of IKE session, used for testing. */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static class VcnIkeSession {
+ private final IkeSession mImpl;
+
+ public VcnIkeSession(
+ VcnContext vcnContext,
+ IkeSessionParams ikeSessionParams,
+ ChildSessionParams childSessionParams,
+ IkeSessionCallback ikeSessionCallback,
+ ChildSessionCallback childSessionCallback) {
+ mImpl =
+ new IkeSession(
+ vcnContext.getContext(),
+ ikeSessionParams,
+ childSessionParams,
+ new HandlerExecutor(new Handler(vcnContext.getLooper())),
+ ikeSessionCallback,
+ childSessionCallback);
+ }
+
+ /** Creates a new IKE Child session. */
+ public void openChildSession(
+ @NonNull ChildSessionParams childSessionParams,
+ @NonNull ChildSessionCallback childSessionCallback) {
+ mImpl.openChildSession(childSessionParams, childSessionCallback);
+ }
+
+ /** Closes an IKE session as identified by the ChildSessionCallback. */
+ public void closeChildSession(@NonNull ChildSessionCallback childSessionCallback) {
+ mImpl.closeChildSession(childSessionCallback);
+ }
+
+ /** Gracefully closes this IKE Session, waiting for remote acknowledgement. */
+ public void close() {
+ mImpl.close();
+ }
+
+ /** Forcibly kills this IKE Session, without waiting for a closure confirmation. */
+ public void kill() {
+ mImpl.kill();
+ }
+
+ /** Sets the underlying network used by the IkeSession. */
+ public void setNetwork(@NonNull Network network) {
+ mImpl.setNetwork(network);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index ae20a72..7257478 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -37,15 +37,11 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Binder;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.proto.ProtoOutputStream;
import android.view.RemoteAnimationAdapter;
import com.android.internal.annotations.VisibleForTesting;
@@ -55,10 +51,8 @@
import com.android.server.uri.NeededUriGrants;
import com.android.server.wm.ActivityStarter.DefaultFactory;
import com.android.server.wm.ActivityStarter.Factory;
-import com.android.server.wm.ActivityTaskSupervisor.PendingActivityLaunch;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.List;
/**
@@ -87,35 +81,12 @@
/** The result of the last home activity we attempted to start. */
private int mLastHomeActivityStartResult;
- /** A list of activities that are waiting to launch. */
- private final ArrayList<ActivityTaskSupervisor.PendingActivityLaunch>
- mPendingActivityLaunches = new ArrayList<>();
-
private final Factory mFactory;
- private final Handler mHandler;
-
private final PendingRemoteAnimationRegistry mPendingRemoteAnimationRegistry;
boolean mCheckedForSetup = false;
- private final class StartHandler extends Handler {
- public StartHandler(Looper looper) {
- super(looper, null, true);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch(msg.what) {
- case DO_PENDING_ACTIVITY_LAUNCHES_MSG:
- synchronized (mService.mGlobalLock) {
- doPendingActivityLaunches(true);
- }
- break;
- }
- }
- }
-
/**
* TODO(b/64750076): Capture information necessary for dump and
* {@link #postStartActivityProcessingForLastStarter} rather than keeping the entire object
@@ -134,7 +105,6 @@
Factory factory) {
mService = service;
mSupervisor = supervisor;
- mHandler = new StartHandler(mService.mH.getLooper());
mFactory = factory;
mFactory.setController(this);
mPendingRemoteAnimationRegistry = new PendingRemoteAnimationRegistry(service.mGlobalLock,
@@ -514,45 +484,6 @@
return START_SUCCESS;
}
- void schedulePendingActivityLaunches(long delayMs) {
- mHandler.removeMessages(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
- Message msg = mHandler.obtainMessage(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
- mHandler.sendMessageDelayed(msg, delayMs);
- }
-
- void doPendingActivityLaunches(boolean doResume) {
- while (!mPendingActivityLaunches.isEmpty()) {
- final PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
- final boolean resume = doResume && mPendingActivityLaunches.isEmpty();
- final ActivityStarter starter = obtainStarter(null /* intent */,
- "pendingActivityLaunch");
- try {
- starter.startResolvedActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags,
- resume, pal.r.getOptions(), null, pal.intentGrants);
- } catch (Exception e) {
- Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
- pal.sendErrorResult(e.getMessage());
- }
- }
- }
-
- void addPendingActivityLaunch(PendingActivityLaunch launch) {
- mPendingActivityLaunches.add(launch);
- }
-
- boolean clearPendingActivityLaunches(String packageName) {
- final int pendingLaunches = mPendingActivityLaunches.size();
-
- for (int palNdx = pendingLaunches - 1; palNdx >= 0; --palNdx) {
- final PendingActivityLaunch pal = mPendingActivityLaunches.get(palNdx);
- final ActivityRecord r = pal.r;
- if (r != null && r.packageName.equals(packageName)) {
- mPendingActivityLaunches.remove(palNdx);
- }
- }
- return mPendingActivityLaunches.size() < pendingLaunches;
- }
-
void registerRemoteAnimationForNextActivityStart(String packageName,
RemoteAnimationAdapter adapter) {
mPendingRemoteAnimationRegistry.addPendingAnimation(packageName, adapter);
@@ -609,10 +540,4 @@
pw.println("(nothing)");
}
}
-
- public void dumpDebug(ProtoOutputStream proto, long fieldId) {
- for (PendingActivityLaunch activity: mPendingActivityLaunches) {
- activity.r.writeIdentifierToProto(proto, fieldId);
- }
- }
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 4fa4a67..c6ed16c 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -122,7 +122,6 @@
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.NeededUriGrants;
import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
-import com.android.server.wm.ActivityTaskSupervisor.PendingActivityLaunch;
import com.android.server.wm.LaunchParamsController.LaunchParams;
import java.io.PrintWriter;
@@ -1171,42 +1170,19 @@
r.appTimeTracker = sourceRecord.appTimeTracker;
}
- final Task rootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
-
- // If we are starting an activity that is not from the same uid as the currently resumed
- // one, check whether app switches are allowed.
- if (voiceSession == null && rootTask != null && (rootTask.getResumedActivity() == null
- || rootTask.getResumedActivity().info.applicationInfo.uid != realCallingUid)) {
- if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
- realCallingPid, realCallingUid, "Activity start")) {
- if (!(restrictedBgActivity && handleBackgroundActivityAbort(r))) {
- mController.addPendingActivityLaunch(new PendingActivityLaunch(r,
- sourceRecord, startFlags, rootTask, callerApp, intentGrants));
- }
- ActivityOptions.abort(checkedOptions);
- return ActivityManager.START_SWITCHES_CANCELED;
- }
+ // Only allow app switching to be resumed if activity is not a restricted background
+ // activity and target app is not home process, otherwise any background activity
+ // started in background task can stop home button protection mode.
+ // As the targeted app is not a home process and we don't need to wait for the 2nd
+ // activity to be started to resume app switching, we can just enable app switching
+ // directly.
+ WindowProcessController homeProcess = mService.mHomeProcess;
+ boolean isHomeProcess = homeProcess != null
+ && aInfo.applicationInfo.uid == homeProcess.mUid;
+ if (!restrictedBgActivity && !isHomeProcess) {
+ mService.resumeAppSwitches();
}
- if (mService.getBalAppSwitchesProtectionEnabled()) {
- // Only allow app switching to be resumed if activity is not a restricted background
- // activity and target app is not home process, otherwise any background activity
- // started in background task can stop home button protection mode.
- // As the targeted app is not a home process and we don't need to wait for the 2nd
- // activity to be started to resume app switching, we can just enable app switching
- // directly.
- WindowProcessController homeProcess = mService.mHomeProcess;
- boolean isHomeProcess = homeProcess != null
- && aInfo.applicationInfo.uid == homeProcess.mUid;
- if (!restrictedBgActivity && !isHomeProcess) {
- mService.resumeAppSwitches();
- }
- } else {
- mService.onStartActivitySetDidAppSwitch();
- }
-
- mController.doPendingActivityLaunches(false);
-
mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
request.voiceInteractor, startFlags, true /* doResume */, checkedOptions, inTask,
restrictedBgActivity, intentGrants);
@@ -1286,8 +1262,6 @@
return false;
}
- // App switching will be allowed if BAL app switching flag is not enabled, or if
- // its app switching rule allows it.
// This is used to block background activity launch even if the app is still
// visible to user after user clicking home button.
final boolean appSwitchAllowed = mService.getBalAppSwitchesAllowed();
@@ -1438,7 +1412,6 @@
Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage
+ "; callingUid: " + callingUid
+ "; appSwitchAllowed: " + appSwitchAllowed
- + "; balAppSwitchEnabled: " + mService.getBalAppSwitchesProtectionEnabled()
+ "; isCallingUidForeground: " + isCallingUidForeground
+ "; callingUidHasAnyVisibleWindow: " + callingUidHasAnyVisibleWindow
+ "; callingUidProcState: " + DebugUtils.valueToString(ActivityManager.class,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 80add64..5610573 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -202,7 +202,6 @@
import android.os.WorkSource;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
-import android.provider.DeviceConfig;
import android.provider.Settings;
import android.service.dreams.DreamActivity;
import android.service.voice.IVoiceInteractionSession;
@@ -324,12 +323,6 @@
/** This activity is being relaunched due to a free-resize operation. */
public static final int RELAUNCH_REASON_FREE_RESIZE = 2;
- /**
- * Apps are blocked from starting activities in the foreground after the user presses home.
- */
- public static final String BLOCK_ACTIVITY_STARTS_AFTER_HOME_FLAG =
- "am_block_activity_starts_after_home";
-
Context mContext;
/**
@@ -386,7 +379,6 @@
volatile WindowProcessController mHeavyWeightProcess;
boolean mHasHeavyWeightFeature;
boolean mHasLeanbackFeature;
- boolean mBlockActivityAfterHomeEnabled;
/** The process of the top most activity. */
volatile WindowProcessController mTopApp;
/**
@@ -490,20 +482,11 @@
/** Temporary to avoid allocations. */
final StringBuilder mStringBuilder = new StringBuilder(256);
- // Amount of time after a call to stopAppSwitches() during which we will
- // prevent further untrusted switches from happening.
- private static final long APP_SWITCH_DELAY_TIME = 5 * 1000;
-
/**
- * The time at which we will allow normal application switches again,
- * after a call to {@link #stopAppSwitches()}.
+ * Whether normal application switches are allowed; a call to {@link #stopAppSwitches()
+ * disables this.
*/
- private long mAppSwitchesAllowedTime;
- /**
- * This is set to true after the first switch after mAppSwitchesAllowedTime
- * is set; any switches after that will clear the time.
- */
- private boolean mDidAppSwitch;
+ private boolean mAppSwitchesAllowed = true;
/**
* Last stop app switches time, apps finished before this time cannot start background activity
@@ -749,9 +732,6 @@
mRecentTasks.onSystemReadyLocked();
mTaskSupervisor.onSystemReady();
mActivityClientController.onSystemReady();
- mBlockActivityAfterHomeEnabled = DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- BLOCK_ACTIVITY_STARTS_AFTER_HOME_FLAG, true);
}
}
@@ -1146,7 +1126,7 @@
if (topFocusedRootTask != null && topFocusedRootTask.getResumedActivity() != null
&& topFocusedRootTask.getResumedActivity().info.applicationInfo.uid
== Binder.getCallingUid()) {
- mAppSwitchesAllowedTime = 0;
+ mAppSwitchesAllowed = true;
}
}
return pir.sendInner(0, fillInIntent, resolvedType, allowlistToken, null, null,
@@ -2002,10 +1982,7 @@
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
assertPackageMatchesCallingUid(callingPackage);
- if (!checkAppSwitchAllowedLocked(callingPid, callingUid, -1, -1, "Task to front")) {
- SafeActivityOptions.abort(options);
- return;
- }
+
final long origId = Binder.clearCallingIdentity();
WindowProcessController callerApp = null;
if (appThread != null) {
@@ -2086,76 +2063,10 @@
}
/**
- * Return true if app switch protection will be handled by background activity launch logic.
- */
- boolean getBalAppSwitchesProtectionEnabled() {
- return mBlockActivityAfterHomeEnabled;
- }
-
- /**
* Return true if app switching is allowed.
*/
boolean getBalAppSwitchesAllowed() {
- if (getBalAppSwitchesProtectionEnabled()) {
- // Apps no longer able to start BAL again until app switching is resumed.
- return mAppSwitchesAllowedTime == 0;
- } else {
- // Legacy behavior, BAL logic won't block app switching.
- return true;
- }
- }
-
- boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid,
- int callingPid, int callingUid, String name) {
-
- // Background activity launch logic replaces app switching protection, so allow
- // apps to start activity here now.
- if (getBalAppSwitchesProtectionEnabled()) {
- return true;
- }
-
- if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) {
- return true;
- }
-
- if (getRecentTasks().isCallerRecents(sourceUid)) {
- return true;
- }
-
- int perm = checkComponentPermission(STOP_APP_SWITCHES, sourcePid, sourceUid, -1, true);
- if (perm == PackageManager.PERMISSION_GRANTED) {
- return true;
- }
- if (checkAllowAppSwitchUid(sourceUid)) {
- return true;
- }
-
- // If the actual IPC caller is different from the logical source, then
- // also see if they are allowed to control app switches.
- if (callingUid != -1 && callingUid != sourceUid) {
- perm = checkComponentPermission(STOP_APP_SWITCHES, callingPid, callingUid, -1, true);
- if (perm == PackageManager.PERMISSION_GRANTED) {
- return true;
- }
- if (checkAllowAppSwitchUid(callingUid)) {
- return true;
- }
- }
-
- Slog.w(TAG, name + " request from " + sourceUid + " stopped");
- return false;
- }
-
- private boolean checkAllowAppSwitchUid(int uid) {
- ArrayMap<String, Integer> types = mAllowAppSwitchUids.get(UserHandle.getUserId(uid));
- if (types != null) {
- for (int i = types.size() - 1; i >= 0; i--) {
- if (types.valueAt(i).intValue() == uid) {
- return true;
- }
- }
- }
- return false;
+ return mAppSwitchesAllowed;
}
@Override
@@ -3663,13 +3574,8 @@
public void stopAppSwitches() {
enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "stopAppSwitches");
synchronized (mGlobalLock) {
- mAppSwitchesAllowedTime = SystemClock.uptimeMillis() + APP_SWITCH_DELAY_TIME;
+ mAppSwitchesAllowed = false;
mLastStopAppSwitchesTime = SystemClock.uptimeMillis();
- mDidAppSwitch = false;
- // If BAL app switching enabled, app switches are blocked not delayed.
- if (!getBalAppSwitchesProtectionEnabled()) {
- getActivityStartController().schedulePendingActivityLaunches(APP_SWITCH_DELAY_TIME);
- }
}
}
@@ -3677,10 +3583,7 @@
public void resumeAppSwitches() {
enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "resumeAppSwitches");
synchronized (mGlobalLock) {
- // Note that we don't execute any pending app switches... we will
- // let those wait until either the timeout, or the next start
- // activity request.
- mAppSwitchesAllowedTime = 0;
+ mAppSwitchesAllowed = true;
}
}
@@ -3688,19 +3591,6 @@
return mLastStopAppSwitchesTime;
}
- void onStartActivitySetDidAppSwitch() {
- if (mDidAppSwitch) {
- // This is the second allowed switch since we stopped switches, so now just generally
- // allow switches. Use case:
- // - user presses home (switches disabled, switch to home, mDidAppSwitch now true);
- // - user taps a home icon (coming from home so allowed, we hit here and now allow
- // anyone to switch again).
- mAppSwitchesAllowedTime = 0;
- } else {
- mDidAppSwitch = true;
- }
- }
-
/** @return whether the system should disable UI modes incompatible with VR mode. */
boolean shouldDisableNonVrUiLocked() {
return mVrController.shouldDisableNonVrUiLocked();
@@ -5822,15 +5712,12 @@
int userId) {
synchronized (mGlobalLock) {
- boolean didSomething =
- getActivityStartController().clearPendingActivityLaunches(packageName);
- didSomething |= mRootWindowContainer.finishDisabledPackageActivities(packageName,
+ return mRootWindowContainer.finishDisabledPackageActivities(packageName,
null /* filterByClasses */, doit, evenPersistent, userId,
// Only remove the activities without process because the activities with
// attached process will be removed when handling process died with
// WindowProcessController#isRemoved == true.
true /* onlyRemoveNoProcess */);
- return didSomething;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 0ad392b..de43643 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -142,7 +142,6 @@
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.am.ActivityManagerService;
import com.android.server.am.UserState;
-import com.android.server.uri.NeededUriGrants;
import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
import java.io.FileDescriptor;
@@ -376,41 +375,6 @@
private boolean mInitialized;
- /**
- * Description of a request to start a new activity, which has been held
- * due to app switches being disabled.
- */
- static class PendingActivityLaunch {
- final ActivityRecord r;
- final ActivityRecord sourceRecord;
- final int startFlags;
- final Task rootTask;
- final WindowProcessController callerApp;
- final NeededUriGrants intentGrants;
-
- PendingActivityLaunch(ActivityRecord r, ActivityRecord sourceRecord,
- int startFlags, Task rootTask, WindowProcessController callerApp,
- NeededUriGrants intentGrants) {
- this.r = r;
- this.sourceRecord = sourceRecord;
- this.startFlags = startFlags;
- this.rootTask = rootTask;
- this.callerApp = callerApp;
- this.intentGrants = intentGrants;
- }
-
- void sendErrorResult(String message) {
- try {
- if (callerApp != null && callerApp.hasThread()) {
- callerApp.getThread().scheduleCrash(message);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Exception scheduling crash of failed "
- + "activity launcher sourceRecord=" + sourceRecord, e);
- }
- }
- }
-
public ActivityTaskSupervisor(ActivityTaskManagerService service, Looper looper) {
mService = service;
mLooper = looper;
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index e6b7585..7f0adca 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -101,10 +101,6 @@
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mService.mGlobalLock) {
- if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, -1, -1,
- "Move to front")) {
- return;
- }
WindowProcessController callerApp = null;
if (appThread != null) {
callerApp = mService.getProcessController(appThread);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ce4e5ec..bbf6c76 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -68,7 +68,6 @@
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.RootWindowContainerProto.IS_HOME_RECENTS_COMPONENT;
import static com.android.server.wm.RootWindowContainerProto.KEYGUARD_CONTROLLER;
-import static com.android.server.wm.RootWindowContainerProto.PENDING_ACTIVITIES;
import static com.android.server.wm.RootWindowContainerProto.WINDOW_CONTAINER;
import static com.android.server.wm.Task.ActivityState.FINISHING;
import static com.android.server.wm.Task.ActivityState.PAUSED;
@@ -1289,7 +1288,6 @@
mTaskSupervisor.getKeyguardController().dumpDebug(proto, KEYGUARD_CONTROLLER);
proto.write(IS_HOME_RECENTS_COMPONENT,
mTaskSupervisor.mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
- mService.getActivityStartController().dumpDebug(proto, PENDING_ACTIVITIES);
proto.end(token);
}
diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp
index d1918d8..bdccf45 100644
--- a/services/core/xsd/Android.bp
+++ b/services/core/xsd/Android.bp
@@ -8,11 +8,19 @@
xsd_config {
name: "platform-compat-config",
- srcs: ["platform-compat-config.xsd"],
- api_dir: "platform-compat-schema",
+ srcs: ["platform-compat/config/platform-compat-config.xsd"],
+ api_dir: "platform-compat/config/schema",
package_name: "com.android.server.compat.config",
}
+xsd_config {
+ name: "platform-compat-overrides",
+ srcs: ["platform-compat/overrides/platform-compat-overrides.xsd"],
+ api_dir: "platform-compat/overrides/schema",
+ package_name: "com.android.server.compat.overrides",
+ gen_writer: true,
+}
+
xsd_config {
name: "display-device-config",
diff --git a/services/core/xsd/platform-compat-schema/OWNERS b/services/core/xsd/platform-compat/OWNERS
similarity index 100%
rename from services/core/xsd/platform-compat-schema/OWNERS
rename to services/core/xsd/platform-compat/OWNERS
diff --git a/services/core/xsd/platform-compat-config.xsd b/services/core/xsd/platform-compat/config/platform-compat-config.xsd
similarity index 100%
rename from services/core/xsd/platform-compat-config.xsd
rename to services/core/xsd/platform-compat/config/platform-compat-config.xsd
diff --git a/services/core/xsd/platform-compat-schema/current.txt b/services/core/xsd/platform-compat/config/schema/current.txt
similarity index 100%
rename from services/core/xsd/platform-compat-schema/current.txt
rename to services/core/xsd/platform-compat/config/schema/current.txt
diff --git a/services/core/xsd/platform-compat-schema/last_current.txt b/services/core/xsd/platform-compat/config/schema/last_current.txt
similarity index 100%
rename from services/core/xsd/platform-compat-schema/last_current.txt
rename to services/core/xsd/platform-compat/config/schema/last_current.txt
diff --git a/services/core/xsd/platform-compat-schema/last_removed.txt b/services/core/xsd/platform-compat/config/schema/last_removed.txt
similarity index 100%
rename from services/core/xsd/platform-compat-schema/last_removed.txt
rename to services/core/xsd/platform-compat/config/schema/last_removed.txt
diff --git a/services/core/xsd/platform-compat-schema/removed.txt b/services/core/xsd/platform-compat/config/schema/removed.txt
similarity index 100%
rename from services/core/xsd/platform-compat-schema/removed.txt
rename to services/core/xsd/platform-compat/config/schema/removed.txt
diff --git a/services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd b/services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd
new file mode 100644
index 0000000..e27e1b8
--- /dev/null
+++ b/services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- This defines the format of the XML file used to store compat config overrides in
+ ~ /data/misc/appcompat/compat_framework_overrides.xml
+-->
+<xs:schema version="2.0" elementFormDefault="qualified"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+
+ <xs:complexType name="override-value">
+ <xs:attribute type="xs:string" name="packageName" use="required" />
+ <xs:attribute type="xs:boolean" name="enabled" use="required" />
+ </xs:complexType>
+
+ <xs:complexType name="change-overrides">
+ <xs:attribute type="xs:long" name="changeId" use="required"/>
+ <xs:element name="validated">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="override-value" type="override-value" maxOccurs="unbounded" minOccurs="0" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="deferred">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="override-value" type="override-value" maxOccurs="unbounded" minOccurs="0" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xs:complexType>
+
+ <xs:element name="overrides">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="change-overrides" type="change-overrides" maxOccurs="unbounded" minOccurs="0" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+</xs:schema>
diff --git a/services/core/xsd/platform-compat/overrides/schema/current.txt b/services/core/xsd/platform-compat/overrides/schema/current.txt
new file mode 100644
index 0000000..08b8207
--- /dev/null
+++ b/services/core/xsd/platform-compat/overrides/schema/current.txt
@@ -0,0 +1,51 @@
+// Signature format: 2.0
+package com.android.server.compat.overrides {
+
+ public class ChangeOverrides {
+ ctor public ChangeOverrides();
+ method public long getChangeId();
+ method public com.android.server.compat.overrides.ChangeOverrides.Deferred getDeferred();
+ method public com.android.server.compat.overrides.ChangeOverrides.Validated getValidated();
+ method public void setChangeId(long);
+ method public void setDeferred(com.android.server.compat.overrides.ChangeOverrides.Deferred);
+ method public void setValidated(com.android.server.compat.overrides.ChangeOverrides.Validated);
+ }
+
+ public static class ChangeOverrides.Deferred {
+ ctor public ChangeOverrides.Deferred();
+ method public java.util.List<com.android.server.compat.overrides.OverrideValue> getOverrideValue();
+ }
+
+ public static class ChangeOverrides.Validated {
+ ctor public ChangeOverrides.Validated();
+ method public java.util.List<com.android.server.compat.overrides.OverrideValue> getOverrideValue();
+ }
+
+ public class OverrideValue {
+ ctor public OverrideValue();
+ method public boolean getEnabled();
+ method public String getPackageName();
+ method public void setEnabled(boolean);
+ method public void setPackageName(String);
+ }
+
+ public class Overrides {
+ ctor public Overrides();
+ method public java.util.List<com.android.server.compat.overrides.ChangeOverrides> getChangeOverrides();
+ }
+
+ public class XmlParser {
+ ctor public XmlParser();
+ method public static com.android.server.compat.overrides.Overrides read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ }
+
+ public class XmlWriter implements java.io.Closeable {
+ ctor public XmlWriter(java.io.PrintWriter);
+ method public void close();
+ method public static void write(com.android.server.compat.overrides.XmlWriter, com.android.server.compat.overrides.Overrides) throws java.io.IOException;
+ }
+
+}
+
diff --git a/services/core/xsd/platform-compat-schema/last_current.txt b/services/core/xsd/platform-compat/overrides/schema/last_current.txt
similarity index 100%
copy from services/core/xsd/platform-compat-schema/last_current.txt
copy to services/core/xsd/platform-compat/overrides/schema/last_current.txt
diff --git a/services/core/xsd/platform-compat-schema/last_removed.txt b/services/core/xsd/platform-compat/overrides/schema/last_removed.txt
similarity index 100%
copy from services/core/xsd/platform-compat-schema/last_removed.txt
copy to services/core/xsd/platform-compat/overrides/schema/last_removed.txt
diff --git a/services/core/xsd/platform-compat-schema/removed.txt b/services/core/xsd/platform-compat/overrides/schema/removed.txt
similarity index 100%
copy from services/core/xsd/platform-compat-schema/removed.txt
copy to services/core/xsd/platform-compat/overrides/schema/removed.txt
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index ad2bc65..12595af 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -103,6 +103,7 @@
import com.android.internal.widget.ILockSettings;
import com.android.server.am.ActivityManagerService;
import com.android.server.appbinding.AppBindingService;
+import com.android.server.apphibernation.AppHibernationService;
import com.android.server.attention.AttentionManagerService;
import com.android.server.audio.AudioService;
import com.android.server.biometrics.AuthService;
@@ -236,6 +237,8 @@
"com.android.server.appwidget.AppWidgetService";
private static final String VOICE_RECOGNITION_MANAGER_SERVICE_CLASS =
"com.android.server.voiceinteraction.VoiceInteractionManagerService";
+ private static final String APP_HIBERNATION_SERVICE_CLASS =
+ "com.android.server.apphibernation.AppHibernationService";
private static final String PRINT_MANAGER_SERVICE_CLASS =
"com.android.server.print.PrintManagerService";
private static final String COMPANION_DEVICE_MANAGER_SERVICE_CLASS =
@@ -495,7 +498,7 @@
}
try {
- Thread.sleep(checkInterval);
+ Thread.sleep(checkInterval * 1000);
} catch (InterruptedException ex) {
continue;
}
@@ -2047,6 +2050,12 @@
mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
t.traceEnd();
+ if (AppHibernationService.isAppHibernationEnabled()) {
+ t.traceBegin("StartAppHibernationService");
+ mSystemServiceManager.startService(APP_HIBERNATION_SERVICE_CLASS);
+ t.traceEnd();
+ }
+
if (GestureLauncherService.isGestureLauncherEnabled(context.getResources())) {
t.traceBegin("StartGestureLauncher");
mSystemServiceManager.startService(GestureLauncherService.class);
diff --git a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
index a691a8d..607fb47 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
@@ -271,8 +271,7 @@
verify(mMockIActivityManager).registerUidObserver(
uidObserverArgumentCaptor.capture(),
eq(ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE
- | ActivityManager.UID_OBSERVER_ACTIVE
- | ActivityManager.UID_OBSERVER_PROCSTATE),
+ | ActivityManager.UID_OBSERVER_ACTIVE),
eq(ActivityManager.PROCESS_STATE_UNKNOWN),
isNull());
verify(mMockIAppOpsService).startWatchingMode(
@@ -650,11 +649,6 @@
assertFalse(instance.isUidActiveSynced(UID_2));
assertTrue(instance.isUidActiveSynced(Process.SYSTEM_UID));
- assertFalse(instance.isUidInForeground(UID_1));
- assertFalse(instance.isUidInForeground(UID_2));
- assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
-
-
mIUidObserver.onUidStateChanged(UID_2,
ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE, 0,
ActivityManager.PROCESS_CAPABILITY_NONE);
@@ -670,11 +664,6 @@
assertFalse(instance.isUidActiveSynced(UID_2));
assertTrue(instance.isUidActiveSynced(Process.SYSTEM_UID));
- assertFalse(instance.isUidInForeground(UID_1));
- assertTrue(instance.isUidInForeground(UID_2));
- assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
-
-
mIUidObserver.onUidStateChanged(UID_1,
ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0,
ActivityManager.PROCESS_CAPABILITY_NONE);
@@ -686,10 +675,6 @@
assertFalse(instance.isUidActive(UID_2));
assertTrue(instance.isUidActive(Process.SYSTEM_UID));
- assertTrue(instance.isUidInForeground(UID_1));
- assertTrue(instance.isUidInForeground(UID_2));
- assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
-
mIUidObserver.onUidGone(UID_1, true);
waitUntilMainHandlerDrain();
@@ -699,10 +684,6 @@
assertFalse(instance.isUidActive(UID_2));
assertTrue(instance.isUidActive(Process.SYSTEM_UID));
- assertFalse(instance.isUidInForeground(UID_1));
- assertTrue(instance.isUidInForeground(UID_2));
- assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
-
mIUidObserver.onUidIdle(UID_2, true);
waitUntilMainHandlerDrain();
@@ -712,10 +693,6 @@
assertFalse(instance.isUidActive(UID_2));
assertTrue(instance.isUidActive(Process.SYSTEM_UID));
- assertFalse(instance.isUidInForeground(UID_1));
- assertFalse(instance.isUidInForeground(UID_2));
- assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
-
mIUidObserver.onUidStateChanged(UID_1,
ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0,
ActivityManager.PROCESS_CAPABILITY_NONE);
@@ -727,10 +704,6 @@
assertFalse(instance.isUidActive(UID_2));
assertTrue(instance.isUidActive(Process.SYSTEM_UID));
- assertTrue(instance.isUidInForeground(UID_1));
- assertFalse(instance.isUidInForeground(UID_2));
- assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
-
mIUidObserver.onUidStateChanged(UID_1,
ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0,
ActivityManager.PROCESS_CAPABILITY_NONE);
@@ -746,10 +719,6 @@
assertFalse(instance.isUidActiveSynced(UID_2));
assertTrue(instance.isUidActiveSynced(Process.SYSTEM_UID));
- assertFalse(instance.isUidInForeground(UID_1));
- assertFalse(instance.isUidInForeground(UID_2));
- assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
-
// The result from AMI.isUidActive() only affects isUidActiveSynced().
when(mMockIActivityManagerInternal.isUidActive(anyInt())).thenReturn(true);
@@ -760,11 +729,6 @@
assertTrue(instance.isUidActiveSynced(UID_1));
assertTrue(instance.isUidActiveSynced(UID_2));
assertTrue(instance.isUidActiveSynced(Process.SYSTEM_UID));
-
- assertFalse(instance.isUidInForeground(UID_1));
- assertFalse(instance.isUidInForeground(UID_2));
- assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
-
}
@Test
@@ -1480,7 +1444,6 @@
callStart(instance);
instance.mActiveUids.put(UID_1, true);
- instance.mForegroundUids.put(UID_2, true);
instance.mRunAnyRestrictedPackages.add(Pair.create(UID_1, PACKAGE_1));
instance.mExemptedBucketPackages.add(UserHandle.getUserId(UID_2), PACKAGE_2);
@@ -1493,7 +1456,6 @@
mReceiver.onReceive(mMockContext, packageRemoved);
assertEquals(1, instance.mActiveUids.size());
- assertEquals(1, instance.mForegroundUids.size());
assertEquals(1, instance.mRunAnyRestrictedPackages.size());
assertEquals(1, instance.mExemptedBucketPackages.size());
@@ -1506,7 +1468,6 @@
mReceiver.onReceive(mMockContext, packageRemoved);
assertEquals(1, instance.mActiveUids.size());
- assertEquals(1, instance.mForegroundUids.size());
assertEquals(1, instance.mRunAnyRestrictedPackages.size());
assertEquals(1, instance.mExemptedBucketPackages.size());
@@ -1518,7 +1479,6 @@
mReceiver.onReceive(mMockContext, packageRemoved);
assertEquals(0, instance.mActiveUids.size());
- assertEquals(1, instance.mForegroundUids.size());
assertEquals(0, instance.mRunAnyRestrictedPackages.size());
assertEquals(1, instance.mExemptedBucketPackages.size());
@@ -1530,7 +1490,6 @@
mReceiver.onReceive(mMockContext, packageRemoved);
assertEquals(0, instance.mActiveUids.size());
- assertEquals(0, instance.mForegroundUids.size());
assertEquals(0, instance.mRunAnyRestrictedPackages.size());
assertEquals(0, instance.mExemptedBucketPackages.size());
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 7a970a1..1254df9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -44,14 +44,15 @@
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.APP_STANDBY_BUCKET_CHANGED;
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.CHARGING_STATUS_CHANGED;
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_FOR_CANCELED;
-import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_LONG_TIME;
-import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_SHORT_TIME;
+import static com.android.server.alarm.AlarmManagerService.Constants.ALLOW_WHILE_IDLE_WINDOW;
+import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_QUOTA;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LAZY_BATCHING;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LISTENER_TIMEOUT;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MAX_INTERVAL;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_FUTURITY;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_INTERVAL;
+import static com.android.server.alarm.AlarmManagerService.FREQUENT_INDEX;
import static com.android.server.alarm.AlarmManagerService.INDEFINITE_DELAY;
import static com.android.server.alarm.AlarmManagerService.IS_WAKEUP_MASK;
import static com.android.server.alarm.AlarmManagerService.TIME_CHANGED_MASK;
@@ -409,6 +410,12 @@
return mockPi;
}
+ private void setDeviceConfigInt(String key, int val) {
+ mDeviceConfigKeys.add(key);
+ doReturn(val).when(mDeviceConfigProperties).getInt(eq(key), anyInt());
+ mService.mConstants.onPropertiesChanged(mDeviceConfigProperties);
+ }
+
private void setDeviceConfigLong(String key, long val) {
mDeviceConfigKeys.add(key);
doReturn(val).when(mDeviceConfigProperties).getLong(eq(key), anyLong());
@@ -430,10 +437,12 @@
setDeviceConfigLong(KEY_MIN_INTERVAL, 0);
mDeviceConfigKeys.add(mService.mConstants.KEYS_APP_STANDBY_QUOTAS[ACTIVE_INDEX]);
mDeviceConfigKeys.add(mService.mConstants.KEYS_APP_STANDBY_QUOTAS[WORKING_INDEX]);
- doReturn(8).when(mDeviceConfigProperties)
+ doReturn(50).when(mDeviceConfigProperties)
.getInt(eq(mService.mConstants.KEYS_APP_STANDBY_QUOTAS[ACTIVE_INDEX]), anyInt());
- doReturn(5).when(mDeviceConfigProperties)
+ doReturn(35).when(mDeviceConfigProperties)
.getInt(eq(mService.mConstants.KEYS_APP_STANDBY_QUOTAS[WORKING_INDEX]), anyInt());
+ doReturn(20).when(mDeviceConfigProperties)
+ .getInt(eq(mService.mConstants.KEYS_APP_STANDBY_QUOTAS[FREQUENT_INDEX]), anyInt());
mService.mConstants.onPropertiesChanged(mDeviceConfigProperties);
}
@@ -496,15 +505,13 @@
setDeviceConfigLong(KEY_MIN_FUTURITY, 5);
setDeviceConfigLong(KEY_MIN_INTERVAL, 10);
setDeviceConfigLong(KEY_MAX_INTERVAL, 15);
- setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_SHORT_TIME, 20);
- setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_LONG_TIME, 25);
+ setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_QUOTA, 20);
setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION, 30);
setDeviceConfigLong(KEY_LISTENER_TIMEOUT, 35);
assertEquals(5, mService.mConstants.MIN_FUTURITY);
assertEquals(10, mService.mConstants.MIN_INTERVAL);
assertEquals(15, mService.mConstants.MAX_INTERVAL);
- assertEquals(20, mService.mConstants.ALLOW_WHILE_IDLE_SHORT_TIME);
- assertEquals(25, mService.mConstants.ALLOW_WHILE_IDLE_LONG_TIME);
+ assertEquals(20, mService.mConstants.ALLOW_WHILE_IDLE_QUOTA);
assertEquals(30, mService.mConstants.ALLOW_WHILE_IDLE_WHITELIST_DURATION);
assertEquals(35, mService.mConstants.LISTENER_TIMEOUT);
}
@@ -1301,62 +1308,54 @@
public void allowWhileIdleAlarmsWhileDeviceIdle() throws Exception {
doReturn(0).when(mService).fuzzForDuration(anyLong());
- final long awiDelayForTest = 23;
- setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_LONG_TIME, awiDelayForTest);
-
- setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 1000,
+ setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + ALLOW_WHILE_IDLE_WINDOW + 1000,
getNewMockPendingIntent());
assertNotNull(mService.mPendingIdleUntil);
- final long seedTrigger = mNowElapsedTest + 3;
- final int numAlarms = 10;
- final PendingIntent[] pis = new PendingIntent[numAlarms];
- for (int i = 0; i < numAlarms; i++) {
- pis[i] = getNewMockPendingIntent();
- setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, seedTrigger + i * i, pis[i], false);
- }
-
- long lastAwiDispatch = -1;
- int i = 0;
- while (i < numAlarms) {
- final long nextDispatch = (lastAwiDispatch >= 0) ? (lastAwiDispatch + awiDelayForTest)
- : (seedTrigger + i * i);
- assertEquals("Wrong allow-while-idle dispatch", nextDispatch, mTestTimer.getElapsed());
-
- mNowElapsedTest = nextDispatch;
+ final int quota = mService.mConstants.ALLOW_WHILE_IDLE_QUOTA;
+ final long firstTrigger = mNowElapsedTest + 10;
+ for (int i = 0; i < quota; i++) {
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
+ getNewMockPendingIntent(), false);
+ mNowElapsedTest = mTestTimer.getElapsed();
mTestTimer.expire();
-
- while (i < numAlarms && (seedTrigger + i * i) <= nextDispatch) {
- verify(pis[i]).send(eq(mMockContext), eq(0), any(Intent.class), any(),
- any(Handler.class), isNull(), any());
- i++;
- }
- Log.d(TAG, "Dispatched alarms upto " + i + " at " + nextDispatch);
- lastAwiDispatch = nextDispatch;
}
+ // This one should get deferred on set.
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
+ getNewMockPendingIntent(), false);
+ final long expectedNextTrigger = firstTrigger + ALLOW_WHILE_IDLE_WINDOW;
+ assertEquals("Incorrect trigger when no quota left", expectedNextTrigger,
+ mTestTimer.getElapsed());
+
+ // Bring the idle until alarm back.
+ setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, expectedNextTrigger - 50,
+ getNewMockPendingIntent());
+ assertEquals(expectedNextTrigger - 50, mService.mPendingIdleUntil.getWhenElapsed());
+ assertEquals(expectedNextTrigger - 50, mTestTimer.getElapsed());
}
@Test
- public void allowWhileIdleUnrestrictedInIdle() throws Exception {
+ public void allowWhileIdleUnrestricted() throws Exception {
doReturn(0).when(mService).fuzzForDuration(anyLong());
- final long awiDelayForTest = 127;
- setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_LONG_TIME, awiDelayForTest);
- setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_SHORT_TIME, 0);
-
+ // Both battery saver and doze are on.
setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 1000,
getNewMockPendingIntent());
assertNotNull(mService.mPendingIdleUntil);
- final long seedTrigger = mNowElapsedTest + 3;
- for (int i = 1; i <= 5; i++) {
- setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, seedTrigger + i * i,
+ when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
+ TEST_CALLING_PACKAGE)).thenReturn(true);
+
+ final int numAlarms = mService.mConstants.ALLOW_WHILE_IDLE_QUOTA + 100;
+ final long firstTrigger = mNowElapsedTest + 10;
+ for (int i = 0; i < numAlarms; i++) {
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
getNewMockPendingIntent(), true);
}
- for (int i = 1; i <= 5; i++) {
- final long nextTrigger = mTestTimer.getElapsed();
- assertEquals("Wrong trigger for alarm " + i, seedTrigger + i * i, nextTrigger);
- mNowElapsedTest = nextTrigger;
+ // All of them should fire as expected.
+ for (int i = 0; i < numAlarms; i++) {
+ mNowElapsedTest = mTestTimer.getElapsed();
+ assertEquals("Incorrect trigger at i=" + i, firstTrigger + i, mNowElapsedTest);
mTestTimer.expire();
}
}
@@ -1427,9 +1426,10 @@
verify(mAppStateTracker).addListener(listenerArgumentCaptor.capture());
final AppStateTrackerImpl.Listener listener = listenerArgumentCaptor.getValue();
- final PendingIntent alarmPi = getNewMockPendingIntent();
when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
TEST_CALLING_PACKAGE)).thenReturn(true);
+
+ final PendingIntent alarmPi = getNewMockPendingIntent();
setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 7, alarmPi);
assertEquals(mNowElapsedTest + INDEFINITE_DELAY, mTestTimer.getElapsed());
@@ -1446,61 +1446,64 @@
@Test
public void allowWhileIdleAlarmsInBatterySaver() throws Exception {
- final ArgumentCaptor<AppStateTrackerImpl.Listener> listenerArgumentCaptor =
- ArgumentCaptor.forClass(AppStateTrackerImpl.Listener.class);
- verify(mAppStateTracker).addListener(listenerArgumentCaptor.capture());
- final AppStateTrackerImpl.Listener listener = listenerArgumentCaptor.getValue();
-
- final long longDelay = 23;
- final long shortDelay = 7;
- setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_LONG_TIME, longDelay);
- setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_SHORT_TIME, shortDelay);
-
when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
TEST_CALLING_PACKAGE)).thenReturn(true);
- setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 1,
+ when(mAppStateTracker.isForceAllAppsStandbyEnabled()).thenReturn(true);
+
+ final int quota = mService.mConstants.ALLOW_WHILE_IDLE_QUOTA;
+ long firstTrigger = mNowElapsedTest + 10;
+ for (int i = 0; i < quota; i++) {
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
+ getNewMockPendingIntent(), false);
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+ }
+ // This one should get deferred on set.
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
getNewMockPendingIntent(), false);
- setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 2,
+ long expectedNextTrigger = firstTrigger + ALLOW_WHILE_IDLE_WINDOW;
+ assertEquals("Incorrect trigger when no quota available", expectedNextTrigger,
+ mTestTimer.getElapsed());
+
+ // Refresh the state
+ mService.removeLocked(TEST_CALLING_UID);
+ mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
+
+ firstTrigger = mNowElapsedTest + 10;
+ for (int i = 0; i < quota; i++) {
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
+ getNewMockPendingIntent(), false);
+ }
+ // This one should get deferred after the latest alarm expires.
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
getNewMockPendingIntent(), false);
+ for (int i = 0; i < quota; i++) {
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+ }
+ expectedNextTrigger = firstTrigger + ALLOW_WHILE_IDLE_WINDOW;
+ assertEquals("Incorrect trigger when no quota available", expectedNextTrigger,
+ mTestTimer.getElapsed());
- assertEquals(mNowElapsedTest + 1, mTestTimer.getElapsed());
+ // Refresh the state
+ mService.removeLocked(TEST_CALLING_UID);
+ mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
- mNowElapsedTest += 1;
- mTestTimer.expire();
-
- assertEquals(mNowElapsedTest + longDelay, mTestTimer.getElapsed());
- listener.onUidForeground(TEST_CALLING_UID, true);
- // The next alarm should be deferred by shortDelay.
- assertEquals(mNowElapsedTest + shortDelay, mTestTimer.getElapsed());
-
- mNowElapsedTest = mTestTimer.getElapsed();
- setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 1,
+ firstTrigger = mNowElapsedTest + 10;
+ for (int i = 0; i < quota; i++) {
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
+ getNewMockPendingIntent(), false);
+ }
+ // This delivery time maintains the quota invariant. Should not be deferred.
+ expectedNextTrigger = firstTrigger + ALLOW_WHILE_IDLE_WINDOW + 5;
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, expectedNextTrigger,
getNewMockPendingIntent(), false);
-
- when(mAppStateTracker.isUidInForeground(TEST_CALLING_UID)).thenReturn(true);
- mTestTimer.expire();
- // The next alarm should be deferred by shortDelay again.
- assertEquals(mNowElapsedTest + shortDelay, mTestTimer.getElapsed());
-
- mNowElapsedTest = mTestTimer.getElapsed();
- setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 1,
- getNewMockPendingIntent(), true);
- when(mAppStateTracker.isUidInForeground(TEST_CALLING_UID)).thenReturn(false);
- mTestTimer.expire();
- final long lastAwiDispatch = mNowElapsedTest;
- // Unrestricted, so should not be changed.
- assertEquals(mNowElapsedTest + 1, mTestTimer.getElapsed());
-
- mNowElapsedTest = mTestTimer.getElapsed();
- // AWI_unrestricted should not affect normal AWI bookkeeping.
- // The next alarm is after the short delay but before the long delay.
- setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, lastAwiDispatch + shortDelay + 1,
- getNewMockPendingIntent(), false);
- mTestTimer.expire();
- assertEquals(lastAwiDispatch + longDelay, mTestTimer.getElapsed());
-
- listener.onUidForeground(TEST_CALLING_UID, true);
- assertEquals(lastAwiDispatch + shortDelay + 1, mTestTimer.getElapsed());
+ for (int i = 0; i < quota; i++) {
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+ }
+ assertEquals("Incorrect trigger when no quota available", expectedNextTrigger,
+ mTestTimer.getElapsed());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
new file mode 100644
index 0000000..d0370b6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -0,0 +1,168 @@
+/*
+ * 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.apphibernation;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.AdditionalAnswers.returnsArgAt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.internal.verification.VerificationModeFactory.times;
+
+import android.app.IActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.UserInfo;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.os.UserManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.SystemService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for {@link com.android.server.apphibernation.AppHibernationService}
+ */
+@SmallTest
+public final class AppHibernationServiceTest {
+ private static final String PACKAGE_SCHEME = "package";
+ private static final String PACKAGE_NAME_1 = "package1";
+ private static final String PACKAGE_NAME_2 = "package2";
+ private static final int USER_ID_1 = 1;
+ private static final int USER_ID_2 = 2;
+
+ private AppHibernationService mAppHibernationService;
+ private BroadcastReceiver mBroadcastReceiver;
+ @Mock
+ private Context mContext;
+ @Mock
+ private IPackageManager mIPackageManager;
+ @Mock
+ private IActivityManager mIActivityManager;
+ @Mock
+ private UserManager mUserManager;
+ @Captor
+ private ArgumentCaptor<BroadcastReceiver> mReceiverCaptor;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
+
+ mAppHibernationService = new AppHibernationService(mContext, mIPackageManager,
+ mIActivityManager, mUserManager);
+
+ verify(mContext, times(2)).registerReceiver(mReceiverCaptor.capture(), any());
+ mBroadcastReceiver = mReceiverCaptor.getValue();
+
+ List<UserInfo> userList = new ArrayList<>();
+ userList.add(new UserInfo(USER_ID_1, "user 1", 0 /* flags */));
+ doReturn(userList).when(mUserManager).getUsers();
+
+ List<PackageInfo> userPackages = new ArrayList<>();
+ userPackages.add(makePackageInfo(PACKAGE_NAME_1));
+
+ doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
+ .getInstalledPackages(anyInt(), eq(USER_ID_1));
+
+ doAnswer(returnsArgAt(2)).when(mIActivityManager).handleIncomingUser(anyInt(), anyInt(),
+ anyInt(), anyBoolean(), anyBoolean(), any(), any());
+
+ mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ }
+
+ @Test
+ public void testSetHibernating_packageIsHibernating() {
+ // WHEN we hibernate a package for a user
+ mAppHibernationService.setHibernating(PACKAGE_NAME_1, USER_ID_1, true);
+
+ // THEN the package is marked hibernating for the user
+ assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_1, USER_ID_1));
+ }
+
+ @Test
+ public void testSetHibernating_newPackageAdded_packageIsHibernating() {
+ // WHEN a new package is added and it is hibernated
+ Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED,
+ Uri.fromParts(PACKAGE_SCHEME, PACKAGE_NAME_2, null /* fragment */));
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_1);
+ mBroadcastReceiver.onReceive(mContext, intent);
+
+ mAppHibernationService.setHibernating(PACKAGE_NAME_2, USER_ID_1, true);
+
+ // THEN the new package is hibernated
+ assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_2, USER_ID_1));
+ }
+
+ @Test
+ public void testSetHibernating_newUserAdded_packageIsHibernating() throws RemoteException {
+ // WHEN a new user is added and a package from the user is hibernated
+ List<PackageInfo> userPackages = new ArrayList<>();
+ userPackages.add(makePackageInfo(PACKAGE_NAME_1));
+ doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
+ .getInstalledPackages(anyInt(), eq(USER_ID_2));
+ Intent intent = new Intent(Intent.ACTION_USER_ADDED);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_2);
+ mBroadcastReceiver.onReceive(mContext, intent);
+
+ mAppHibernationService.setHibernating(PACKAGE_NAME_1, USER_ID_2, true);
+
+ // THEN the new user's package is hibernated
+ assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_1, USER_ID_2));
+ }
+
+ @Test
+ public void testIsHibernating_packageReplaced_stillReturnsHibernating() {
+ // GIVEN a package is currently hibernated
+ mAppHibernationService.setHibernating(PACKAGE_NAME_1, USER_ID_1, true);
+
+ // WHEN the package is removed but marked as replacing
+ Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED,
+ Uri.fromParts(PACKAGE_SCHEME, PACKAGE_NAME_2, null /* fragment */));
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_1);
+ intent.putExtra(Intent.EXTRA_REPLACING, true);
+ mBroadcastReceiver.onReceive(mContext, intent);
+
+ // THEN the package is still hibernating
+ assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_1, USER_ID_1));
+ }
+
+ private static PackageInfo makePackageInfo(String packageName) {
+ PackageInfo pkg = new PackageInfo();
+ pkg.packageName = packageName;
+ return pkg;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index 90ce6cb..57b0d01 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -33,6 +33,7 @@
import android.hardware.biometrics.IBiometricService;
import android.os.Binder;
import android.os.IBinder;
+import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import androidx.annotation.NonNull;
@@ -267,6 +268,39 @@
assertEquals(0, bsp.recentOperations.length);
}
+ @Test
+ public void testCancelPendingAuth() throws RemoteException {
+ final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
+
+ final TestClientMonitor client1 = new TestClientMonitor(mContext, mToken, lazyDaemon);
+ final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
+ final TestAuthenticationClient client2 = new TestAuthenticationClient(mContext, lazyDaemon,
+ mToken, callback);
+
+ // Add a non-cancellable client, then add the auth client
+ mScheduler.scheduleClientMonitor(client1);
+ mScheduler.scheduleClientMonitor(client2);
+ waitForIdle();
+
+ assertEquals(mScheduler.getCurrentClient(), client1);
+ assertEquals(Operation.STATE_WAITING_IN_QUEUE,
+ mScheduler.mPendingOperations.getFirst().mState);
+
+ // Request cancel before the authentication client has started
+ mScheduler.cancelAuthentication(mToken);
+ waitForIdle();
+ assertEquals(Operation.STATE_WAITING_IN_QUEUE_CANCELING,
+ mScheduler.mPendingOperations.getFirst().mState);
+
+ // Finish the blocking client. The authentication client should send ERROR_CANCELED
+ client1.getCallback().onClientFinished(client1, true /* success */);
+ waitForIdle();
+ verify(callback).onError(anyInt(), anyInt(),
+ eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED),
+ eq(0) /* vendorCode */);
+ assertNull(mScheduler.getCurrentClient());
+ }
+
private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception {
return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer));
}
@@ -293,6 +327,29 @@
}
}
+ private static class TestAuthenticationClient extends AuthenticationClient<Object> {
+
+ public TestAuthenticationClient(@NonNull Context context,
+ @NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter listener) {
+ super(context, lazyDaemon, token, listener, 0 /* targetUserId */, 0 /* operationId */,
+ false /* restricted */, TAG, 1 /* cookie */, false /* requireConfirmation */,
+ TEST_SENSOR_ID, true /* isStrongBiometric */, 0 /* statsModality */,
+ 0 /* statsClient */, null /* taskStackListener */, mock(LockoutTracker.class),
+ false /* isKeyguard */);
+ }
+
+ @Override
+ protected void stopHalOperation() {
+
+ }
+
+ @Override
+ protected void startHalOperation() {
+
+ }
+ }
+
private static class TestClientMonitor2 extends TestClientMonitor {
private final int mProtoEnum;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
index 61cc8e6..904ade8 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
@@ -19,17 +19,20 @@
import static junit.framework.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.os.Handler;
import android.os.Looper;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
+import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -80,7 +83,7 @@
.thenReturn(5);
mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
- mFingerprint21 = new Fingerprint21(mContext, mScheduler,
+ mFingerprint21 = new TestableFingerprint21(mContext, mScheduler,
new Handler(Looper.getMainLooper()), SENSOR_ID,
BiometricManager.Authenticators.BIOMETRIC_WEAK, mLockoutResetDispatcher,
mHalResultController);
@@ -100,4 +103,21 @@
waitForIdle();
verify(mScheduler).reset();
}
+
+ private static class TestableFingerprint21 extends Fingerprint21 {
+
+ TestableFingerprint21(@NonNull Context context,
+ @NonNull BiometricScheduler scheduler,
+ @NonNull Handler handler, int sensorId, int strength,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull HalResultController controller) {
+ super(context, scheduler, handler, sensorId, strength, lockoutResetDispatcher,
+ controller);
+ }
+
+ @Override
+ synchronized IBiometricsFingerprint getDaemon() {
+ return mock(IBiometricsFingerprint.class);
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index ac8dc34..a53ff9b 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -44,6 +44,8 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.UUID;
@RunWith(AndroidJUnit4.class)
@@ -69,6 +71,10 @@
os.close();
}
+ private String readFile(File file) throws IOException {
+ return new String(Files.readAllBytes(Paths.get(file.toURI())));
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -499,4 +505,86 @@
assertThat(compatConfig.isChangeEnabled(1236L,
ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue();
}
+
+ @Test
+ public void testSaveOverrides() throws Exception {
+ File overridesFile = new File(createTempDir(), "overrides.xml");
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1L)
+ .addEnableSinceSdkChangeWithId(2, 2L)
+ .build();
+ compatConfig.forceNonDebuggableFinalForTest(true);
+ compatConfig.initOverrides(overridesFile);
+ when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create()
+ .withPackageName("foo.bar")
+ .debuggable()
+ .build());
+ when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt()))
+ .thenThrow(new NameNotFoundException());
+
+ compatConfig.addOverride(1L, "foo.bar", true);
+ compatConfig.addOverride(2L, "bar.baz", false);
+
+ assertThat(readFile(overridesFile)).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<overrides>\n"
+ + " <change-overrides changeId=\"1\">\n"
+ + " <validated>\n"
+ + " <override-value packageName=\"foo.bar\" enabled=\"true\">\n"
+ + " </override-value>\n"
+ + " </validated>\n"
+ + " <deferred>\n"
+ + " </deferred>\n"
+ + " </change-overrides>\n"
+ + " <change-overrides changeId=\"2\">\n"
+ + " <validated>\n"
+ + " </validated>\n"
+ + " <deferred>\n"
+ + " <override-value packageName=\"bar.baz\" enabled=\"false\">\n"
+ + " </override-value>\n"
+ + " </deferred>\n"
+ + " </change-overrides>\n"
+ + "</overrides>\n");
+ }
+
+ @Test
+ public void testLoadOverrides() throws Exception {
+ File tempDir = createTempDir();
+ File overridesFile = new File(tempDir, "overrides.xml");
+ // Change 1 is enabled for foo.bar (validated)
+ // Change 2 is disabled for bar.baz (deferred)
+ String xmlData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ + "<overrides>"
+ + "<change-overrides changeId=\"1\">"
+ + "<deferred/>"
+ + "<validated>"
+ + "<override-value packageName=\"foo.bar\" enabled=\"true\"/>"
+ + "</validated>"
+ + "</change-overrides>"
+ + "<change-overrides changeId=\"2\">"
+ + "<deferred>"
+ + "<override-value packageName=\"bar.baz\" enabled=\"false\"/>"
+ + "</deferred>"
+ + "<validated/>"
+ + "</change-overrides>"
+ + "</overrides>";
+ writeToFile(tempDir, "overrides.xml", xmlData);
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1L)
+ .addEnableSinceSdkChangeWithId(2, 2L)
+ .build();
+ compatConfig.forceNonDebuggableFinalForTest(true);
+ compatConfig.initOverrides(overridesFile);
+ ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+ .withPackageName("foo.bar")
+ .debuggable()
+ .build();
+ when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt()))
+ .thenReturn(applicationInfo);
+ when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt()))
+ .thenThrow(new NameNotFoundException());
+
+ assertThat(compatConfig.isChangeEnabled(1L, applicationInfo)).isTrue();
+ assertThat(compatConfig.willChangeBeEnabled(2L, "bar.baz")).isFalse();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
index a408d4c..137bd88 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
@@ -113,6 +113,15 @@
+ " </allowed-values>"
+ " <default-value int-value=\"1\" />"
+ " </setting>"
+ + " <setting name=\"tv_send_standby_on_sleep\""
+ + " value-type=\"int\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value int-value=\"0\" />"
+ + " <value int-value=\"1\" />"
+ + " </allowed-values>"
+ + " <default-value int-value=\"1\" />"
+ + " </setting>"
+ " <setting name=\"rc_profile_tv\""
+ " value-type=\"int\""
+ " user-configurable=\"false\">"
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 95a0a74..eedbc95 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -596,6 +596,15 @@
}
@Test
+ public void setArcStatus() {
+ mHdmiCecLocalDeviceAudioSystem.setArcStatus(true);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isTrue();
+
+ mHdmiCecLocalDeviceAudioSystem.setArcStatus(false);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
+ }
+
+ @Test
@Ignore("b/151150320")
public void handleSystemAudioModeRequest_fromNonTV_tVNotSupport() {
HdmiCecMessage message =
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 882d2f5..e6b56ca 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -35,7 +35,6 @@
import android.os.PowerManager;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
-import android.provider.Settings.Global;
import android.sysprop.HdmiProperties;
import android.view.KeyEvent;
@@ -133,7 +132,6 @@
}
};
- mHdmiControlService.writeBooleanSetting(Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, true);
mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlService);
mHdmiCecLocalDevicePlayback.init();
mHdmiControlService.setIoLooper(mMyLooper);
@@ -560,7 +558,9 @@
HdmiControlManager.POWER_CONTROL_MODE_TV);
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
"HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true);
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
@@ -580,7 +580,9 @@
HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
"HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true);
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
@@ -600,7 +602,9 @@
HdmiControlManager.POWER_CONTROL_MODE_NONE);
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
"HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true);
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
@@ -620,7 +624,9 @@
HdmiControlManager.POWER_CONTROL_MODE_TV);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true);
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
@@ -640,7 +646,9 @@
HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true);
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
@@ -660,7 +668,9 @@
HdmiControlManager.POWER_CONTROL_MODE_NONE);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true);
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index ec806fab..0f527f3 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -15,6 +15,7 @@
*/
package com.android.server.hdmi;
+import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
@@ -269,6 +270,30 @@
}
@Test
+ public void tvSendStandbyOnSleep_Enabled() {
+ mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
+ mTestLooper.dispatchAll();
+ mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
+ mTestLooper.dispatchAll();
+ HdmiCecMessage standby = HdmiCecMessageBuilder.buildStandby(ADDR_TV, ADDR_BROADCAST);
+ assertThat(mNativeWrapper.getResultMessages()).contains(standby);
+ }
+
+ @Test
+ public void tvSendStandbyOnSleep_Disabled() {
+ mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_DISABLED);
+ mTestLooper.dispatchAll();
+ mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
+ mTestLooper.dispatchAll();
+ HdmiCecMessage standby = HdmiCecMessageBuilder.buildStandby(ADDR_TV, ADDR_BROADCAST);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standby);
+ }
+
+ @Test
public void getRcFeatures() {
ArrayList<Integer> features = new ArrayList<>(mHdmiCecLocalDeviceTv.getRcFeatures());
assertThat(features.contains(Constants.RC_PROFILE_TV_NONE)).isTrue();
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 4db7ce2..df19aeb 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -2051,6 +2051,7 @@
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_IFACE);
final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+ networkCapabilities.setSSID(TEST_SSID);
return new NetworkState(info, prop, networkCapabilities, null, null, TEST_SSID);
}
diff --git a/services/tests/shortcutmanagerutils/OWNERS b/services/tests/shortcutmanagerutils/OWNERS
new file mode 100644
index 0000000..d825dfd
--- /dev/null
+++ b/services/tests/shortcutmanagerutils/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/pm/OWNERS
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
index ce96771..db241de 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
@@ -16,9 +16,6 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -26,22 +23,17 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import android.app.IApplicationThread;
import android.content.Intent;
-import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import com.android.server.wm.ActivityStarter.Factory;
-import com.android.server.wm.ActivityTaskSupervisor.PendingActivityLaunch;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.Random;
-
/**
* Tests for the {@link ActivityStartController} class.
*
@@ -66,36 +58,6 @@
}
/**
- * Ensures that pending launches are processed.
- */
- @Test
- public void testPendingActivityLaunches() {
- final Random random = new Random();
-
- final ActivityRecord activity = new ActivityBuilder(mAtm).build();
- final ActivityRecord source = new ActivityBuilder(mAtm)
- .setCreateTask(true)
- .build();
- final int startFlags = random.nextInt();
- final Task rootTask = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final WindowProcessController wpc = new WindowProcessController(mAtm,
- mAtm.mContext.getApplicationInfo(), "name", 12345,
- UserHandle.getUserId(12345), mock(Object.class),
- mock(WindowProcessListener.class));
- wpc.setThread(mock(IApplicationThread.class));
-
- mController.addPendingActivityLaunch(
- new PendingActivityLaunch(activity, source, startFlags, rootTask, wpc, null));
- final boolean resume = random.nextBoolean();
- mController.doPendingActivityLaunches(resume);
-
- verify(mStarter, times(1)).startResolvedActivity(eq(activity), eq(source), eq(null),
- eq(null), eq(startFlags), eq(resume), eq(null), eq(null), eq(null));
- }
-
-
- /**
* Ensures instances are recycled after execution.
*/
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 4bfc837..36adf28 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -27,7 +27,6 @@
import static android.app.ActivityManager.START_PERMISSION_DENIED;
import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
import static android.app.ActivityManager.START_SUCCESS;
-import static android.app.ActivityManager.START_SWITCHES_CANCELED;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -119,7 +118,6 @@
private static final int PRECONDITION_DIFFERENT_UID = 1 << 7;
private static final int PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION = 1 << 8;
private static final int PRECONDITION_CANNOT_START_ANY_ACTIVITY = 1 << 9;
- private static final int PRECONDITION_DISALLOW_APP_SWITCHING = 1 << 10;
private static final int FAKE_CALLING_UID = 666;
private static final int FAKE_REAL_CALLING_UID = 667;
@@ -153,8 +151,6 @@
| PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION,
START_NOT_VOICE_COMPATIBLE);
verifyStartActivityPreconditions(PRECONDITION_CANNOT_START_ANY_ACTIVITY, START_ABORTED);
- verifyStartActivityPreconditions(PRECONDITION_DISALLOW_APP_SWITCHING,
- START_SWITCHES_CANCELED);
}
private static boolean containsConditions(int preconditions, int mask) {
@@ -244,11 +240,6 @@
intent.setComponent(source.mActivityComponent);
}
- if (containsConditions(preconditions, PRECONDITION_DISALLOW_APP_SWITCHING)) {
- doReturn(false).when(service).checkAppSwitchAllowedLocked(
- anyInt(), anyInt(), anyInt(), anyInt(), any());
- }
-
if (containsConditions(preconditions, PRECONDITION_CANNOT_START_ANY_ACTIVITY)) {
doReturn(false).when(service.mTaskSupervisor).checkStartAnyActivityPermission(
any(), any(), any(), anyInt(), anyInt(), anyInt(), any(), any(),
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 4f44a06..7215cd5 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4715,6 +4715,12 @@
"default_rtt_mode_int";
/**
+ * Indicates whether RTT is supported while roaming.
+ */
+ public static final String KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL =
+ "rtt_supported_while_roaming_bool";
+
+ /**
* Indicates if auto-configuration server is used for the RCS config
* Reference: GSMA RCC.14
*/
@@ -5090,6 +5096,7 @@
sDefaults.putBoolean(KEY_RTT_SUPPORTED_BOOL, false);
sDefaults.putBoolean(KEY_TTY_SUPPORTED_BOOL, true);
sDefaults.putBoolean(KEY_HIDE_TTY_HCO_VCO_WITH_RTT_BOOL, false);
+ sDefaults.putBoolean(KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL, false);
sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_NO_REPLY_TIMER_FOR_CFNRY_BOOL, true);
sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null);
diff --git a/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatCommandNotInstalledTest.kt b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatCommandNotInstalledTest.kt
index e9227e94..eb04f69 100644
--- a/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatCommandNotInstalledTest.kt
+++ b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatCommandNotInstalledTest.kt
@@ -131,6 +131,10 @@
assertThat(platformCompat.isChangeEnabled(TEST_CHANGE_ID, appInfo)).isEqualTo(params.result)
}
- private fun command(command: String) =
- FileReader(uiAutomation.executeShellCommand(command).fileDescriptor).readText()
+ private fun command(command: String): String {
+ val fileDescriptor = uiAutomation.executeShellCommand(command)
+ return String(ParcelFileDescriptor.AutoCloseInputStream(fileDescriptor).use {
+ inputStream -> inputStream.readBytes()
+ })
+ }
}
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index 16c4865..083c8c8 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -38,6 +38,7 @@
import android.os.ConditionVariable
import android.os.IBinder
import android.os.INetworkManagementService
+import android.os.UserHandle
import android.testing.TestableContext
import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -55,10 +56,13 @@
import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.AdditionalAnswers
import org.mockito.Mock
import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
import org.mockito.MockitoAnnotations
@@ -143,7 +147,10 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- doNothing().`when`(context).sendStickyBroadcastAsUser(any(), any(), any())
+ val asUserCtx = mock(Context::class.java, AdditionalAnswers.delegatesTo<Context>(context))
+ doReturn(UserHandle.ALL).`when`(asUserCtx).user
+ doReturn(asUserCtx).`when`(context).createContextAsUser(eq(UserHandle.ALL), anyInt())
+ doNothing().`when`(context).sendStickyBroadcast(any(), any())
networkStackClient = TestNetworkStackClient(realContext)
networkStackClient.init()
diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt
index 9ba56e4..91fcbc0 100644
--- a/tests/net/java/android/net/NetworkTemplateTest.kt
+++ b/tests/net/java/android/net/NetworkTemplateTest.kt
@@ -67,6 +67,7 @@
val caps = NetworkCapabilities().apply {
setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false)
setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true)
+ setSSID(ssid)
}
return NetworkState(info, lp, caps, mock(Network::class.java), subscriberId, ssid)
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c5e6c35..b3092b9 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -201,6 +201,7 @@
import android.net.UidRange;
import android.net.UidRangeParcel;
import android.net.Uri;
+import android.net.VpnInfo;
import android.net.VpnManager;
import android.net.metrics.IpConnectivityLog;
import android.net.shared.NetworkMonitorUtils;
@@ -245,7 +246,6 @@
import com.android.internal.app.IBatteryStats;
import com.android.internal.net.VpnConfig;
-import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.WakeupMessage;
@@ -8323,8 +8323,7 @@
assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid);
mMockVpn.setVpnType(vpnType);
- final VpnInfo vpnInfo = new VpnInfo();
- vpnInfo.ownerUid = vpnOwnerUid;
+ final VpnInfo vpnInfo = new VpnInfo(vpnOwnerUid, null, null);
mMockVpn.setVpnInfo(vpnInfo);
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
index 3aafe0b..1b33930e 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
@@ -33,8 +33,7 @@
import static org.junit.Assert.assertEquals;
import android.net.NetworkStats;
-
-import com.android.internal.net.VpnInfo;
+import android.net.VpnInfo;
/** Superclass with utilities for NetworkStats(Service|Factory)Test */
abstract class NetworkStatsBaseTest {
@@ -113,10 +112,6 @@
}
static VpnInfo createVpnInfo(String vpnIface, String[] underlyingIfaces) {
- VpnInfo info = new VpnInfo();
- info.ownerUid = UID_VPN;
- info.vpnIface = vpnIface;
- info.underlyingIfaces = underlyingIfaces;
- return info;
+ return new VpnInfo(UID_VPN, vpnIface, underlyingIfaces);
}
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
index e4996d9..76647a6 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -36,13 +36,13 @@
import android.content.res.Resources;
import android.net.NetworkStats;
import android.net.TrafficStats;
+import android.net.VpnInfo;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.tests.net.R;
-import com.android.internal.net.VpnInfo;
import libcore.io.IoUtils;
import libcore.io.Streams;
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index c783629..b4e37de 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -21,7 +21,6 @@
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI;
-import static android.net.ConnectivityManager.TYPE_WIMAX;
import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
@@ -44,6 +43,7 @@
import static android.net.NetworkTemplate.NETWORK_TYPE_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateMobileWithRatType;
+import static android.net.NetworkTemplate.buildTemplateWifi;
import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.net.TrafficStats.UID_REMOVED;
@@ -86,6 +86,7 @@
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
+import android.net.VpnInfo;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.os.ConditionVariable;
import android.os.Handler;
@@ -104,7 +105,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
@@ -146,7 +146,7 @@
private static final String IMSI_2 = "310260";
private static final String TEST_SSID = "AndroidAP";
- private static NetworkTemplate sTemplateWifi = buildTemplateWifiWildcard();
+ private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_SSID);
private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1);
private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2);
@@ -291,7 +291,6 @@
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
-
// modify some number on wifi, and trigger poll event
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
@@ -567,61 +566,6 @@
}
@Test
- public void testUid3gWimaxCombinedByTemplate() throws Exception {
- // pretend that network comes online
- expectDefaultSettings();
- NetworkState[] states = new NetworkState[] {buildMobile3gState(IMSI_1)};
- expectNetworkStatsSummary(buildEmptyStats());
- expectNetworkStatsUidDetail(buildEmptyStats());
-
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
-
- // create some traffic
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectDefaultSettings();
- expectNetworkStatsSummary(buildEmptyStats());
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
- mService.incrementOperationCount(UID_RED, 0xF00D, 5);
-
- forcePollAndWaitForIdle();
-
- // verify service recorded history
- assertUidTotal(sTemplateImsi1, UID_RED, 1024L, 8L, 1024L, 8L, 5);
-
-
- // now switch over to wimax network
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectDefaultSettings();
- states = new NetworkState[] {buildWimaxState(TEST_IFACE2)};
- expectNetworkStatsSummary(buildEmptyStats());
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
-
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
- forcePollAndWaitForIdle();
-
-
- // create traffic on second network
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectDefaultSettings();
- expectNetworkStatsSummary(buildEmptyStats());
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
- .insertEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
- .insertEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L));
- mService.incrementOperationCount(UID_RED, 0xFAAD, 5);
-
- forcePollAndWaitForIdle();
-
- // verify that ALL_MOBILE template combines both
- assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 12L, 1280L, 10L, 10);
- }
-
- @Test
public void testMobileStatsByRatType() throws Exception {
final NetworkTemplate template3g =
buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS);
@@ -1503,6 +1447,7 @@
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !isMetered);
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+ capabilities.setSSID(TEST_SSID);
return new NetworkState(info, prop, capabilities, WIFI_NETWORK, null, TEST_SSID);
}
@@ -1524,17 +1469,6 @@
return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, subscriberId, null);
}
- private static NetworkState buildWimaxState(@NonNull String iface) {
- final NetworkInfo info = new NetworkInfo(TYPE_WIMAX, 0, null, null);
- info.setDetailedState(DetailedState.CONNECTED, null, null);
- final LinkProperties prop = new LinkProperties();
- prop.setInterfaceName(iface);
- final NetworkCapabilities capabilities = new NetworkCapabilities();
- capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
- capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
- return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, null, null);
- }
-
private NetworkStats buildEmptyStats() {
return new NetworkStats(getElapsedRealtime(), 0);
}
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index dfd0c8a..86a1591 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -28,6 +28,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@@ -39,6 +40,12 @@
NetworkCapabilities.NET_CAPABILITY_INTERNET, NetworkCapabilities.NET_CAPABILITY_MMS
};
public static final int[] UNDERLYING_CAPS = new int[] {NetworkCapabilities.NET_CAPABILITY_DUN};
+
+ static {
+ Arrays.sort(EXPOSED_CAPS);
+ Arrays.sort(UNDERLYING_CAPS);
+ }
+
public static final long[] RETRY_INTERVALS_MS =
new long[] {
TimeUnit.SECONDS.toMillis(5),
@@ -124,12 +131,13 @@
public void testBuilderAndGetters() {
final VcnGatewayConnectionConfig config = buildTestConfig();
- for (int cap : EXPOSED_CAPS) {
- config.hasExposedCapability(cap);
- }
- for (int cap : UNDERLYING_CAPS) {
- config.requiresUnderlyingCapability(cap);
- }
+ int[] exposedCaps = config.getExposedCapabilities();
+ Arrays.sort(exposedCaps);
+ assertArrayEquals(EXPOSED_CAPS, exposedCaps);
+
+ int[] underlyingCaps = config.getRequiredUnderlyingCapabilities();
+ Arrays.sort(underlyingCaps);
+ assertArrayEquals(UNDERLYING_CAPS, underlyingCaps);
assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMs());
assertEquals(MAX_MTU, config.getMaxMtu());
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 29cfdb6f..f0cdde3 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -18,6 +18,7 @@
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
+import static com.android.server.vcn.VcnTestUtils.setupSystemService;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -138,11 +139,16 @@
private final IBinder mMockIBinder = mock(IBinder.class);
public VcnManagementServiceTest() throws Exception {
- setupSystemService(mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
- setupSystemService(mTelMgr, Context.TELEPHONY_SERVICE, TelephonyManager.class);
setupSystemService(
- mSubMgr, Context.TELEPHONY_SUBSCRIPTION_SERVICE, SubscriptionManager.class);
- setupSystemService(mAppOpsMgr, Context.APP_OPS_SERVICE, AppOpsManager.class);
+ mMockContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+ setupSystemService(
+ mMockContext, mTelMgr, Context.TELEPHONY_SERVICE, TelephonyManager.class);
+ setupSystemService(
+ mMockContext,
+ mSubMgr,
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE,
+ SubscriptionManager.class);
+ setupSystemService(mMockContext, mAppOpsMgr, Context.APP_OPS_SERVICE, AppOpsManager.class);
doReturn(TEST_PACKAGE_NAME).when(mMockContext).getOpPackageName();
@@ -186,11 +192,6 @@
mTestLooper.dispatchAll();
}
- private void setupSystemService(Object service, String name, Class<?> serviceClass) {
- doReturn(name).when(mMockContext).getSystemServiceName(serviceClass);
- doReturn(service).when(mMockContext).getSystemService(name);
- }
-
private void setupMockedCarrierPrivilege(boolean isPrivileged) {
doReturn(Collections.singletonList(TEST_SUBSCRIPTION_INFO))
.when(mSubMgr)
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
new file mode 100644
index 0000000..4ecd215
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.server.vcn;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for VcnGatewayConnection.DisconnectedState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnectionTestBase {
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mGatewayConnection.transitionTo(mGatewayConnection.mDisconnectedState);
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void testEnterWhileNotRunningTriggersQuit() throws Exception {
+ final VcnGatewayConnection vgc =
+ new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps);
+
+ vgc.setIsRunning(false);
+ vgc.transitionTo(vgc.mDisconnectedState);
+ mTestLooper.dispatchAll();
+
+ assertNull(vgc.getCurrentState());
+ }
+
+ @Test
+ public void testNetworkChangesTriggerStateTransitions() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testNullNetworkDoesNotTriggerStateTransition() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(null);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testTeardown() throws Exception {
+ mGatewayConnection.teardownAsynchronously();
+ mTestLooper.dispatchAll();
+
+ assertNull(mGatewayConnection.getCurrentState());
+ verify(mIpSecSvc).deleteTunnelInterface(eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), any());
+ }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
new file mode 100644
index 0000000..d0fec55
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.server.vcn;
+
+import static com.android.server.vcn.VcnGatewayConnection.TEARDOWN_TIMEOUT_SECONDS;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+
+/** Tests for VcnGatewayConnection.DisconnectedState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnectionTestBase {
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mGatewayConnection.setIkeSession(mGatewayConnection.buildIkeSession());
+
+ mGatewayConnection.transitionTo(mGatewayConnection.mDisconnectingState);
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void testIkeSessionClosed() throws Exception {
+ getIkeSessionCallback().onClosed();
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testTimeoutExpired() throws Exception {
+ mTestLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS));
+ mTestLooper.dispatchAll();
+
+ verify(mMockIkeSession).kill();
+ }
+
+ @Test
+ public void testTeardown() throws Exception {
+ mGatewayConnection.teardownAsynchronously();
+ mTestLooper.dispatchAll();
+
+ // Should do nothing; already tearing down.
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
new file mode 100644
index 0000000..3467859
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -0,0 +1,120 @@
+/*
+ * 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.server.vcn;
+
+import static com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
+import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+import static com.android.server.vcn.VcnTestUtils.setupIpSecManager;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.net.IpSecManager;
+import android.net.IpSecTunnelInterfaceResponse;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.vcn.VcnGatewayConnectionConfig;
+import android.net.vcn.VcnGatewayConnectionConfigTest;
+import android.os.ParcelUuid;
+import android.os.test.TestLooper;
+
+import com.android.server.IpSecService;
+
+import org.junit.Before;
+import org.mockito.ArgumentCaptor;
+
+import java.util.UUID;
+
+public class VcnGatewayConnectionTestBase {
+ protected static final ParcelUuid TEST_SUB_GRP = new ParcelUuid(UUID.randomUUID());
+ protected static final int TEST_IPSEC_TUNNEL_RESOURCE_ID = 1;
+ protected static final String TEST_IPSEC_TUNNEL_IFACE = "IPSEC_IFACE";
+ protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 =
+ new UnderlyingNetworkRecord(
+ new Network(0),
+ new NetworkCapabilities(),
+ new LinkProperties(),
+ false /* blocked */);
+ protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_2 =
+ new UnderlyingNetworkRecord(
+ new Network(1),
+ new NetworkCapabilities(),
+ new LinkProperties(),
+ false /* blocked */);
+
+ @NonNull protected final Context mContext;
+ @NonNull protected final TestLooper mTestLooper;
+ @NonNull protected final VcnNetworkProvider mVcnNetworkProvider;
+ @NonNull protected final VcnContext mVcnContext;
+ @NonNull protected final VcnGatewayConnectionConfig mConfig;
+ @NonNull protected final VcnGatewayConnection.Dependencies mDeps;
+ @NonNull protected final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
+
+ @NonNull protected final IpSecService mIpSecSvc;
+
+ protected VcnIkeSession mMockIkeSession;
+ protected VcnGatewayConnection mGatewayConnection;
+
+ public VcnGatewayConnectionTestBase() {
+ mContext = mock(Context.class);
+ mTestLooper = new TestLooper();
+ mVcnNetworkProvider = mock(VcnNetworkProvider.class);
+ mVcnContext = mock(VcnContext.class);
+ mConfig = VcnGatewayConnectionConfigTest.buildTestConfig();
+ mDeps = mock(VcnGatewayConnection.Dependencies.class);
+ mUnderlyingNetworkTracker = mock(UnderlyingNetworkTracker.class);
+
+ mIpSecSvc = mock(IpSecService.class);
+ setupIpSecManager(mContext, mIpSecSvc);
+
+ doReturn(mContext).when(mVcnContext).getContext();
+ doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
+ doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
+
+ doReturn(mUnderlyingNetworkTracker)
+ .when(mDeps)
+ .newUnderlyingNetworkTracker(any(), any(), any());
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ IpSecTunnelInterfaceResponse resp =
+ new IpSecTunnelInterfaceResponse(
+ IpSecManager.Status.OK,
+ TEST_IPSEC_TUNNEL_RESOURCE_ID,
+ TEST_IPSEC_TUNNEL_IFACE);
+ doReturn(resp).when(mIpSecSvc).createTunnelInterface(any(), any(), any(), any(), any());
+
+ mMockIkeSession = mock(VcnIkeSession.class);
+ doReturn(mMockIkeSession).when(mDeps).newIkeSession(any(), any(), any(), any(), any());
+
+ mGatewayConnection = new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps);
+ }
+
+ protected IkeSessionCallback getIkeSessionCallback() {
+ ArgumentCaptor<IkeSessionCallback> captor =
+ ArgumentCaptor.forClass(IkeSessionCallback.class);
+ verify(mDeps).newIkeSession(any(), any(), any(), captor.capture(), any());
+ return captor.getValue();
+ }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTestUtils.java b/tests/vcn/java/com/android/server/vcn/VcnTestUtils.java
new file mode 100644
index 0000000..2b10806
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnTestUtils.java
@@ -0,0 +1,44 @@
+/*
+ * 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.server.vcn;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.content.Context;
+import android.net.IpSecManager;
+
+import com.android.server.IpSecService;
+
+public class VcnTestUtils {
+ /** Mock system services by directly mocking the *Manager interface. */
+ public static void setupSystemService(
+ Context mockContext, Object service, String name, Class<?> serviceClass) {
+ doReturn(name).when(mockContext).getSystemServiceName(serviceClass);
+ doReturn(service).when(mockContext).getSystemService(name);
+ }
+
+ /** Mock IpSecService by mocking the underlying service binder. */
+ public static IpSecManager setupIpSecManager(Context mockContext, IpSecService service) {
+ doReturn(Context.IPSEC_SERVICE).when(mockContext).getSystemServiceName(IpSecManager.class);
+
+ final IpSecManager ipSecMgr = new IpSecManager(mockContext, service);
+ doReturn(ipSecMgr).when(mockContext).getSystemService(Context.IPSEC_SERVICE);
+
+ // Return to ensure this doesn't get reaped.
+ return ipSecMgr;
+ }
+}