Merge "Log when enforceActor fails" into sc-dev
diff --git a/apex/jobscheduler/framework/Android.bp b/apex/jobscheduler/framework/Android.bp
index 6650e67..fd35537 100644
--- a/apex/jobscheduler/framework/Android.bp
+++ b/apex/jobscheduler/framework/Android.bp
@@ -22,6 +22,7 @@
],
},
libs: [
+ "app-compat-annotations",
"framework-minus-apex",
"unsupportedappusage",
],
diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
index 7851087..7c7b210 100644
--- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java
+++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
@@ -16,11 +16,14 @@
package android.app;
+import android.Manifest;
import android.annotation.IntDef;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
@@ -183,6 +186,25 @@
@UnsupportedAppUsage
public static final int FLAG_IDLE_UNTIL = 1<<4;
+ /**
+ * Flag for alarms: Used to provide backwards compatibility for apps with targetSdkVersion less
+ * than {@link Build.VERSION_CODES#S}
+ * @hide
+ */
+ public static final int FLAG_ALLOW_WHILE_IDLE_COMPAT = 1 << 5;
+
+ /**
+ * For apps targeting {@link Build.VERSION_CODES#S} or above, APIs
+ * {@link #setExactAndAllowWhileIdle(int, long, PendingIntent)} and
+ * {@link #setAlarmClock(AlarmClockInfo, PendingIntent)} will require holding a new
+ * permission {@link android.Manifest.permission#SCHEDULE_EXACT_ALARM}
+ *
+ * @hide
+ */
+ @ChangeId
+ @Disabled // TODO (b/171306433): Enable starting S.
+ public static final long REQUIRE_EXACT_ALARM_PERMISSION = 171306433L;
+
@UnsupportedAppUsage
private final IAlarmManager mService;
private final Context mContext;
@@ -588,6 +610,11 @@
* This method is like {@link #setExact(int, long, PendingIntent)}, but implies
* {@link #RTC_WAKEUP}.
*
+ * <p>
+ * Starting from API {@link Build.VERSION_CODES#S}, using this method requires the
+ * {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission. Alarms scheduled via this API
+ * will be allowed to start a foreground service even if the app is in the background.
+ *
* @param info
* @param operation Action to perform when the alarm goes off;
* typically comes from {@link PendingIntent#getBroadcast
@@ -603,6 +630,7 @@
* @see android.content.Context#registerReceiver
* @see android.content.Intent#filterEquals
*/
+ @RequiresPermission(Manifest.permission.SCHEDULE_EXACT_ALARM)
public void setAlarmClock(AlarmClockInfo info, PendingIntent operation) {
setImpl(RTC_WAKEUP, info.getTriggerTime(), WINDOW_EXACT, 0, 0, operation,
null, null, null, null, info);
@@ -876,6 +904,12 @@
* device is idle it may take even more liberties with scheduling in order to optimize
* for battery life.</p>
*
+ * <p>
+ * Starting from API {@link Build.VERSION_CODES#S}, using this method requires the
+ * {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission, unless the app is exempt from
+ * battery restrictions. Alarms scheduled via this API will be allowed to start a foreground
+ * service even if the app is in the background.
+ *
* @param type type of alarm.
* @param triggerAtMillis time in milliseconds that the alarm should go
* off, using the appropriate clock (depending on the alarm type).
@@ -895,6 +929,7 @@
* @see #RTC
* @see #RTC_WAKEUP
*/
+ @RequiresPermission(value = Manifest.permission.SCHEDULE_EXACT_ALARM, conditional = true)
public void setExactAndAllowWhileIdle(@AlarmType int type, long triggerAtMillis,
PendingIntent operation) {
setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, operation,
@@ -1018,6 +1053,18 @@
}
/**
+ * Called to check if the caller has permission to use alarms set via {@link }
+ * @return
+ */
+ public boolean canScheduleExactAlarms() {
+ try {
+ return mService.canScheduleExactAlarms();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Gets information about the next alarm clock currently scheduled.
*
* The alarm clocks considered are those scheduled by any application
diff --git a/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl b/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl
index 2c51935..2f21ce3 100644
--- a/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl
+++ b/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl
@@ -41,4 +41,5 @@
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
long currentNetworkTimeMillis();
+ boolean canScheduleExactAlarms();
}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
index 657c368..3bc7b30 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
@@ -26,6 +26,7 @@
import android.app.AlarmManager;
import android.app.IAlarmListener;
import android.app.PendingIntent;
+import android.os.Bundle;
import android.os.WorkSource;
import android.util.IndentingPrintWriter;
import android.util.TimeUtils;
@@ -92,10 +93,12 @@
private long mWhenElapsed;
private long mMaxWhenElapsed;
public AlarmManagerService.PriorityClass priorityClass;
+ /** Broadcast options to use when delivering this alarm */
+ public Bundle mIdleOptions;
Alarm(int type, long when, long requestedWhenElapsed, long windowLength, long interval,
PendingIntent op, IAlarmListener rec, String listenerTag, WorkSource ws, int flags,
- AlarmManager.AlarmClockInfo info, int uid, String pkgName) {
+ AlarmManager.AlarmClockInfo info, int uid, String pkgName, Bundle idleOptions) {
this.type = type;
origWhen = when;
wakeup = type == AlarmManager.ELAPSED_REALTIME_WAKEUP
@@ -115,6 +118,7 @@
alarmClock = info;
this.uid = uid;
packageName = pkgName;
+ mIdleOptions = idleOptions;
sourcePackage = (operation != null) ? operation.getCreatorPackage() : packageName;
creatorUid = (operation != null) ? operation.getCreatorUid() : this.uid;
}
@@ -303,6 +307,10 @@
ipw.print("listener=");
ipw.println(listener.asBinder());
}
+ if (mIdleOptions != null) {
+ ipw.print("idle-options=");
+ ipw.println(mIdleOptions.toString());
+ }
}
public void dumpDebug(ProtoOutputStream proto, long fieldId, long nowElapsed) {
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 f6a1b8a..559a434 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -20,11 +20,15 @@
import static android.app.AlarmManager.ELAPSED_REALTIME;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE;
+import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_COMPAT;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
import static android.app.AlarmManager.FLAG_IDLE_UNTIL;
+import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE;
+import static android.app.AlarmManager.INTERVAL_HOUR;
import static android.app.AlarmManager.RTC;
import static android.app.AlarmManager.RTC_WAKEUP;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
import static android.os.UserHandle.USER_SYSTEM;
import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX;
@@ -32,6 +36,7 @@
import static com.android.server.alarm.Alarm.DEVICE_IDLE_POLICY_INDEX;
import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.Activity;
@@ -43,12 +48,14 @@
import android.app.IAlarmListener;
import android.app.IAlarmManager;
import android.app.PendingIntent;
+import android.app.compat.CompatChanges;
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.PermissionChecker;
import android.content.pm.PackageManagerInternal;
import android.net.Uri;
import android.os.BatteryManager;
@@ -65,6 +72,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
@@ -95,6 +103,8 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IAppOpsCallback;
+import com.android.internal.app.IAppOpsService;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.LocalLog;
@@ -177,6 +187,7 @@
final LocalLog mLog = new LocalLog(TAG);
AppOpsManager mAppOps;
+ IAppOpsService mAppOpsService;
DeviceIdleInternal mLocalDeviceIdleController;
private UsageStatsManagerInternal mUsageStatsManagerInternal;
private ActivityManagerInternal mActivityManagerInternal;
@@ -253,10 +264,8 @@
"REORDER_ALARMS_FOR_STANDBY",
});
- /**
- * Broadcast options to use for FLAG_ALLOW_WHILE_IDLE.
- */
- Bundle mIdleOptions;
+ BroadcastOptions mOptsWithFgs = BroadcastOptions.makeBasic();
+ BroadcastOptions mOptsWithoutFgs = BroadcastOptions.makeBasic();
// TODO(b/172085676): Move inside alarm store.
private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser =
@@ -410,6 +419,10 @@
@VisibleForTesting
static final String KEY_ALLOW_WHILE_IDLE_QUOTA = "allow_while_idle_quota";
+ @VisibleForTesting
+ static final String KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA = "allow_while_idle_compat_quota";
+ private static final String KEY_ALLOW_WHILE_IDLE_WINDOW = "allow_while_idle_window";
+
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;
@@ -433,8 +446,14 @@
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.
+ /**
+ * Default quota for pre-S apps. Enough to accommodate the existing policy of an alarm
+ * every ALLOW_WHILE_IDLE_LONG_DELAY, which was 9 minutes.
+ */
+ private static final int DEFAULT_ALLOW_WHILE_IDLE_COMPAT_QUOTA = 7;
+ private static final int DEFAULT_ALLOW_WHILE_IDLE_QUOTA = 72;
+
+ private static final long DEFAULT_ALLOW_WHILE_IDLE_WINDOW = 60 * 60 * 1000; // 1 hour.
// Minimum futurity of a new alarm
public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
@@ -463,6 +482,19 @@
public int ALLOW_WHILE_IDLE_QUOTA = DEFAULT_ALLOW_WHILE_IDLE_QUOTA;
+ /**
+ * Used to provide backwards compatibility to pre-S apps with a quota equivalent to the
+ * earlier delay throttling mechanism.
+ */
+ public int ALLOW_WHILE_IDLE_COMPAT_QUOTA = DEFAULT_ALLOW_WHILE_IDLE_COMPAT_QUOTA;
+
+ /**
+ * The window used for enforcing {@link #ALLOW_WHILE_IDLE_QUOTA} and
+ * {@link #ALLOW_WHILE_IDLE_COMPAT_QUOTA}. Can be configured, but only recommended for
+ * testing.
+ */
+ public long ALLOW_WHILE_IDLE_WINDOW = DEFAULT_ALLOW_WHILE_IDLE_WINDOW;
+
private long mLastAllowWhileIdleWhitelistDuration = -1;
Constants() {
@@ -480,9 +512,11 @@
public void updateAllowWhileIdleWhitelistDurationLocked() {
if (mLastAllowWhileIdleWhitelistDuration != ALLOW_WHILE_IDLE_WHITELIST_DURATION) {
mLastAllowWhileIdleWhitelistDuration = ALLOW_WHILE_IDLE_WHITELIST_DURATION;
- BroadcastOptions opts = BroadcastOptions.makeBasic();
- opts.setTemporaryAppWhitelistDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION);
- mIdleOptions = opts.toBundle();
+
+ mOptsWithFgs.setTemporaryAppWhitelistDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION);
+ mOptsWithoutFgs.setTemporaryAppWhitelistDuration(
+ TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED,
+ ALLOW_WHILE_IDLE_WHITELIST_DURATION);
}
}
@@ -516,6 +550,27 @@
ALLOW_WHILE_IDLE_QUOTA = 1;
}
break;
+ case KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA:
+ ALLOW_WHILE_IDLE_COMPAT_QUOTA = properties.getInt(
+ KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA,
+ DEFAULT_ALLOW_WHILE_IDLE_COMPAT_QUOTA);
+ if (ALLOW_WHILE_IDLE_COMPAT_QUOTA <= 0) {
+ Slog.w(TAG, "Cannot have quota lower than 1.");
+ ALLOW_WHILE_IDLE_COMPAT_QUOTA = 1;
+ }
+ break;
+ case KEY_ALLOW_WHILE_IDLE_WINDOW:
+ ALLOW_WHILE_IDLE_WINDOW = properties.getLong(
+ KEY_ALLOW_WHILE_IDLE_WINDOW, DEFAULT_ALLOW_WHILE_IDLE_WINDOW);
+ if (ALLOW_WHILE_IDLE_WINDOW > DEFAULT_ALLOW_WHILE_IDLE_WINDOW) {
+ Slog.w(TAG, "Cannot have allow_while_idle_window > "
+ + DEFAULT_ALLOW_WHILE_IDLE_WINDOW);
+ ALLOW_WHILE_IDLE_WINDOW = DEFAULT_ALLOW_WHILE_IDLE_WINDOW;
+ } else if (ALLOW_WHILE_IDLE_WINDOW < DEFAULT_ALLOW_WHILE_IDLE_WINDOW) {
+ Slog.w(TAG, "Using a non-default allow_while_idle_window = "
+ + ALLOW_WHILE_IDLE_WINDOW);
+ }
+ break;
case KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION:
ALLOW_WHILE_IDLE_WHITELIST_DURATION = properties.getLong(
KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION,
@@ -643,10 +698,14 @@
TimeUtils.formatDuration(LISTENER_TIMEOUT, pw);
pw.println();
- pw.print("allow_while_idle_window=");
+ pw.print(KEY_ALLOW_WHILE_IDLE_WINDOW);
+ pw.print("=");
TimeUtils.formatDuration(ALLOW_WHILE_IDLE_WINDOW, pw);
pw.println();
+ pw.print(KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA, ALLOW_WHILE_IDLE_COMPAT_QUOTA);
+ pw.println();
+
pw.print(KEY_ALLOW_WHILE_IDLE_QUOTA, ALLOW_WHILE_IDLE_QUOTA);
pw.println();
@@ -830,7 +889,15 @@
if (futurity < MIN_FUZZABLE_INTERVAL) {
futurity = 0;
}
- return clampPositive(triggerAtTime + (long) (.75 * futurity));
+ long maxElapsed = triggerAtTime + (long) (0.75 * futurity);
+ // For non-repeating alarms, window is capped at a maximum of one hour from the requested
+ // delivery time. This allows for inexact-while-idle alarms to be slightly more reliable.
+ // In practice, the delivery window should generally be much smaller than that
+ // when the device is not idling.
+ if (interval == 0) {
+ maxElapsed = Math.min(maxElapsed, triggerAtTime + INTERVAL_HOUR);
+ }
+ return clampPositive(maxElapsed);
}
// The RTC clock has moved arbitrarily, so we need to recalculate all the RTC alarm deliveries.
@@ -998,7 +1065,7 @@
setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed,
nextMaxElapsed - nextElapsed, alarm.repeatInterval, alarm.operation, null,
null, alarm.flags, alarm.workSource, alarm.alarmClock, alarm.uid,
- alarm.packageName);
+ alarm.packageName, null);
// Kernel alarms will be rescheduled as needed in setImplLocked
}
}
@@ -1241,7 +1308,8 @@
mAlarmStore.setAlarmClockRemovalListener(mAlarmClockUpdater);
mAppWakeupHistory = new AppWakeupHistory(Constants.DEFAULT_APP_STANDBY_WINDOW);
- mAllowWhileIdleHistory = new AppWakeupHistory(Constants.ALLOW_WHILE_IDLE_WINDOW);
+ mAllowWhileIdleHistory = new AppWakeupHistory(
+ Constants.DEFAULT_ALLOW_WHILE_IDLE_WINDOW);
mNextWakeup = mNextNonWakeup = 0;
@@ -1327,6 +1395,28 @@
synchronized (mLock) {
mConstants.start();
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
+ mAppOpsService = mInjector.getAppOpsService();
+ try {
+ mAppOpsService.startWatchingMode(AppOpsManager.OP_SCHEDULE_EXACT_ALARM, null,
+ new IAppOpsCallback.Stub() {
+ @Override
+ public void opChanged(int op, int uid, String packageName)
+ throws RemoteException {
+ if (op != AppOpsManager.OP_SCHEDULE_EXACT_ALARM) {
+ return;
+ }
+ final int mode = mAppOpsService.checkOperation(op, uid,
+ packageName);
+ if (mode != AppOpsManager.MODE_ALLOWED
+ && mode != AppOpsManager.MODE_DEFAULT) {
+ mHandler.obtainMessage(AlarmHandler.REMOVE_EXACT_ALARMS,
+ uid, 0, packageName).sendToTarget();
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ }
+
mLocalDeviceIdleController =
LocalServices.getService(DeviceIdleInternal.class);
mUsageStatsManagerInternal =
@@ -1428,7 +1518,7 @@
void setImpl(int type, long triggerAtTime, long windowLength, long interval,
PendingIntent operation, IAlarmListener directReceiver, String listenerTag,
int flags, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock,
- int callingUid, String callingPackage) {
+ int callingUid, String callingPackage, Bundle idleOptions) {
if ((operation == null && directReceiver == null)
|| (operation != null && directReceiver != null)) {
Slog.w(TAG, "Alarms must either supply a PendingIntent or an AlarmReceiver");
@@ -1519,17 +1609,18 @@
}
setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, interval, operation,
directReceiver, listenerTag, flags, workSource, alarmClock, callingUid,
- callingPackage);
+ callingPackage, idleOptions);
}
}
private void setImplLocked(int type, long when, long whenElapsed, long windowLength,
long interval, PendingIntent operation, IAlarmListener directReceiver,
String listenerTag, int flags, WorkSource workSource,
- AlarmManager.AlarmClockInfo alarmClock, int callingUid, String callingPackage) {
+ AlarmManager.AlarmClockInfo alarmClock, int callingUid, String callingPackage,
+ Bundle idleOptions) {
final Alarm a = new Alarm(type, when, whenElapsed, windowLength, interval,
operation, directReceiver, listenerTag, workSource, flags, alarmClock,
- callingUid, callingPackage);
+ callingUid, callingPackage, idleOptions);
if (mActivityManagerInternal.isAppStartModeDisabled(callingUid, callingPackage)) {
Slog.w(TAG, "Not setting alarm from " + callingUid + ":" + a
+ " -- package not allowed to start");
@@ -1605,19 +1696,21 @@
return false;
}
- if (!(mAppStateTracker != null && mAppStateTracker.areAlarmsRestrictedByBatterySaver(
- alarm.creatorUid, alarm.sourcePackage))) {
+ if (mAppStateTracker == null || !mAppStateTracker.areAlarmsRestrictedByBatterySaver(
+ alarm.creatorUid, alarm.sourcePackage)) {
return alarm.setPolicyElapsed(BATTERY_SAVER_POLICY_INDEX, nowElapsed);
}
final long batterySaverPolicyElapsed;
- if ((alarm.flags & (AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED)) != 0) {
+ if ((alarm.flags & (FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED)) != 0) {
// Unrestricted.
batterySaverPolicyElapsed = nowElapsed;
- } else if ((alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
+ } else if (isAllowedWhileIdleRestricted(alarm)) {
// Allowed but limited.
final int userId = UserHandle.getUserId(alarm.creatorUid);
- final int quota = mConstants.ALLOW_WHILE_IDLE_QUOTA;
+ final int quota = ((alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0)
+ ? mConstants.ALLOW_WHILE_IDLE_QUOTA
+ : mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA;
final int dispatchesInWindow = mAllowWhileIdleHistory.getTotalWakeupsInWindow(
alarm.sourcePackage, userId);
if (dispatchesInWindow < quota) {
@@ -1625,7 +1718,7 @@
batterySaverPolicyElapsed = nowElapsed;
} else {
batterySaverPolicyElapsed = mAllowWhileIdleHistory.getNthLastWakeupForPackage(
- alarm.sourcePackage, userId, quota) + Constants.ALLOW_WHILE_IDLE_WINDOW;
+ alarm.sourcePackage, userId, quota) + mConstants.ALLOW_WHILE_IDLE_WINDOW;
}
} else {
// Not allowed.
@@ -1635,6 +1728,16 @@
}
/**
+ * Returns {@code true} if the given alarm has the flag
+ * {@link AlarmManager#FLAG_ALLOW_WHILE_IDLE} or
+ * {@link AlarmManager#FLAG_ALLOW_WHILE_IDLE_COMPAT}
+ *
+ */
+ private static boolean isAllowedWhileIdleRestricted(Alarm a) {
+ return (a.flags & (FLAG_ALLOW_WHILE_IDLE | FLAG_ALLOW_WHILE_IDLE_COMPAT)) != 0;
+ }
+
+ /**
* Adjusts the delivery time of the alarm based on device_idle (doze) rules.
*
* @param alarm The alarm to adjust
@@ -1647,14 +1750,15 @@
}
final long deviceIdlePolicyTime;
- if ((alarm.flags & (AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED
- | AlarmManager.FLAG_WAKE_FROM_IDLE)) != 0) {
+ if ((alarm.flags & (FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | FLAG_WAKE_FROM_IDLE)) != 0) {
// Unrestricted.
deviceIdlePolicyTime = nowElapsed;
- } else if ((alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
+ } else if (isAllowedWhileIdleRestricted(alarm)) {
// Allowed but limited.
final int userId = UserHandle.getUserId(alarm.creatorUid);
- final int quota = mConstants.ALLOW_WHILE_IDLE_QUOTA;
+ final int quota = ((alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0)
+ ? mConstants.ALLOW_WHILE_IDLE_QUOTA
+ : mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA;
final int dispatchesInWindow = mAllowWhileIdleHistory.getTotalWakeupsInWindow(
alarm.sourcePackage, userId);
if (dispatchesInWindow < quota) {
@@ -1662,7 +1766,7 @@
deviceIdlePolicyTime = nowElapsed;
} else {
final long whenInQuota = mAllowWhileIdleHistory.getNthLastWakeupForPackage(
- alarm.sourcePackage, userId, quota) + Constants.ALLOW_WHILE_IDLE_WINDOW;
+ alarm.sourcePackage, userId, quota) + mConstants.ALLOW_WHILE_IDLE_WINDOW;
deviceIdlePolicyTime = Math.min(whenInQuota, mPendingIdleUntil.getWhenElapsed());
}
} else {
@@ -1749,7 +1853,7 @@
} else if (mPendingIdleUntil != null) {
adjustDeliveryTimeBasedOnDeviceIdle(a);
}
- if ((a.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
+ if ((a.flags & FLAG_WAKE_FROM_IDLE) != 0) {
if (mNextWakeFromIdle == null || mNextWakeFromIdle.getWhenElapsed()
> a.getWhenElapsed()) {
mNextWakeFromIdle = a;
@@ -1810,6 +1914,14 @@
}
/**
+ * Returns true if the given uid is on the system or user's power save exclusion list.
+ */
+ boolean isWhitelisted(int uid) {
+ return (mLocalDeviceIdleController == null || mLocalDeviceIdleController.isAppOnWhitelist(
+ UserHandle.getAppId(uid)));
+ }
+
+ /**
* Public-facing binder interface
*/
private final IBinder mService = new IAlarmManager.Stub() {
@@ -1824,6 +1936,54 @@
// wakelock time spent in alarm delivery
mAppOps.checkPackage(callingUid, callingPackage);
+ final boolean allowWhileIdle = (flags & FLAG_ALLOW_WHILE_IDLE) != 0;
+
+ Bundle idleOptions = null;
+ if (alarmClock != null || allowWhileIdle) {
+ // make sure the caller is allowed to use the requested kind of alarm, and also
+ // decide what broadcast options to use.
+ final boolean needsPermission;
+ boolean lowQuota;
+ if (CompatChanges.isChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION,
+ callingPackage, UserHandle.getUserHandleForUid(callingUid))) {
+ if (windowLength != AlarmManager.WINDOW_EXACT) {
+ needsPermission = false;
+ lowQuota = true;
+ idleOptions = isWhitelisted(callingUid) ? mOptsWithFgs.toBundle()
+ : mOptsWithoutFgs.toBundle();
+ } else if (alarmClock != null) {
+ needsPermission = true;
+ lowQuota = false;
+ idleOptions = mOptsWithFgs.toBundle();
+ } else {
+ needsPermission = true;
+ lowQuota = false;
+ idleOptions = mOptsWithFgs.toBundle();
+ }
+ } else {
+ needsPermission = false;
+ lowQuota = allowWhileIdle;
+ idleOptions = allowWhileIdle ? mOptsWithFgs.toBundle() : null;
+ }
+ if (needsPermission && !canScheduleExactAlarms()) {
+ if (alarmClock == null && isWhitelisted(callingUid)) {
+ // If the app is on the full system allow-list (not except-idle), we still
+ // allow the alarms, but with a lower quota to keep pre-S compatibility.
+ lowQuota = true;
+ } else {
+ final String errorMessage = "Caller needs to hold "
+ + Manifest.permission.SCHEDULE_EXACT_ALARM + " to set "
+ + ((allowWhileIdle) ? "exact, allow-while-idle" : "alarm-clock")
+ + " alarms.";
+ throw new SecurityException(errorMessage);
+ }
+ }
+ if (lowQuota) {
+ flags &= ~FLAG_ALLOW_WHILE_IDLE;
+ flags |= FLAG_ALLOW_WHILE_IDLE_COMPAT;
+ }
+ }
+
// Repeating alarms must use PendingIntent, not direct listener
if (interval != 0) {
if (directReceiver != null) {
@@ -1840,8 +2000,7 @@
// No incoming callers can request either WAKE_FROM_IDLE or
// ALLOW_WHILE_IDLE_UNRESTRICTED -- we will apply those later as appropriate.
- flags &= ~(AlarmManager.FLAG_WAKE_FROM_IDLE
- | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED);
+ flags &= ~(FLAG_WAKE_FROM_IDLE | FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED);
// Only the system can use FLAG_IDLE_UNTIL -- this is used to tell the alarm
// manager when to come out of idle mode, which is only for DeviceIdleController.
@@ -1857,22 +2016,32 @@
// If this alarm is for an alarm clock, then it must be standalone and we will
// use it to wake early from idle if needed.
if (alarmClock != null) {
- flags |= AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE;
+ flags |= FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE;
// If the caller is a core system component or on the user's whitelist, and not calling
// to do work on behalf of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED.
// This means we will allow these alarms to go off as normal even while idle, with no
// timing restrictions.
- } else if (workSource == null && (callingUid < Process.FIRST_APPLICATION_UID
+ } else if (workSource == null && (UserHandle.isCore(callingUid)
|| UserHandle.isSameApp(callingUid, mSystemUiUid)
|| ((mAppStateTracker != null)
&& mAppStateTracker.isUidPowerSaveUserExempt(callingUid)))) {
- flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
- flags &= ~AlarmManager.FLAG_ALLOW_WHILE_IDLE;
+ flags |= FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
+ flags &= ~FLAG_ALLOW_WHILE_IDLE;
+ flags &= ~FLAG_ALLOW_WHILE_IDLE_COMPAT;
+ idleOptions = null;
}
setImpl(type, triggerAtTime, windowLength, interval, operation, directReceiver,
- listenerTag, flags, workSource, alarmClock, callingUid, callingPackage);
+ listenerTag, flags, workSource, alarmClock, callingUid, callingPackage,
+ idleOptions);
+ }
+
+ @Override
+ public boolean canScheduleExactAlarms() {
+ return PermissionChecker.checkCallingOrSelfPermissionForPreflight(getContext(),
+ Manifest.permission.SCHEDULE_EXACT_ALARM)
+ == PermissionChecker.PERMISSION_GRANTED;
}
@Override
@@ -2755,6 +2924,77 @@
}
}
+ /**
+ * Called when an app loses {@link Manifest.permission#SCHEDULE_EXACT_ALARM} to remove alarms
+ * that the app is no longer eligible to use.
+ * TODO (b/179541791): Revisit and write tests once UX is final.
+ */
+ void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName) {
+ if (UserHandle.isCore(uid) || uid == mSystemUiUid) {
+ return;
+ }
+ if (isWhitelisted(uid)) {
+ return;
+ }
+ if (!CompatChanges.isChangeEnabled(
+ AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION,
+ packageName, UserHandle.getUserHandleForUid(uid))) {
+ return;
+ }
+
+ final Predicate<Alarm> whichAlarms =
+ a -> (a.uid == uid && a.packageName.equals(packageName)
+ && ((a.flags & FLAG_ALLOW_WHILE_IDLE) != 0 || a.alarmClock != null));
+ final ArrayList<Alarm> removed = mAlarmStore.remove(whichAlarms);
+ final boolean didRemove = !removed.isEmpty();
+ if (didRemove) {
+ decrementAlarmCount(uid, removed.size());
+ }
+
+ for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) {
+ final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i);
+ for (int j = alarmsForUid.size() - 1; j >= 0; j--) {
+ final Alarm alarm = alarmsForUid.get(j);
+ if (whichAlarms.test(alarm)) {
+ // Don't set didRemove, since this doesn't impact the scheduled alarms.
+ alarmsForUid.remove(j);
+ decrementAlarmCount(alarm.uid, 1);
+ }
+ }
+ if (alarmsForUid.size() == 0) {
+ mPendingBackgroundAlarms.removeAt(i);
+ }
+ }
+ for (int i = mPendingNonWakeupAlarms.size() - 1; i >= 0; i--) {
+ final Alarm a = mPendingNonWakeupAlarms.get(i);
+ if (whichAlarms.test(a)) {
+ // Don't set didRemove, since this doesn't impact the scheduled alarms.
+ mPendingNonWakeupAlarms.remove(i);
+ decrementAlarmCount(a.uid, 1);
+ }
+ }
+
+ if (didRemove) {
+ if (mNextWakeFromIdle != null && whichAlarms.test(mNextWakeFromIdle)) {
+ mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm();
+ if (mPendingIdleUntil != null) {
+ final boolean idleUntilUpdated = mAlarmStore.updateAlarmDeliveries(alarm -> {
+ if (alarm != mPendingIdleUntil) {
+ return false;
+ }
+ return adjustIdleUntilTime(alarm);
+ });
+ if (idleUntilUpdated) {
+ mAlarmStore.updateAlarmDeliveries(
+ alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm));
+ }
+ }
+ }
+ rescheduleKernelAlarmsLocked();
+ updateNextAlarmClockLocked();
+ }
+ }
+
void removeLocked(PendingIntent operation, IAlarmListener directReceiver) {
if (operation == null && directReceiver == null) {
if (localLOGV) {
@@ -3082,7 +3322,7 @@
}
}
- private boolean isExemptFromBatterySaver(Alarm alarm) {
+ private static boolean isExemptFromBatterySaver(Alarm alarm) {
if (alarm.alarmClock != null) {
return true;
}
@@ -3142,7 +3382,7 @@
alarm.count = 1;
triggerList.add(alarm);
- if ((alarm.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
+ if ((alarm.flags & FLAG_WAKE_FROM_IDLE) != 0) {
EventLogTags.writeDeviceIdleWakeFromIdle(mPendingIdleUntil != null ? 1 : 0,
alarm.statsTag);
}
@@ -3180,7 +3420,7 @@
setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed,
nextMaxElapsed - nextElapsed, alarm.repeatInterval, alarm.operation, null,
null, alarm.flags, alarm.workSource, alarm.alarmClock, alarm.uid,
- alarm.packageName);
+ alarm.packageName, null);
}
if (alarm.wakeup) {
@@ -3257,7 +3497,6 @@
mLastAlarmDeliveryTime = nowELAPSED;
for (int i = 0; i < triggerList.size(); i++) {
Alarm alarm = triggerList.get(i);
- final boolean allowWhileIdle = (alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0;
if (alarm.wakeup) {
Trace.traceBegin(Trace.TRACE_TAG_POWER,
"Dispatch wakeup alarm to " + alarm.packageName);
@@ -3273,7 +3512,7 @@
mActivityManagerInternal.noteAlarmStart(alarm.operation, alarm.workSource,
alarm.uid, alarm.statsTag);
}
- mDeliveryTracker.deliverLocked(alarm, nowELAPSED, allowWhileIdle);
+ mDeliveryTracker.deliverLocked(alarm, nowELAPSED);
} catch (RuntimeException e) {
Slog.w(TAG, "Failure sending alarm.", e);
}
@@ -3282,9 +3521,10 @@
}
}
- private boolean isExemptFromAppStandby(Alarm a) {
+ @VisibleForTesting
+ static boolean isExemptFromAppStandby(Alarm a) {
return a.alarmClock != null || UserHandle.isCore(a.creatorUid)
- || (a.flags & FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED) != 0;
+ || (a.flags & (FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | FLAG_ALLOW_WHILE_IDLE)) != 0;
}
@VisibleForTesting
@@ -3368,6 +3608,11 @@
MATCH_SYSTEM_ONLY, USER_SYSTEM);
}
+ IAppOpsService getAppOpsService() {
+ return IAppOpsService.Stub.asInterface(
+ ServiceManager.getService(Context.APP_OPS_SERVICE));
+ }
+
ClockReceiver getClockReceiver(AlarmManagerService service) {
return service.new ClockReceiver();
}
@@ -3566,6 +3811,7 @@
public static final int APP_STANDBY_BUCKET_CHANGED = 5;
public static final int CHARGING_STATUS_CHANGED = 6;
public static final int REMOVE_FOR_CANCELED = 7;
+ public static final int REMOVE_EXACT_ALARMS = 8;
AlarmHandler() {
super(Looper.myLooper());
@@ -3645,6 +3891,14 @@
}
break;
+ case REMOVE_EXACT_ALARMS:
+ final int uid = msg.arg1;
+ final String packageName = (String) msg.obj;
+ synchronized (mLock) {
+ removeExactAlarmsOnPermissionRevokedLocked(uid, packageName);
+ }
+ break;
+
default:
// nope, just ignore it
break;
@@ -3720,7 +3974,7 @@
setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0,
0, null, mTimeTickTrigger, TIME_TICK_TAG, flags, workSource, null,
- Process.myUid(), "android");
+ Process.myUid(), "android", null);
// Finally, remember when we set the tick alarm
synchronized (mLock) {
@@ -3740,7 +3994,7 @@
final WorkSource workSource = null; // Let system take blame for date change events.
setImpl(RTC, calendar.getTimeInMillis(), 0, 0, mDateChangeSender, null, null,
AlarmManager.FLAG_STANDALONE, workSource, null,
- Process.myUid(), "android");
+ Process.myUid(), "android", null);
}
}
@@ -4112,7 +4366,7 @@
* Deliver an alarm and set up the post-delivery handling appropriately
*/
@GuardedBy("mLock")
- public void deliverLocked(Alarm alarm, long nowELAPSED, boolean allowWhileIdle) {
+ public void deliverLocked(Alarm alarm, long nowELAPSED) {
final long workSourceToken = ThreadLocalWorkSource.setUid(
getAlarmAttributionUid(alarm));
try {
@@ -4122,10 +4376,8 @@
try {
alarm.operation.send(getContext(), 0,
- mBackgroundIntent.putExtra(
- Intent.EXTRA_ALARM_COUNT, alarm.count),
- mDeliveryTracker, mHandler, null,
- allowWhileIdle ? mIdleOptions : null);
+ mBackgroundIntent.putExtra(Intent.EXTRA_ALARM_COUNT, alarm.count),
+ mDeliveryTracker, mHandler, null, alarm.mIdleOptions);
} catch (PendingIntent.CanceledException e) {
if (alarm.repeatInterval > 0) {
// This IntentSender is no longer valid, but this
@@ -4194,7 +4446,7 @@
if (inflight.isBroadcast()) {
notifyBroadcastAlarmPendingLocked(alarm.uid);
}
- if (allowWhileIdle) {
+ if (isAllowedWhileIdleRestricted(alarm)) {
final boolean doze = (mPendingIdleUntil != null);
final boolean batterySaver = (mAppStateTracker != null
&& mAppStateTracker.isForceAllAppsStandbyEnabled());
@@ -4204,8 +4456,7 @@
mAllowWhileIdleHistory.recordAlarmForPackage(alarm.sourcePackage,
UserHandle.getUserId(alarm.creatorUid), nowELAPSED);
mAlarmStore.updateAlarmDeliveries(a -> {
- if (a.creatorUid != alarm.creatorUid
- || (a.flags & FLAG_ALLOW_WHILE_IDLE) == 0) {
+ if (a.creatorUid != alarm.creatorUid || !isAllowedWhileIdleRestricted(a)) {
return false;
}
return (doze && adjustDeliveryTimeBasedOnDeviceIdle(a))
diff --git a/core/api/current.txt b/core/api/current.txt
index e72f943..d3d754c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -145,6 +145,7 @@
field public static final String REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE = "android.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE";
field public static final String REQUEST_PASSWORD_COMPLEXITY = "android.permission.REQUEST_PASSWORD_COMPLEXITY";
field @Deprecated public static final String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
+ field public static final String SCHEDULE_EXACT_ALARM = "android.permission.SCHEDULE_EXACT_ALARM";
field public static final String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
field public static final String SEND_SMS = "android.permission.SEND_SMS";
field public static final String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
@@ -4328,16 +4329,17 @@
}
public class AlarmManager {
+ method public boolean canScheduleExactAlarms();
method public void cancel(android.app.PendingIntent);
method public void cancel(android.app.AlarmManager.OnAlarmListener);
method public android.app.AlarmManager.AlarmClockInfo getNextAlarmClock();
method public void set(int, long, android.app.PendingIntent);
method public void set(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler);
- method public void setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent);
+ method @RequiresPermission(android.Manifest.permission.SCHEDULE_EXACT_ALARM) public void setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent);
method public void setAndAllowWhileIdle(int, long, android.app.PendingIntent);
method public void setExact(int, long, android.app.PendingIntent);
method public void setExact(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler);
- method public void setExactAndAllowWhileIdle(int, long, android.app.PendingIntent);
+ method @RequiresPermission(value=android.Manifest.permission.SCHEDULE_EXACT_ALARM, conditional=true) public void setExactAndAllowWhileIdle(int, long, android.app.PendingIntent);
method public void setInexactRepeating(int, long, long, android.app.PendingIntent);
method public void setRepeating(int, long, long, android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.SET_TIME) public void setTime(long);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index b85b186..1906ee4 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -795,7 +795,8 @@
// when adding one of these:
// - increment _NUM_OP
// - define an OPSTR_* constant (marked as @SystemApi)
- // - add rows to sOpToSwitch, sOpToString, sOpNames, sOpToPerms, sOpDefault
+ // - add rows to sOpToSwitch, sOpToString, sOpNames, sOpPerms, sOpDefaultMode, sOpDisableReset,
+ // sOpRestrictions, sOpAllowSystemRestrictionBypass
// - add descriptive strings to Settings/res/values/arrays.xml
// - add the op to the appropriate template in AppOpsState.OpsTemplate (settings app)
@@ -1174,13 +1175,19 @@
*
* @hide
*/
- // TODO: Add as AppProtoEnums
- public static final int OP_RECORD_AUDIO_OUTPUT = 106;
+ public static final int OP_RECORD_AUDIO_OUTPUT = AppProtoEnums.APP_OP_RECORD_AUDIO_OUTPUT;
+
+ /**
+ * App can schedule exact alarm to perform timing based background work
+ *
+ * @hide
+ */
+ public static final int OP_SCHEDULE_EXACT_ALARM = AppProtoEnums.APP_OP_SCHEDULE_EXACT_ALARM;
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int _NUM_OP = 107;
+ public static final int _NUM_OP = 108;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1553,6 +1560,13 @@
*/
public static final String OPSTR_RECORD_AUDIO_OUTPUT = "android:record_audio_output";
+ /**
+ * App can schedule exact alarm to perform timing based background work.
+ *
+ * @hide
+ */
+ public static final String OPSTR_SCHEDULE_EXACT_ALARM = "android:schedule_exact_alarm";
+
/** {@link #sAppOpsToNote} not initialized yet for this op */
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
/** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -1633,6 +1647,7 @@
OP_LOADER_USAGE_STATS,
OP_MANAGE_ONGOING_CALLS,
OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
+ OP_SCHEDULE_EXACT_ALARM,
};
/**
@@ -1751,6 +1766,7 @@
OP_MANAGE_CREDENTIALS, // MANAGE_CREDENTIALS
OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
OP_RECORD_AUDIO_OUTPUT, // RECORD_AUDIO_OUTPUT
+ OP_SCHEDULE_EXACT_ALARM, // SCHEDULE_EXACT_ALARM
};
/**
@@ -1864,6 +1880,7 @@
OPSTR_MANAGE_CREDENTIALS,
OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
OPSTR_RECORD_AUDIO_OUTPUT,
+ OPSTR_SCHEDULE_EXACT_ALARM,
};
/**
@@ -1978,6 +1995,7 @@
"MANAGE_CREDENTIALS",
"USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER",
"RECORD_AUDIO_OUTPUT",
+ "SCHEDULE_EXACT_ALARM",
};
/**
@@ -2093,6 +2111,7 @@
null, // no permission for OP_MANAGE_CREDENTIALS
Manifest.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
null, // no permission for OP_RECORD_AUDIO_OUTPUT
+ Manifest.permission.SCHEDULE_EXACT_ALARM,
};
/**
@@ -2208,6 +2227,7 @@
null, // MANAGE_CREDENTIALS
null, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
null, // RECORD_AUDIO_OUTPUT
+ null, // SCHEDULE_EXACT_ALARM
};
/**
@@ -2322,6 +2342,7 @@
null, // MANAGE_CREDENTIALS
null, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
null, // RECORD_AUDIO_OUTPUT
+ null, // SCHEDULE_EXACT_ALARM
};
/**
@@ -2435,6 +2456,7 @@
AppOpsManager.MODE_DEFAULT, // MANAGE_CREDENTIALS
AppOpsManager.MODE_DEFAULT, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
AppOpsManager.MODE_ALLOWED, // RECORD_AUDIO_OUTPUT
+ AppOpsManager.MODE_DEFAULT, // SCHEDULE_EXACT_ALARM
};
/**
@@ -2552,6 +2574,7 @@
false, // MANAGE_CREDENTIALS
true, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
false, // RECORD_AUDIO_OUTPUT
+ false, // SCHEDULE_EXACT_ALARM
};
/**
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index 7410a1c..dfc105a 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -428,6 +428,13 @@
*/
private IAppTraceRetriever mAppTraceRetriever;
+ /**
+ * ParcelFileDescriptor pointing to a native tombstone.
+ *
+ * @see #getTraceInputStream
+ */
+ private IParcelFileDescriptorRetriever mNativeTombstoneRetriever;
+
/** @hide */
@IntDef(prefix = { "REASON_" }, value = {
REASON_UNKNOWN,
@@ -603,22 +610,38 @@
* prior to the death of the process; typically it'll be available when
* the reason is {@link #REASON_ANR}, though if the process gets an ANR
* but recovers, and dies for another reason later, this trace will be included
- * in the record of {@link ApplicationExitInfo} still.
+ * in the record of {@link ApplicationExitInfo} still. Beginning with API 31,
+ * tombstone traces will be returned for
+ * {@link #REASON_CRASH_NATIVE}, with an InputStream containing a protobuf with
+ * <a href="https://android.googlesource.com/platform/system/core/+/refs/heads/master/debuggerd/proto/tombstone.proto">this schema</a>.
+ * Note thatbecause these traces are kept in a separate global circular buffer, crashes may be
+ * overwritten by newer crashes (including from other applications), so this may still return
+ * null.
*
* @return The input stream to the traces that was taken by the system
* prior to the death of the process.
*/
public @Nullable InputStream getTraceInputStream() throws IOException {
- if (mAppTraceRetriever == null) {
+ if (mAppTraceRetriever == null && mNativeTombstoneRetriever == null) {
return null;
}
+
try {
- final ParcelFileDescriptor fd = mAppTraceRetriever.getTraceFileDescriptor(
- mPackageName, mPackageUid, mPid);
- if (fd == null) {
- return null;
+ if (mNativeTombstoneRetriever != null) {
+ final ParcelFileDescriptor pfd = mNativeTombstoneRetriever.getPfd();
+ if (pfd == null) {
+ return null;
+ }
+
+ return new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+ } else {
+ final ParcelFileDescriptor fd = mAppTraceRetriever.getTraceFileDescriptor(
+ mPackageName, mPackageUid, mPid);
+ if (fd == null) {
+ return null;
+ }
+ return new GZIPInputStream(new ParcelFileDescriptor.AutoCloseInputStream(fd));
}
- return new GZIPInputStream(new ParcelFileDescriptor.AutoCloseInputStream(fd));
} catch (RemoteException e) {
return null;
}
@@ -849,6 +872,15 @@
mAppTraceRetriever = retriever;
}
+ /**
+ * @see mNativeTombstoneRetriever
+ *
+ * @hide
+ */
+ public void setNativeTombstoneRetriever(final IParcelFileDescriptorRetriever retriever) {
+ mNativeTombstoneRetriever = retriever;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -878,6 +910,12 @@
} else {
dest.writeInt(0);
}
+ if (mNativeTombstoneRetriever != null) {
+ dest.writeInt(1);
+ dest.writeStrongBinder(mNativeTombstoneRetriever.asBinder());
+ } else {
+ dest.writeInt(0);
+ }
}
/** @hide */
@@ -906,6 +944,7 @@
mState = other.mState;
mTraceFile = other.mTraceFile;
mAppTraceRetriever = other.mAppTraceRetriever;
+ mNativeTombstoneRetriever = other.mNativeTombstoneRetriever;
}
private ApplicationExitInfo(@NonNull Parcel in) {
@@ -928,6 +967,10 @@
if (in.readInt() == 1) {
mAppTraceRetriever = IAppTraceRetriever.Stub.asInterface(in.readStrongBinder());
}
+ if (in.readInt() == 1) {
+ mNativeTombstoneRetriever = IParcelFileDescriptorRetriever.Stub.asInterface(
+ in.readStrongBinder());
+ }
}
public @NonNull static final Creator<ApplicationExitInfo> CREATOR =
@@ -986,6 +1029,7 @@
sb.append(" state=").append(ArrayUtils.isEmpty(mState)
? "empty" : Integer.toString(mState.length) + " bytes");
sb.append(" trace=").append(mTraceFile);
+
return sb.toString();
}
diff --git a/core/java/android/speech/tts/ITextToSpeechSession.aidl b/core/java/android/app/IParcelFileDescriptorRetriever.aidl
similarity index 61%
rename from core/java/android/speech/tts/ITextToSpeechSession.aidl
rename to core/java/android/app/IParcelFileDescriptorRetriever.aidl
index b2afeb0..7e808e7 100644
--- a/core/java/android/speech/tts/ITextToSpeechSession.aidl
+++ b/core/java/android/app/IParcelFileDescriptorRetriever.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -14,20 +14,18 @@
* limitations under the License.
*/
-package android.speech.tts;
+package android.app;
+
+import android.os.ParcelFileDescriptor;
/**
- * TextToSpeech session interface. Allows to control remote TTS service session once connected.
+ * An interface used to lazily provide a ParcelFileDescriptor to apps.
*
- * @see ITextToSpeechManager
- * @see ITextToSpeechSessionCallback
- *
- * {@hide}
+ * @hide
*/
-oneway interface ITextToSpeechSession {
-
+interface IParcelFileDescriptorRetriever {
/**
- * Disconnects the client from the TTS provider.
+ * Retrieve the ParcelFileDescriptor.
*/
- void disconnect();
-}
\ No newline at end of file
+ ParcelFileDescriptor getPfd();
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 10b00f2..51669432 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4583,14 +4583,6 @@
public static final String AUTOFILL_MANAGER_SERVICE = "autofill";
/**
- * Official published name of the (internal) text to speech manager service.
- *
- * @hide
- * @see #getSystemService(String)
- */
- public static final String TEXT_TO_SPEECH_MANAGER_SERVICE = "texttospeech";
-
- /**
* Official published name of the content capture service.
*
* @hide
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityMetricsEvent.aidl b/core/java/android/net/ConnectivityMetricsEvent.aidl
similarity index 100%
rename from packages/Connectivity/framework/src/android/net/ConnectivityMetricsEvent.aidl
rename to core/java/android/net/ConnectivityMetricsEvent.aidl
diff --git a/packages/Connectivity/framework/src/android/net/InterfaceConfiguration.aidl b/core/java/android/net/InterfaceConfiguration.aidl
similarity index 100%
rename from packages/Connectivity/framework/src/android/net/InterfaceConfiguration.aidl
rename to core/java/android/net/InterfaceConfiguration.aidl
diff --git a/packages/Connectivity/framework/src/android/net/UidRange.aidl b/core/java/android/net/UidRange.aidl
similarity index 100%
rename from packages/Connectivity/framework/src/android/net/UidRange.aidl
rename to core/java/android/net/UidRange.aidl
diff --git a/core/java/android/net/vcn/IVcnStatusCallback.aidl b/core/java/android/net/vcn/IVcnStatusCallback.aidl
index a7386718..555e9b5 100644
--- a/core/java/android/net/vcn/IVcnStatusCallback.aidl
+++ b/core/java/android/net/vcn/IVcnStatusCallback.aidl
@@ -19,4 +19,9 @@
/** @hide */
interface IVcnStatusCallback {
void onEnteredSafeMode();
+ void onGatewayConnectionError(
+ in int[] gatewayNetworkCapabilities,
+ int errorCode,
+ in String exceptionClass,
+ in String exceptionMessage);
}
\ No newline at end of file
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index aed64de..aea0ea9 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -17,7 +17,9 @@
import static java.util.Objects.requireNonNull;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.content.Context;
@@ -32,6 +34,8 @@
import com.android.internal.annotations.VisibleForTesting.Visibility;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -262,6 +266,42 @@
}
}
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ VCN_ERROR_CODE_INTERNAL_ERROR,
+ VCN_ERROR_CODE_CONFIG_ERROR,
+ VCN_ERROR_CODE_NETWORK_ERROR
+ })
+ public @interface VcnErrorCode {}
+
+ /**
+ * Value indicating that an internal failure occurred in this Gateway Connection.
+ *
+ * @hide
+ */
+ public static final int VCN_ERROR_CODE_INTERNAL_ERROR = 0;
+
+ /**
+ * Value indicating that an error with this Gateway Connection's configuration occurred.
+ *
+ * <p>For example, this error code will be returned after authentication failures.
+ *
+ * @hide
+ */
+ public static final int VCN_ERROR_CODE_CONFIG_ERROR = 1;
+
+ /**
+ * Value indicating that a Network error occurred with this Gateway Connection.
+ *
+ * <p>For example, this error code will be returned if an underlying {@link android.net.Network}
+ * for this Gateway Connection is lost, or if an error occurs while resolving the connection
+ * endpoint address.
+ *
+ * @hide
+ */
+ public static final int VCN_ERROR_CODE_NETWORK_ERROR = 2;
+
// TODO: make VcnStatusCallback @SystemApi
/**
* VcnStatusCallback is the interface for Carrier apps to receive updates for their VCNs.
@@ -285,6 +325,24 @@
* via {@link #setVcnConfig(ParcelUuid, VcnConfig)}.
*/
public abstract void onEnteredSafeMode();
+
+ /**
+ * Invoked when a VCN Gateway Connection corresponding to this callback's subscription
+ * encounters an error.
+ *
+ * @param networkCapabilities an array of underlying NetworkCapabilities for the Gateway
+ * Connection that encountered the error for identification purposes. These will be a
+ * sorted list with no duplicates, matching one of the {@link
+ * VcnGatewayConnectionConfig}s set in the {@link VcnConfig} for this subscription
+ * group.
+ * @param errorCode {@link VcnErrorCode} to indicate the error that occurred
+ * @param detail Throwable to provide additional information about the error, or {@code
+ * null} if none
+ */
+ public abstract void onGatewayConnectionError(
+ @NonNull int[] networkCapabilities,
+ @VcnErrorCode int errorCode,
+ @Nullable Throwable detail);
}
/**
@@ -385,11 +443,12 @@
*
* @hide
*/
- private class VcnStatusCallbackBinder extends IVcnStatusCallback.Stub {
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static class VcnStatusCallbackBinder extends IVcnStatusCallback.Stub {
@NonNull private final Executor mExecutor;
@NonNull private final VcnStatusCallback mCallback;
- private VcnStatusCallbackBinder(
+ public VcnStatusCallbackBinder(
@NonNull Executor executor, @NonNull VcnStatusCallback callback) {
mExecutor = executor;
mCallback = callback;
@@ -400,5 +459,36 @@
Binder.withCleanCallingIdentity(
() -> mExecutor.execute(() -> mCallback.onEnteredSafeMode()));
}
+
+ // TODO(b/180521637): use ServiceSpecificException for safer Exception 'parceling'
+ @Override
+ public void onGatewayConnectionError(
+ @NonNull int[] networkCapabilities,
+ @VcnErrorCode int errorCode,
+ @Nullable String exceptionClass,
+ @Nullable String exceptionMessage) {
+ final Throwable cause = createThrowableByClassName(exceptionClass, exceptionMessage);
+
+ Binder.withCleanCallingIdentity(
+ () ->
+ mExecutor.execute(
+ () ->
+ mCallback.onGatewayConnectionError(
+ networkCapabilities, errorCode, cause)));
+ }
+
+ private static Throwable createThrowableByClassName(
+ @Nullable String className, @Nullable String message) {
+ if (className == null) {
+ return null;
+ }
+
+ try {
+ Class<?> c = Class.forName(className);
+ return (Throwable) c.getConstructor(String.class).newInstance(message);
+ } catch (ReflectiveOperationException | ClassCastException e) {
+ return new RuntimeException(className + ": " + message);
+ }
+ }
}
}
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkSpecifier.java b/core/java/android/net/vcn/VcnUnderlyingNetworkSpecifier.java
new file mode 100644
index 0000000..a975637
--- /dev/null
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkSpecifier.java
@@ -0,0 +1,125 @@
+/*
+ * 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.net.vcn;
+
+import android.annotation.NonNull;
+import android.net.NetworkSpecifier;
+import android.net.TelephonyNetworkSpecifier;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.ArrayUtils;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * NetworkSpecifier object for VCN underlying network requests.
+ *
+ * <p>This matches any underlying network with the appropriate subIds.
+ *
+ * @hide
+ */
+public final class VcnUnderlyingNetworkSpecifier extends NetworkSpecifier implements Parcelable {
+ @NonNull private final int[] mSubIds;
+
+ /**
+ * Builds a new VcnUnderlyingNetworkSpecifier with the given list of subIds
+ *
+ * @hide
+ */
+ public VcnUnderlyingNetworkSpecifier(@NonNull int[] subIds) {
+ mSubIds = Objects.requireNonNull(subIds, "subIds were null");
+ }
+
+ /**
+ * Retrieves the list of subIds supported by this VcnUnderlyingNetworkSpecifier
+ *
+ * @hide
+ */
+ @NonNull
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public int[] getSubIds() {
+ return mSubIds;
+ }
+
+ public static final @NonNull Creator<VcnUnderlyingNetworkSpecifier> CREATOR =
+ new Creator<VcnUnderlyingNetworkSpecifier>() {
+ @Override
+ public VcnUnderlyingNetworkSpecifier createFromParcel(Parcel in) {
+ int[] subIds = in.createIntArray();
+ return new VcnUnderlyingNetworkSpecifier(subIds);
+ }
+
+ @Override
+ public VcnUnderlyingNetworkSpecifier[] newArray(int size) {
+ return new VcnUnderlyingNetworkSpecifier[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeIntArray(mSubIds);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(mSubIds);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof VcnUnderlyingNetworkSpecifier)) {
+ return false;
+ }
+
+ VcnUnderlyingNetworkSpecifier lhs = (VcnUnderlyingNetworkSpecifier) obj;
+ return Arrays.equals(mSubIds, lhs.mSubIds);
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("VcnUnderlyingNetworkSpecifier [")
+ .append("mSubIds = ").append(Arrays.toString(mSubIds))
+ .append("]")
+ .toString();
+ }
+
+ /** @hide */
+ @Override
+ public boolean canBeSatisfiedBy(NetworkSpecifier other) {
+ if (other instanceof TelephonyNetworkSpecifier) {
+ return ArrayUtils.contains(
+ mSubIds, ((TelephonyNetworkSpecifier) other).getSubscriptionId());
+ }
+ // TODO(b/180140053): Allow matching against WifiNetworkAgentSpecifier
+
+ // MatchAllNetworkSpecifier matched in NetworkCapabilities.
+ return equals(other);
+ }
+}
diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java
index a828077..bb40d90 100644
--- a/core/java/android/os/UidBatteryConsumer.java
+++ b/core/java/android/os/UidBatteryConsumer.java
@@ -29,21 +29,11 @@
private final int mUid;
@Nullable
private final String mPackageWithHighestDrain;
- private boolean mSystemComponent;
public int getUid() {
return mUid;
}
- /**
- * Returns true if this battery consumer is considered to be a part of the operating
- * system itself. For example, the UidBatteryConsumer with the UID {@link Process#BLUETOOTH_UID}
- * is a system component.
- */
- public boolean isSystemComponent() {
- return mSystemComponent;
- }
-
@Nullable
public String getPackageWithHighestDrain() {
return mPackageWithHighestDrain;
@@ -52,7 +42,6 @@
private UidBatteryConsumer(@NonNull Builder builder) {
super(builder.mPowerComponentsBuilder.build());
mUid = builder.mUid;
- mSystemComponent = builder.mSystemComponent;
mPackageWithHighestDrain = builder.mPackageWithHighestDrain;
}
@@ -95,7 +84,6 @@
private final BatteryStats.Uid mBatteryStatsUid;
private final int mUid;
private String mPackageWithHighestDrain;
- private boolean mSystemComponent;
private boolean mExcludeFromBatteryUsageStats;
public Builder(int customPowerComponentCount, int customTimeComponentCount,
diff --git a/core/java/android/speech/tts/ITextToSpeechManager.aidl b/core/java/android/speech/tts/ITextToSpeechManager.aidl
deleted file mode 100644
index e6b63df..0000000
--- a/core/java/android/speech/tts/ITextToSpeechManager.aidl
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.speech.tts;
-
-import android.speech.tts.ITextToSpeechSessionCallback;
-
-/**
- * TextToSpeechManagerService interface. Allows opening {@link TextToSpeech} session with the
- * specified provider proxied by the system service.
- *
- * @hide
- */
-oneway interface ITextToSpeechManager {
- void createSession(in String engine, in ITextToSpeechSessionCallback managerCallback);
-}
diff --git a/core/java/android/speech/tts/ITextToSpeechSessionCallback.aidl b/core/java/android/speech/tts/ITextToSpeechSessionCallback.aidl
deleted file mode 100644
index 545622a..0000000
--- a/core/java/android/speech/tts/ITextToSpeechSessionCallback.aidl
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.speech.tts;
-import android.speech.tts.ITextToSpeechSession;
-
-/**
- * Callback interface for a session created by {@link ITextToSpeechManager} API.
- *
- * @hide
- */
-oneway interface ITextToSpeechSessionCallback {
-
- void onConnected(in ITextToSpeechSession session, in IBinder serviceBinder);
-
- void onDisconnected();
-
- void onError(in String errorInfo);
-}
\ No newline at end of file
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 5d66dc7..7a18538 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -35,7 +35,6 @@
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.text.TextUtils;
import android.util.Log;
@@ -52,7 +51,6 @@
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Set;
-import java.util.concurrent.Executor;
/**
*
@@ -697,8 +695,6 @@
public static final String KEY_FEATURE_NETWORK_RETRIES_COUNT = "networkRetriesCount";
}
- private static final boolean DEBUG = false;
-
private final Context mContext;
@UnsupportedAppUsage
private Connection mConnectingServiceConnection;
@@ -720,9 +716,6 @@
private final Map<CharSequence, Uri> mUtterances;
private final Bundle mParams = new Bundle();
private final TtsEngines mEnginesHelper;
- private final boolean mIsSystem;
- @Nullable private final Executor mInitExecutor;
-
@UnsupportedAppUsage
private volatile String mCurrentEngine = null;
@@ -765,21 +758,8 @@
*/
public TextToSpeech(Context context, OnInitListener listener, String engine,
String packageName, boolean useFallback) {
- this(context, /* initExecutor= */ null, listener, engine, packageName,
- useFallback, /* isSystem= */ true);
- }
-
- /**
- * Used internally to instantiate TextToSpeech objects.
- *
- * @hide
- */
- private TextToSpeech(Context context, @Nullable Executor initExecutor,
- OnInitListener initListener, String engine, String packageName, boolean useFallback,
- boolean isSystem) {
mContext = context;
- mInitExecutor = initExecutor;
- mInitListener = initListener;
+ mInitListener = listener;
mRequestedEngine = engine;
mUseFallback = useFallback;
@@ -788,9 +768,6 @@
mUtteranceProgressListener = null;
mEnginesHelper = new TtsEngines(mContext);
-
- mIsSystem = isSystem;
-
initTts();
}
@@ -865,14 +842,10 @@
}
private boolean connectToEngine(String engine) {
- Connection connection;
- if (mIsSystem) {
- connection = new SystemConnection();
- } else {
- connection = new DirectConnection();
- }
-
- boolean bound = connection.connect(engine);
+ Connection connection = new Connection();
+ Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE);
+ intent.setPackage(engine);
+ boolean bound = mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE);
if (!bound) {
Log.e(TAG, "Failed to bind to " + engine);
return false;
@@ -884,19 +857,11 @@
}
private void dispatchOnInit(int result) {
- Runnable onInitCommand = () -> {
- synchronized (mStartLock) {
- if (mInitListener != null) {
- mInitListener.onInit(result);
- mInitListener = null;
- }
+ synchronized (mStartLock) {
+ if (mInitListener != null) {
+ mInitListener.onInit(result);
+ mInitListener = null;
}
- };
-
- if (mInitExecutor != null) {
- mInitExecutor.execute(onInitCommand);
- } else {
- onInitCommand.run();
}
}
@@ -2162,17 +2127,13 @@
return mEnginesHelper.getEngines();
}
- private abstract class Connection implements ServiceConnection {
+ private class Connection implements ServiceConnection {
private ITextToSpeechService mService;
private SetupConnectionAsyncTask mOnSetupConnectionAsyncTask;
private boolean mEstablished;
- abstract boolean connect(String engine);
-
- abstract void disconnect();
-
private final ITextToSpeechCallback.Stub mCallback =
new ITextToSpeechCallback.Stub() {
public void onStop(String utteranceId, boolean isStarted)
@@ -2238,6 +2199,11 @@
};
private class SetupConnectionAsyncTask extends AsyncTask<Void, Void, Integer> {
+ private final ComponentName mName;
+
+ public SetupConnectionAsyncTask(ComponentName name) {
+ mName = name;
+ }
@Override
protected Integer doInBackground(Void... params) {
@@ -2261,7 +2227,7 @@
mParams.putString(Engine.KEY_PARAM_VOICE_NAME, defaultVoiceName);
}
- Log.i(TAG, "Setting up the connection to TTS engine...");
+ Log.i(TAG, "Set up connection to " + mName);
return SUCCESS;
} catch (RemoteException re) {
Log.e(TAG, "Error connecting to service, setCallback() failed");
@@ -2283,11 +2249,11 @@
}
@Override
- public void onServiceConnected(ComponentName componentName, IBinder service) {
+ public void onServiceConnected(ComponentName name, IBinder service) {
synchronized(mStartLock) {
mConnectingServiceConnection = null;
- Log.i(TAG, "Connected to TTS engine");
+ Log.i(TAG, "Connected to " + name);
if (mOnSetupConnectionAsyncTask != null) {
mOnSetupConnectionAsyncTask.cancel(false);
@@ -2297,7 +2263,7 @@
mServiceConnection = Connection.this;
mEstablished = false;
- mOnSetupConnectionAsyncTask = new SetupConnectionAsyncTask();
+ mOnSetupConnectionAsyncTask = new SetupConnectionAsyncTask(name);
mOnSetupConnectionAsyncTask.execute();
}
}
@@ -2311,7 +2277,7 @@
*
* @return true if we cancel mOnSetupConnectionAsyncTask in progress.
*/
- protected boolean clearServiceConnection() {
+ private boolean clearServiceConnection() {
synchronized(mStartLock) {
boolean result = false;
if (mOnSetupConnectionAsyncTask != null) {
@@ -2329,8 +2295,8 @@
}
@Override
- public void onServiceDisconnected(ComponentName componentName) {
- Log.i(TAG, "Disconnected from TTS engine");
+ public void onServiceDisconnected(ComponentName name) {
+ Log.i(TAG, "Asked to disconnect from " + name);
if (clearServiceConnection()) {
/* We need to protect against a rare case where engine
* dies just after successful connection - and we process onServiceDisconnected
@@ -2342,6 +2308,11 @@
}
}
+ public void disconnect() {
+ mContext.unbindService(this);
+ clearServiceConnection();
+ }
+
public boolean isEstablished() {
return mService != null && mEstablished;
}
@@ -2371,91 +2342,6 @@
}
}
- // Currently all the clients are routed through the System connection. Direct connection
- // is left for debugging, testing and benchmarking purposes.
- // TODO(b/179599071): Remove direct connection once system one is fully tested.
- private class DirectConnection extends Connection {
- @Override
- boolean connect(String engine) {
- Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE);
- intent.setPackage(engine);
- return mContext.bindService(intent, this, Context.BIND_AUTO_CREATE);
- }
-
- @Override
- void disconnect() {
- mContext.unbindService(this);
- clearServiceConnection();
- }
- }
-
- private class SystemConnection extends Connection {
-
- @Nullable
- private volatile ITextToSpeechSession mSession;
-
- @Override
- boolean connect(String engine) {
- IBinder binder = ServiceManager.getService(Context.TEXT_TO_SPEECH_MANAGER_SERVICE);
-
- ITextToSpeechManager manager = ITextToSpeechManager.Stub.asInterface(binder);
-
- if (manager == null) {
- Log.e(TAG, "System service is not available!");
- return false;
- }
-
- if (DEBUG) {
- Log.d(TAG, "Connecting to engine: " + engine);
- }
-
- try {
- manager.createSession(engine, new ITextToSpeechSessionCallback.Stub() {
- @Override
- public void onConnected(ITextToSpeechSession session, IBinder serviceBinder) {
- mSession = session;
- onServiceConnected(
- /* componentName= */ null,
- serviceBinder);
- }
-
- @Override
- public void onDisconnected() {
- onServiceDisconnected(/* componentName= */ null);
- }
-
- @Override
- public void onError(String errorInfo) {
- Log.w(TAG, "System TTS connection error: " + errorInfo);
- // The connection was not established successfully - handle as
- // disconnection: clear the state and notify the user.
- onServiceDisconnected(/* componentName= */ null);
- }
- });
-
- return true;
- } catch (RemoteException ex) {
- Log.e(TAG, "Error communicating with the System Server: ", ex);
- throw ex.rethrowFromSystemServer();
- }
- }
-
- @Override
- void disconnect() {
- ITextToSpeechSession session = mSession;
-
- if (session != null) {
- try {
- session.disconnect();
- } catch (RemoteException ex) {
- Log.w(TAG, "Error disconnecting session", ex);
- }
-
- clearServiceConnection();
- }
- }
- }
-
private interface Action<R> {
R run(ITextToSpeechService service) throws RemoteException;
}
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index 95999a7..fe3042d 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -74,6 +74,7 @@
private static final int GMSCORE_LASTK_LOG_SIZE = 196608;
private static final String TAG_TOMBSTONE = "SYSTEM_TOMBSTONE";
+ private static final String TAG_TOMBSTONE_PROTO = "SYSTEM_TOMBSTONE_PROTO";
// The pre-froyo package and class of the system updater, which
// ran in the system process. We need to remove its packages here
@@ -251,14 +252,14 @@
* @param ctx Context
* @param tombstone path to the tombstone
*/
- public static void addTombstoneToDropBox(Context ctx, File tombstone) {
+ public static void addTombstoneToDropBox(Context ctx, File tombstone, boolean proto) {
final DropBoxManager db = ctx.getSystemService(DropBoxManager.class);
final String bootReason = SystemProperties.get("ro.boot.bootreason", null);
HashMap<String, Long> timestamps = readTimestamps();
try {
final String headers = getBootHeadersToLogAndUpdate();
addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE,
- TAG_TOMBSTONE);
+ proto ? TAG_TOMBSTONE_PROTO : TAG_TOMBSTONE);
} catch (IOException e) {
Slog.e(TAG, "Can't log tombstone", e);
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 18cc3ac..c61802d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3905,6 +3905,12 @@
<permission android:name="android.permission.SET_KEYBOARD_LAYOUT"
android:protectionLevel="signature" />
+ <!-- Allows an app to use exact alarm scheduling APIs to perform timing
+ sensitive background work.
+ -->
+ <permission android:name="android.permission.SCHEDULE_EXACT_ALARM"
+ android:protectionLevel="normal|appop"/>
+
<!-- Allows an application to query tablet mode state and monitor changes
in it.
<p>Not for use by third-party applications.
diff --git a/data/etc/com.android.emergency.xml b/data/etc/com.android.emergency.xml
index fa92b6d..2d6ae2e 100644
--- a/data/etc/com.android.emergency.xml
+++ b/data/etc/com.android.emergency.xml
@@ -21,5 +21,7 @@
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
<permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
+ <!-- Required to update emergency gesture settings -->
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 84da930..5633de3 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -259,6 +259,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1834214907": {
+ "message": "createNonAppWindowAnimations()",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
"-1824578273": {
"message": "Reporting new frame to %s: %s",
"level": "VERBOSE",
@@ -811,6 +817,18 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimation.java"
},
+ "-1153814764": {
+ "message": "onAnimationCancelled",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/NonAppWindowAnimationAdapter.java"
+ },
+ "-1144293044": {
+ "message": "SURFACE SET FREEZE LAYER: %s",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
"-1142279614": {
"message": "Looking for focus: %s, flags=%d, canReceive=%b, reason=%s",
"level": "VERBOSE",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index 73371e7..56fe126 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -75,7 +75,7 @@
*/
public static final int SPRING_TO_TOUCH_STIFFNESS = 12000;
public static final float IME_ANIMATION_STIFFNESS = SpringForce.STIFFNESS_LOW;
- private static final int CHAIN_STIFFNESS = 600;
+ private static final int CHAIN_STIFFNESS = 800;
public static final float DEFAULT_BOUNCINESS = 0.9f;
private final PhysicsAnimator.SpringConfig mAnimateOutSpringConfig =
diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS
index 2c6c7b3..d80699d 100644
--- a/libs/WindowManager/Shell/tests/OWNERS
+++ b/libs/WindowManager/Shell/tests/OWNERS
@@ -1,2 +1,3 @@
+# Bug component: 909476
# includes OWNERS from parent directories
[email protected]
diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp
index ce9b288..5d3f6f2 100644
--- a/libs/hwui/FrameInfo.cpp
+++ b/libs/hwui/FrameInfo.cpp
@@ -40,6 +40,7 @@
"DequeueBufferDuration",
"QueueBufferDuration",
"GpuCompleted",
+ "SwapBuffersCompleted"
};
static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 20,
diff --git a/media/OWNERS b/media/OWNERS
index e741490..abfc8bf 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -26,3 +26,7 @@
# SEO
[email protected]
+
+# SEA/KIR/BVE
[email protected]
[email protected]
diff --git a/packages/Connectivity/framework/Android.bp b/packages/Connectivity/framework/Android.bp
index 8db8d76..73e1511 100644
--- a/packages/Connectivity/framework/Android.bp
+++ b/packages/Connectivity/framework/Android.bp
@@ -14,15 +14,37 @@
// limitations under the License.
//
-// TODO: use a java_library in the bootclasspath instead
filegroup {
- name: "framework-connectivity-sources",
+ name: "framework-connectivity-internal-sources",
srcs: [
"src/**/*.java",
"src/**/*.aidl",
],
path: "src",
visibility: [
+ "//visibility:private",
+ ],
+}
+
+filegroup {
+ name: "framework-connectivity-aidl-export-sources",
+ srcs: [
+ "aidl-export/**/*.aidl",
+ ],
+ path: "aidl-export",
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
+// TODO: use a java_library in the bootclasspath instead
+filegroup {
+ name: "framework-connectivity-sources",
+ srcs: [
+ ":framework-connectivity-internal-sources",
+ ":framework-connectivity-aidl-export-sources",
+ ],
+ visibility: [
"//frameworks/base",
"//packages/modules/Connectivity:__subpackages__",
],
diff --git a/packages/Connectivity/framework/src/android/net/CaptivePortalData.aidl b/packages/Connectivity/framework/aidl-export/android/net/CaptivePortalData.aidl
similarity index 100%
rename from packages/Connectivity/framework/src/android/net/CaptivePortalData.aidl
rename to packages/Connectivity/framework/aidl-export/android/net/CaptivePortalData.aidl
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.aidl b/packages/Connectivity/framework/aidl-export/android/net/ConnectivityDiagnosticsManager.aidl
similarity index 100%
rename from packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.aidl
rename to packages/Connectivity/framework/aidl-export/android/net/ConnectivityDiagnosticsManager.aidl
diff --git a/packages/Connectivity/framework/src/android/net/DhcpInfo.aidl b/packages/Connectivity/framework/aidl-export/android/net/DhcpInfo.aidl
similarity index 100%
rename from packages/Connectivity/framework/src/android/net/DhcpInfo.aidl
rename to packages/Connectivity/framework/aidl-export/android/net/DhcpInfo.aidl
diff --git a/packages/Connectivity/framework/src/android/net/IpConfiguration.aidl b/packages/Connectivity/framework/aidl-export/android/net/IpConfiguration.aidl
similarity index 100%
rename from packages/Connectivity/framework/src/android/net/IpConfiguration.aidl
rename to packages/Connectivity/framework/aidl-export/android/net/IpConfiguration.aidl
diff --git a/packages/Connectivity/framework/src/android/net/IpPrefix.aidl b/packages/Connectivity/framework/aidl-export/android/net/IpPrefix.aidl
similarity index 100%
rename from packages/Connectivity/framework/src/android/net/IpPrefix.aidl
rename to packages/Connectivity/framework/aidl-export/android/net/IpPrefix.aidl
diff --git a/packages/Connectivity/framework/src/android/net/KeepalivePacketData.aidl b/packages/Connectivity/framework/aidl-export/android/net/KeepalivePacketData.aidl
similarity index 100%
rename from packages/Connectivity/framework/src/android/net/KeepalivePacketData.aidl
rename to packages/Connectivity/framework/aidl-export/android/net/KeepalivePacketData.aidl
diff --git a/packages/Connectivity/framework/src/android/net/LinkAddress.aidl b/packages/Connectivity/framework/aidl-export/android/net/LinkAddress.aidl
similarity index 100%
rename from packages/Connectivity/framework/src/android/net/LinkAddress.aidl
rename to packages/Connectivity/framework/aidl-export/android/net/LinkAddress.aidl
diff --git a/packages/Connectivity/framework/src/android/net/LinkProperties.aidl b/packages/Connectivity/framework/aidl-export/android/net/LinkProperties.aidl
similarity index 100%
rename from packages/Connectivity/framework/src/android/net/LinkProperties.aidl
rename to packages/Connectivity/framework/aidl-export/android/net/LinkProperties.aidl
diff --git a/packages/Connectivity/framework/src/android/net/MacAddress.aidl b/packages/Connectivity/framework/aidl-export/android/net/MacAddress.aidl
similarity index 100%
rename from packages/Connectivity/framework/src/android/net/MacAddress.aidl
rename to packages/Connectivity/framework/aidl-export/android/net/MacAddress.aidl
diff --git a/packages/Connectivity/framework/src/android/net/Network.aidl b/packages/Connectivity/framework/aidl-export/android/net/Network.aidl
similarity index 100%
rename from packages/Connectivity/framework/src/android/net/Network.aidl
rename to packages/Connectivity/framework/aidl-export/android/net/Network.aidl
diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.aidl b/packages/Connectivity/framework/aidl-export/android/net/NetworkAgentConfig.aidl
similarity index 100%
rename from packages/Connectivity/framework/src/android/net/NetworkAgentConfig.aidl
rename to packages/Connectivity/framework/aidl-export/android/net/NetworkAgentConfig.aidl
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.aidl b/packages/Connectivity/framework/aidl-export/android/net/NetworkCapabilities.aidl
similarity index 100%
rename from packages/Connectivity/framework/src/android/net/NetworkCapabilities.aidl
rename to packages/Connectivity/framework/aidl-export/android/net/NetworkCapabilities.aidl
diff --git a/packages/Connectivity/framework/src/android/net/NetworkInfo.aidl b/packages/Connectivity/framework/aidl-export/android/net/NetworkInfo.aidl
similarity index 100%
rename from packages/Connectivity/framework/src/android/net/NetworkInfo.aidl
rename to packages/Connectivity/framework/aidl-export/android/net/NetworkInfo.aidl
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.aidl b/packages/Connectivity/framework/aidl-export/android/net/NetworkRequest.aidl
similarity index 100%
rename from packages/Connectivity/framework/src/android/net/NetworkRequest.aidl
rename to packages/Connectivity/framework/aidl-export/android/net/NetworkRequest.aidl
diff --git a/packages/Connectivity/framework/src/android/net/ProxyInfo.aidl b/packages/Connectivity/framework/aidl-export/android/net/ProxyInfo.aidl
similarity index 100%
rename from packages/Connectivity/framework/src/android/net/ProxyInfo.aidl
rename to packages/Connectivity/framework/aidl-export/android/net/ProxyInfo.aidl
diff --git a/packages/Connectivity/framework/src/android/net/RouteInfo.aidl b/packages/Connectivity/framework/aidl-export/android/net/RouteInfo.aidl
similarity index 100%
rename from packages/Connectivity/framework/src/android/net/RouteInfo.aidl
rename to packages/Connectivity/framework/aidl-export/android/net/RouteInfo.aidl
diff --git a/packages/Connectivity/framework/src/android/net/StaticIpConfiguration.aidl b/packages/Connectivity/framework/aidl-export/android/net/StaticIpConfiguration.aidl
similarity index 100%
rename from packages/Connectivity/framework/src/android/net/StaticIpConfiguration.aidl
rename to packages/Connectivity/framework/aidl-export/android/net/StaticIpConfiguration.aidl
diff --git a/packages/Connectivity/framework/src/android/net/TestNetworkInterface.aidl b/packages/Connectivity/framework/aidl-export/android/net/TestNetworkInterface.aidl
similarity index 100%
rename from packages/Connectivity/framework/src/android/net/TestNetworkInterface.aidl
rename to packages/Connectivity/framework/aidl-export/android/net/TestNetworkInterface.aidl
diff --git a/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.aidl b/packages/Connectivity/framework/aidl-export/android/net/apf/ApfCapabilities.aidl
similarity index 100%
rename from packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.aidl
rename to packages/Connectivity/framework/aidl-export/android/net/apf/ApfCapabilities.aidl
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
index 81ca9ea..228de03 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
@@ -163,8 +163,12 @@
long locationAccessFinishTime = 0L;
// Earliest time for a location access to end and still be shown in list.
long recentLocationCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS;
+ // Compute the most recent access time from all op entries.
for (AppOpsManager.OpEntry entry : entries) {
- locationAccessFinishTime = entry.getLastAccessTime(TRUSTED_STATE_FLAGS);
+ long lastAccessTime = entry.getLastAccessTime(TRUSTED_STATE_FLAGS);
+ if (lastAccessTime > locationAccessFinishTime) {
+ locationAccessFinishTime = lastAccessTime;
+ }
}
// Bail out if the entry is out of date.
if (locationAccessFinishTime < recentLocationCutoffTime) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
index 40c2386..fc89783 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
@@ -32,6 +32,8 @@
import com.android.systemui.controls.CustomIconCache
import com.android.systemui.controls.controller.ControlsControllerImpl
import com.android.systemui.controls.controller.StructureInfo
+import com.android.systemui.controls.ui.ControlsDialog
+import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.globalactions.GlobalActionsComponent
import com.android.systemui.settings.CurrentUserTracker
import com.android.systemui.util.LifecycleActivity
@@ -42,9 +44,10 @@
*/
class ControlsEditingActivity @Inject constructor(
private val controller: ControlsControllerImpl,
- broadcastDispatcher: BroadcastDispatcher,
+ private val broadcastDispatcher: BroadcastDispatcher,
private val globalActionsComponent: GlobalActionsComponent,
- private val customIconCache: CustomIconCache
+ private val customIconCache: CustomIconCache,
+ private val uiController: ControlsUiController
) : LifecycleActivity() {
companion object {
@@ -59,6 +62,7 @@
private lateinit var model: FavoritesModel
private lateinit var subtitle: TextView
private lateinit var saveButton: View
+ private var backToGlobalActions = true
private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
private val startingUser = controller.currentUserId
@@ -82,6 +86,11 @@
structure = it
} ?: run(this::finish)
+ backToGlobalActions = intent.getBooleanExtra(
+ ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
+ true
+ )
+
bindViews()
bindButtons()
@@ -100,7 +109,11 @@
}
override fun onBackPressed() {
- globalActionsComponent.handleShowGlobalActionsMenu()
+ if (backToGlobalActions) {
+ globalActionsComponent.handleShowGlobalActionsMenu()
+ } else {
+ ControlsDialog(applicationContext, broadcastDispatcher).show(uiController)
+ }
animateExitAndFinish()
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index b282157..1c2f17c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -40,6 +40,8 @@
import com.android.systemui.controls.TooltipManager
import com.android.systemui.controls.controller.ControlsControllerImpl
import com.android.systemui.controls.controller.StructureInfo
+import com.android.systemui.controls.ui.ControlsDialog
+import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.globalactions.GlobalActionsComponent
import com.android.systemui.settings.CurrentUserTracker
@@ -53,8 +55,9 @@
@Main private val executor: Executor,
private val controller: ControlsControllerImpl,
private val listingController: ControlsListingController,
- broadcastDispatcher: BroadcastDispatcher,
- private val globalActionsComponent: GlobalActionsComponent
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val globalActionsComponent: GlobalActionsComponent,
+ private val uiController: ControlsUiController
) : LifecycleActivity() {
companion object {
@@ -89,6 +92,7 @@
private lateinit var comparator: Comparator<StructureContainer>
private var cancelLoadRunnable: Runnable? = null
private var isPagerLoaded = false
+ private var backToGlobalActions = true
private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
private val startingUser = controller.currentUserId
@@ -114,7 +118,7 @@
override fun onBackPressed() {
if (!fromProviderSelector) {
- globalActionsComponent.handleShowGlobalActionsMenu()
+ openControlsOrigin()
}
animateExitAndFinish()
}
@@ -129,6 +133,11 @@
component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)
fromProviderSelector = intent.getBooleanExtra(EXTRA_FROM_PROVIDER_SELECTOR, false)
+ backToGlobalActions = intent.getBooleanExtra(
+ ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
+ true
+ )
+
bindViews()
}
@@ -330,11 +339,19 @@
)
}
animateExitAndFinish()
- globalActionsComponent.handleShowGlobalActionsMenu()
+ openControlsOrigin()
}
}
}
+ private fun openControlsOrigin() {
+ if (backToGlobalActions) {
+ globalActionsComponent.handleShowGlobalActionsMenu()
+ } else {
+ ControlsDialog(applicationContext, broadcastDispatcher).show(uiController)
+ }
+ }
+
override fun onPause() {
super.onPause()
mTooltipManager?.hide(false)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
index db68d17..537334a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
@@ -67,7 +67,7 @@
val vg = requireViewById<ViewGroup>(com.android.systemui.R.id.global_actions_controls)
vg.alpha = 0f
- controller.show(vg, { /* do nothing */ }, false /* startedFromGlobalActions */)
+ controller.show(vg, { dismiss() }, false /* startedFromGlobalActions */)
vg.animate()
.alpha(1f)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
index 9448877..20bdf60 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
@@ -27,6 +27,7 @@
companion object {
public const val TAG = "ControlsUiController"
public const val EXTRA_ANIMATE = "extra_animate"
+ public const val BACK_TO_GLOBAL_ACTIONS = "back_to_global_actions"
}
fun show(parent: ViewGroup, onDismiss: Runnable, startedFromGlobalActions: Boolean)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 762362c..c94d85a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -266,6 +266,10 @@
private fun startActivity(context: Context, intent: Intent) {
// Force animations when transitioning from a dialog to an activity
intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true)
+ intent.putExtra(
+ ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
+ controlActionCoordinator.startedFromGlobalActions
+ )
onDismiss.run()
activityStarter.dismissKeyguardThenExecute({
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index dab4d0b..1660deab 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -855,6 +855,14 @@
@Override
public void onRotationProposal(final int rotation, boolean isValid) {
+ if (mNavigationBarView == null) {
+ if (RotationContextButton.DEBUG_ROTATION) {
+ Log.v(TAG, "onRotationProposal proposedRotation=" +
+ Surface.rotationToString(rotation) + ", mNavigationBarView is null");
+ }
+ return;
+ }
+
final int winRotation = mNavigationBarView.getDisplay().getRotation();
final boolean rotateSuggestionsDisabled = RotationButtonController
.hasDisable2RotateSuggestionFlag(mDisabledFlags2);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 87252ff..a0bf584 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -90,6 +90,7 @@
private OngoingPrivacyChip mPrivacyChip;
private Space mSpace;
private BatteryMeterView mBatteryRemainingIcon;
+ private TintedIconManager mTintedIconManager;
// Used for RingerModeTracker
private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
@@ -144,6 +145,7 @@
}
void onAttach(TintedIconManager iconManager) {
+ mTintedIconManager = iconManager;
int fillColor = Utils.getColorAttrDefaultColor(getContext(),
android.R.attr.textColorPrimary);
@@ -268,6 +270,9 @@
android.R.attr.textColorSecondary);
mTextColorPrimary = textColor;
mClockView.setTextColor(textColor);
+ if (mTintedIconManager != null) {
+ mTintedIconManager.setTint(textColor);
+ }
mBatteryRemainingIcon.updateColors(mTextColorPrimary, textColorSecondary,
mTextColorPrimary);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index d562726..138c811 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -24,7 +24,6 @@
import android.content.Context;
import android.content.res.ColorStateList;
-import android.graphics.Color;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.FeatureFlagUtils;
@@ -238,11 +237,7 @@
@Override
public void setStaticDrawableColor(int color) {
ColorStateList list = ColorStateList.valueOf(color);
- float intensity = color == Color.WHITE ? 0 : 1;
- // We want the ability to change the theme from the one set by SignalDrawable in certain
- // surfaces. In this way, we can pass a theme to the view.
- mMobileDrawable.setTintList(
- ColorStateList.valueOf(mDualToneHandler.getSingleColor(intensity)));
+ mMobileDrawable.setTintList(list);
mIn.setImageTintList(list);
mOut.setImageTintList(list);
mMobileType.setImageTintList(list);
diff --git a/packages/services/CameraExtensionsProxy/Android.bp b/packages/services/CameraExtensionsProxy/Android.bp
index 54b7453..e2e4af2 100644
--- a/packages/services/CameraExtensionsProxy/Android.bp
+++ b/packages/services/CameraExtensionsProxy/Android.bp
@@ -18,6 +18,7 @@
name: "CameraExtensionsProxy",
srcs: ["src/**/*.java"],
libs: ["androidx.camera.extensions.stub"],
+ optional_uses_libs: ["androidx.camera.extensions.impl"],
platform_apis: true,
certificate: "platform",
}
diff --git a/services/Android.bp b/services/Android.bp
index 3154628..8aae8e6 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -6,7 +6,7 @@
}
filegroup {
- name: "services-all-sources",
+ name: "services-non-updatable-sources",
srcs: [
":services.core-sources",
":services.core-sources-am-wm",
@@ -33,12 +33,19 @@
":services.startop.iorap-sources",
":services.systemcaptions-sources",
":services.translation-sources",
- ":services.texttospeech-sources",
":services.usage-sources",
":services.usb-sources",
":services.voiceinteraction-sources",
":services.wifi-sources",
- ":service-media-s-sources", // TODO (b/177640454)
+ ],
+ visibility: ["//visibility:private"],
+}
+
+filegroup {
+ name: "services-all-sources",
+ srcs: [
+ ":services-non-updatable-sources",
+ ":service-media-s-sources",
":service-permission-sources",
":service-statsd-sources",
],
@@ -84,7 +91,6 @@
"services.startop",
"services.systemcaptions",
"services.translation",
- "services.texttospeech",
"services.usage",
"services.usb",
"services.voiceinteraction",
@@ -125,9 +131,8 @@
// API stub
// =============================================================
-droidstubs {
- name: "services-stubs.sources",
- srcs: [":services-all-sources"],
+stubs_defaults {
+ name: "services-stubs-default",
installable: false,
args: " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.SYSTEM_SERVER\\)" +
" --hide-annotation android.annotation.Hide" +
@@ -137,7 +142,13 @@
" --hide DeprecationMismatch" +
" --hide HiddenTypedefConstant",
visibility: ["//visibility:private"],
- filter_packages: ["com.android."],
+ filter_packages: ["com.android."]
+}
+
+droidstubs {
+ name: "services-stubs.sources",
+ srcs: [":services-all-sources"],
+ defaults: ["services-stubs-default"],
check_api: {
current: {
api_file: "api/current.txt",
@@ -183,3 +194,34 @@
dir: "apistubs/android/system-server",
},
}
+
+droidstubs {
+ name: "services-non-updatable-stubs.sources",
+ srcs: [":services-non-updatable-sources"],
+ defaults: ["services-stubs-default"],
+ check_api: {
+ current: {
+ api_file: "api/non-updatable-current.txt",
+ removed_api_file: "api/non-updatable-removed.txt",
+ },
+ api_lint: {
+ enabled: true,
+ new_since: ":android-non-updatable.api.system-server.latest",
+ baseline_file: "api/non-updatable-lint-baseline.txt",
+ },
+ },
+ dists: [
+ {
+ targets: ["sdk", "win_sdk"],
+ dir: "apistubs/android/system-server/api",
+ dest: "android-non-updatable.txt",
+ tag: ".api.txt"
+ },
+ {
+ targets: ["sdk", "win_sdk"],
+ dir: "apistubs/android/system-server/api",
+ dest: "android-non-updatable-removed.txt",
+ tag: ".removed-api.txt",
+ },
+ ]
+}
\ No newline at end of file
diff --git a/services/api/non-updatable-current.txt b/services/api/non-updatable-current.txt
new file mode 100644
index 0000000..3c72d38
--- /dev/null
+++ b/services/api/non-updatable-current.txt
@@ -0,0 +1,55 @@
+// Signature format: 2.0
+package com.android.server {
+
+ public final class LocalManagerRegistry {
+ method public static <T> void addManager(@NonNull Class<T>, @NonNull T);
+ method @Nullable public static <T> T getManager(@NonNull Class<T>);
+ }
+
+ public abstract class SystemService {
+ ctor public SystemService(@NonNull android.content.Context);
+ method @NonNull public final android.content.Context getContext();
+ method public boolean isUserSupported(@NonNull com.android.server.SystemService.TargetUser);
+ method public void onBootPhase(int);
+ method public abstract void onStart();
+ method public void onUserStarting(@NonNull com.android.server.SystemService.TargetUser);
+ method public void onUserStopped(@NonNull com.android.server.SystemService.TargetUser);
+ method public void onUserStopping(@NonNull com.android.server.SystemService.TargetUser);
+ method public void onUserSwitching(@Nullable com.android.server.SystemService.TargetUser, @NonNull com.android.server.SystemService.TargetUser);
+ method public void onUserUnlocked(@NonNull com.android.server.SystemService.TargetUser);
+ method public void onUserUnlocking(@NonNull com.android.server.SystemService.TargetUser);
+ method protected final void publishBinderService(@NonNull String, @NonNull android.os.IBinder);
+ method protected final void publishBinderService(@NonNull String, @NonNull android.os.IBinder, boolean);
+ field public static final int PHASE_ACTIVITY_MANAGER_READY = 550; // 0x226
+ field public static final int PHASE_BOOT_COMPLETED = 1000; // 0x3e8
+ field public static final int PHASE_DEVICE_SPECIFIC_SERVICES_READY = 520; // 0x208
+ field public static final int PHASE_LOCK_SETTINGS_READY = 480; // 0x1e0
+ field public static final int PHASE_SYSTEM_SERVICES_READY = 500; // 0x1f4
+ field public static final int PHASE_THIRD_PARTY_APPS_CAN_START = 600; // 0x258
+ field public static final int PHASE_WAIT_FOR_DEFAULT_DISPLAY = 100; // 0x64
+ }
+
+ public static final class SystemService.TargetUser {
+ method @NonNull public android.os.UserHandle getUserHandle();
+ }
+
+}
+
+package com.android.server.role {
+
+ public interface RoleServicePlatformHelper {
+ method @NonNull public String computePackageStateHash(int);
+ method @NonNull public java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getLegacyRoleState(int);
+ }
+
+}
+
+package com.android.server.wifi {
+
+ public class SupplicantManager {
+ method public static void start();
+ method public static void stop();
+ }
+
+}
+
diff --git a/services/api/non-updatable-lint-baseline.txt b/services/api/non-updatable-lint-baseline.txt
new file mode 100644
index 0000000..b46d21e
--- /dev/null
+++ b/services/api/non-updatable-lint-baseline.txt
@@ -0,0 +1,9 @@
+// Baseline format: 1.0
+NotCloseable: com.android.server.wifi.SupplicantManager:
+ Classes that release resources (stop()) should implement AutoClosable and CloseGuard: class com.android.server.wifi.SupplicantManager
+
+
+ProtectedMember: com.android.server.SystemService#publishBinderService(String, android.os.IBinder):
+ Protected methods not allowed; must be public: method com.android.server.SystemService.publishBinderService(String,android.os.IBinder)}
+ProtectedMember: com.android.server.SystemService#publishBinderService(String, android.os.IBinder, boolean):
+ Protected methods not allowed; must be public: method com.android.server.SystemService.publishBinderService(String,android.os.IBinder,boolean)}
diff --git a/services/api/non-updatable-removed.txt b/services/api/non-updatable-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/services/api/non-updatable-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 154e183..5077cc6 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3721,7 +3721,12 @@
// Looking up the app passed param request in mRequests isn't possible since it may return
// null for a request managed by a per-app default. Therefore use getNriForAppRequest() to
// do the lookup since that will also find per-app default managed requests.
- final NetworkRequestInfo nri = getNriForAppRequest(request);
+ // Additionally, this lookup needs to be relatively fast (hence the lookup optimization)
+ // to avoid potential race conditions when validating a package->uid mapping when sending
+ // the callback on the very low-chance that an application shuts down prior to the callback
+ // being sent.
+ final NetworkRequestInfo nri = mNetworkRequests.get(request) != null
+ ? mNetworkRequests.get(request) : getNriForAppRequest(request);
if (nri != null) {
if (Process.SYSTEM_UID != callingUid && Process.NETWORK_STACK_UID != callingUid
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index ed4e1d9..329ab99 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -33,6 +33,7 @@
import android.net.vcn.IVcnStatusCallback;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
+import android.net.vcn.VcnManager.VcnErrorCode;
import android.net.vcn.VcnUnderlyingNetworkPolicy;
import android.net.wifi.WifiInfo;
import android.os.Binder;
@@ -304,8 +305,8 @@
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config,
@NonNull TelephonySubscriptionSnapshot snapshot,
- @NonNull VcnSafeModeCallback safeModeCallback) {
- return new Vcn(vcnContext, subscriptionGroup, config, snapshot, safeModeCallback);
+ @NonNull VcnCallback vcnCallback) {
+ return new Vcn(vcnContext, subscriptionGroup, config, snapshot, vcnCallback);
}
/** Gets the subId indicated by the given {@link WifiInfo}. */
@@ -457,12 +458,10 @@
// TODO(b/176939047): Support multiple VCNs active at the same time, or limit to one active
// VCN.
- final VcnSafeModeCallbackImpl safeModeCallback =
- new VcnSafeModeCallbackImpl(subscriptionGroup);
+ final VcnCallbackImpl vcnCallback = new VcnCallbackImpl(subscriptionGroup);
final Vcn newInstance =
- mDeps.newVcn(
- mVcnContext, subscriptionGroup, config, mLastSnapshot, safeModeCallback);
+ mDeps.newVcn(mVcnContext, subscriptionGroup, config, mLastSnapshot, vcnCallback);
mVcns.put(subscriptionGroup, newInstance);
// Now that a new VCN has started, notify all registered listeners to refresh their
@@ -784,20 +783,47 @@
}
// TODO(b/180452282): Make name more generic and implement directly with VcnManagementService
- /** Callback for signalling when a Vcn has entered safe mode. */
- public interface VcnSafeModeCallback {
+ /** Callback for Vcn signals sent up to VcnManagementService. */
+ public interface VcnCallback {
/** Called by a Vcn to signal that it has entered safe mode. */
void onEnteredSafeMode();
+
+ /** Called by a Vcn to signal that an error occurred. */
+ void onGatewayConnectionError(
+ @NonNull int[] networkCapabilities,
+ @VcnErrorCode int errorCode,
+ @Nullable String exceptionClass,
+ @Nullable String exceptionMessage);
}
- /** VcnSafeModeCallback is used by Vcns to notify VcnManagementService on entering safe mode. */
- private class VcnSafeModeCallbackImpl implements VcnSafeModeCallback {
+ /** VcnCallbackImpl for Vcn signals sent up to VcnManagementService. */
+ private class VcnCallbackImpl implements VcnCallback {
@NonNull private final ParcelUuid mSubGroup;
- private VcnSafeModeCallbackImpl(@NonNull final ParcelUuid subGroup) {
+ private VcnCallbackImpl(@NonNull final ParcelUuid subGroup) {
mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup");
}
+ private boolean isCallbackPermissioned(@NonNull VcnStatusCallbackInfo cbInfo) {
+ if (!mSubGroup.equals(cbInfo.mSubGroup)) {
+ return false;
+ }
+
+ if (!mLastSnapshot.packageHasPermissionsForSubscriptionGroup(
+ mSubGroup, cbInfo.mPkgName)) {
+ return false;
+ }
+
+ if (!mLocationPermissionChecker.checkLocationPermission(
+ cbInfo.mPkgName,
+ "VcnStatusCallback" /* featureId */,
+ cbInfo.mUid,
+ null /* message */)) {
+ return false;
+ }
+ return true;
+ }
+
@Override
public void onEnteredSafeMode() {
synchronized (mLock) {
@@ -810,23 +836,36 @@
// Notify all registered StatusCallbacks for this subGroup
for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) {
- if (!mSubGroup.equals(cbInfo.mSubGroup)) {
- continue;
+ if (isCallbackPermissioned(cbInfo)) {
+ Binder.withCleanCallingIdentity(() -> cbInfo.mCallback.onEnteredSafeMode());
}
- if (!mLastSnapshot.packageHasPermissionsForSubscriptionGroup(
- mSubGroup, cbInfo.mPkgName)) {
- continue;
- }
+ }
+ }
+ }
- if (!mLocationPermissionChecker.checkLocationPermission(
- cbInfo.mPkgName,
- "VcnStatusCallback" /* featureId */,
- cbInfo.mUid,
- null /* message */)) {
- continue;
- }
+ @Override
+ public void onGatewayConnectionError(
+ @NonNull int[] networkCapabilities,
+ @VcnErrorCode int errorCode,
+ @Nullable String exceptionClass,
+ @Nullable String exceptionMessage) {
+ synchronized (mLock) {
+ // Ignore if this subscription group doesn't exist anymore
+ if (!mVcns.containsKey(mSubGroup)) {
+ return;
+ }
- Binder.withCleanCallingIdentity(() -> cbInfo.mCallback.onEnteredSafeMode());
+ // Notify all registered StatusCallbacks for this subGroup
+ for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) {
+ if (isCallbackPermissioned(cbInfo)) {
+ Binder.withCleanCallingIdentity(
+ () ->
+ cbInfo.mCallback.onGatewayConnectionError(
+ networkCapabilities,
+ errorCode,
+ exceptionClass,
+ exceptionMessage));
+ }
}
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0b1c115..5ee0e04 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -130,6 +130,7 @@
import static com.android.server.wm.ActivityTaskManagerService.DUMP_RECENTS_CMD;
import static com.android.server.wm.ActivityTaskManagerService.DUMP_RECENTS_SHORT_CMD;
import static com.android.server.wm.ActivityTaskManagerService.DUMP_STARTER_CMD;
+import static com.android.server.wm.ActivityTaskManagerService.DUMP_TOP_RESUMED_ACTIVITY;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
import static com.android.server.wm.ActivityTaskManagerService.relaunchReasonToString;
@@ -353,6 +354,7 @@
import com.android.server.firewall.IntentFirewall;
import com.android.server.graphics.fonts.FontManagerInternal;
import com.android.server.job.JobSchedulerInternal;
+import com.android.server.os.NativeTombstoneManager;
import com.android.server.pm.Installer;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.uri.GrantUri;
@@ -8273,6 +8275,9 @@
mUserController.handleIncomingUser(callingPid, callingUid, userId, true, ALLOW_NON_FULL,
"getHistoricalProcessExitReasons", null);
+ NativeTombstoneManager tombstoneService = LocalServices.getService(
+ NativeTombstoneManager.class);
+
final ArrayList<ApplicationExitInfo> results = new ArrayList<ApplicationExitInfo>();
if (!TextUtils.isEmpty(packageName)) {
final int uid = enforceDumpPermissionForPackage(packageName, userId, callingUid,
@@ -8280,11 +8285,13 @@
if (uid != Process.INVALID_UID) {
mProcessList.mAppExitInfoTracker.getExitInfo(
packageName, uid, pid, maxNum, results);
+ tombstoneService.collectTombstones(results, uid, pid, maxNum);
}
} else {
// If no package name is given, use the caller's uid as the filter uid.
mProcessList.mAppExitInfoTracker.getExitInfo(
packageName, callingUid, pid, maxNum, results);
+ tombstoneService.collectTombstones(results, callingUid, pid, maxNum);
}
return new ParceledListSlice<ApplicationExitInfo>(results);
@@ -8668,7 +8675,8 @@
if (DUMP_ACTIVITIES_CMD.equals(cmd) || DUMP_ACTIVITIES_SHORT_CMD.equals(cmd)
|| DUMP_LASTANR_CMD.equals(cmd) || DUMP_LASTANR_TRACES_CMD.equals(cmd)
|| DUMP_STARTER_CMD.equals(cmd) || DUMP_CONTAINERS_CMD.equals(cmd)
- || DUMP_RECENTS_CMD.equals(cmd) || DUMP_RECENTS_SHORT_CMD.equals(cmd)) {
+ || DUMP_RECENTS_CMD.equals(cmd) || DUMP_RECENTS_SHORT_CMD.equals(cmd)
+ || DUMP_TOP_RESUMED_ACTIVITY.equals(cmd)) {
mAtmInternal.dump(
cmd, fd, pw, args, opti, true /* dumpAll */, dumpClient, dumpPackage);
} else if ("binder-proxies".equals(cmd)) {
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 17be210..b85d729 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -63,8 +63,10 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.IoThread;
+import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemServiceManager;
+import com.android.server.os.NativeTombstoneManager;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -78,6 +80,7 @@
import java.util.Collections;
import java.util.Date;
import java.util.List;
+import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
@@ -762,6 +765,10 @@
* Helper function for shell command
*/
void clearHistoryProcessExitInfo(String packageName, int userId) {
+ NativeTombstoneManager tombstoneService = LocalServices.getService(
+ NativeTombstoneManager.class);
+ Optional<Integer> appId = Optional.empty();
+
if (TextUtils.isEmpty(packageName)) {
synchronized (mLock) {
removeByUserIdLocked(userId);
@@ -769,10 +776,13 @@
} else {
final int uid = mService.mPackageManagerInt.getPackageUid(packageName,
PackageManager.MATCH_ALL, userId);
+ appId = Optional.of(UserHandle.getAppId(uid));
synchronized (mLock) {
removePackageLocked(packageName, uid, true, userId);
}
}
+
+ tombstoneService.purge(Optional.of(userId), appId);
schedulePersistProcessExitInfo(true);
}
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index 09c0937..01e839da 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -41,7 +41,6 @@
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
-import com.android.server.security.FileIntegrityService;
import com.android.server.security.VerityUtils;
import java.io.File;
@@ -226,7 +225,7 @@
@Nullable
private static UpdatableFontDir createUpdatableFontDir() {
// If apk verity is supported, fs-verity should be available.
- if (!FileIntegrityService.isApkVeritySupported()) return null;
+ if (!VerityUtils.isFsVeritySupported()) return null;
return new UpdatableFontDir(new File(FONT_FILES_DIR),
Arrays.asList(new File(SystemFonts.SYSTEM_FONT_DIR),
new File(SystemFonts.OEM_FONT_DIR)),
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index fa1fb48..d014f149 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -774,8 +774,14 @@
private void initializeCec(int initiatedBy) {
mAddressAllocated = false;
- mCecVersion = getHdmiCecConfig().getIntValue(
+ int settingsCecVersion = getHdmiCecConfig().getIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION);
+ int supportedCecVersion = mCecController.getVersion();
+
+ // Limit the used CEC version to the highest supported version by HAL and selected
+ // version in settings (but at least v1.4b).
+ mCecVersion = Math.max(HdmiControlManager.HDMI_CEC_VERSION_1_4_B,
+ Math.min(settingsCecVersion, supportedCecVersion));
mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, true);
mCecController.setLanguage(mMenuLanguage);
@@ -2184,6 +2190,7 @@
pw.println("mProhibitMode: " + mProhibitMode);
pw.println("mPowerStatus: " + mPowerStatusController.getPowerStatus());
+ pw.println("mCecVersion: " + mCecVersion);
// System settings
pw.println("System_settings:");
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java b/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
index ee3427f..a1e6136 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
@@ -70,6 +70,10 @@
pw.println(" --args <vendor specific arguments>");
pw.println(" [--id <true if vendor command should be sent with vendor id>]");
pw.println(" Send a Vendor Command to the given target device");
+ pw.println(" cec_setting get <setting name>");
+ pw.println(" Get the current value of a CEC setting");
+ pw.println(" cec_setting set <setting name> <value>");
+ pw.println(" Set the value of a CEC setting");
}
private int handleShellCommand(String cmd) throws RemoteException {
@@ -81,6 +85,8 @@
return oneTouchPlay(pw);
case "vendorcommand":
return vendorCommand(pw);
+ case "cec_setting":
+ return cecSetting(pw);
}
getErrPrintWriter().println("Unhandled command: " + cmd);
@@ -157,4 +163,39 @@
mBinderService.sendVendorCommand(deviceType, destination, params, hasVendorId);
return 0;
}
+
+ private int cecSetting(PrintWriter pw) throws RemoteException {
+ if (getRemainingArgsCount() < 1) {
+ throw new IllegalArgumentException("Expected at least 1 argument (operation).");
+ }
+ String operation = getNextArgRequired();
+ switch (operation) {
+ case "get": {
+ String setting = getNextArgRequired();
+ try {
+ String value = mBinderService.getCecSettingStringValue(setting);
+ pw.println(setting + " = " + value);
+ } catch (IllegalArgumentException e) {
+ int intValue = mBinderService.getCecSettingIntValue(setting);
+ pw.println(setting + " = " + intValue);
+ }
+ return 0;
+ }
+ case "set": {
+ String setting = getNextArgRequired();
+ String value = getNextArgRequired();
+ try {
+ mBinderService.setCecSettingStringValue(setting, value);
+ pw.println(setting + " = " + value);
+ } catch (IllegalArgumentException e) {
+ int intValue = Integer.parseInt(value);
+ mBinderService.setCecSettingIntValue(setting, intValue);
+ pw.println(setting + " = " + intValue);
+ }
+ return 0;
+ }
+ default:
+ throw new IllegalArgumentException("Unknown operation: " + operation);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index bd577f3..c4c0f68 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -371,9 +371,6 @@
int durationMs) {
Slog.i(mTag, "setTemporaryService(" + userId + ") to " + componentName + " for "
+ durationMs + "ms");
- if (mServiceNameResolver == null) {
- return;
- }
enforceCallingPermissionForManagement();
Objects.requireNonNull(componentName);
@@ -407,9 +404,6 @@
enforceCallingPermissionForManagement();
synchronized (mLock) {
- if (mServiceNameResolver == null) {
- return false;
- }
final boolean changed = mServiceNameResolver.setDefaultServiceEnabled(userId, enabled);
if (!changed) {
if (verbose) {
@@ -440,10 +434,6 @@
public final boolean isDefaultServiceEnabled(@UserIdInt int userId) {
enforceCallingPermissionForManagement();
- if (mServiceNameResolver == null) {
- return false;
- }
-
synchronized (mLock) {
return mServiceNameResolver.isDefaultServiceEnabled(userId);
}
@@ -968,10 +958,6 @@
public void onPackageModified(String packageName) {
if (verbose) Slog.v(mTag, "onPackageModified(): " + packageName);
- if (mServiceNameResolver == null) {
- return;
- }
-
final int userId = getChangingUserId();
final String serviceName = mServiceNameResolver.getDefaultServiceName(userId);
if (serviceName == null) {
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
index a83edb7..9984bfa 100644
--- a/services/core/java/com/android/server/os/NativeTombstoneManager.java
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -16,17 +16,29 @@
package com.android.server.os;
+import static android.app.ApplicationExitInfo.REASON_CRASH_NATIVE;
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import android.annotation.AppIdInt;
+import android.annotation.CurrentTimeMillisLong;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.ApplicationExitInfo;
+import android.app.IParcelFileDescriptorRetriever;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.os.FileObserver;
import android.os.Handler;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructStat;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoInputStream;
@@ -34,6 +46,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.server.BootReceiver;
import com.android.server.ServiceThread;
+import com.android.server.os.TombstoneProtos.Cause;
import com.android.server.os.TombstoneProtos.Tombstone;
import libcore.io.IoUtils;
@@ -42,7 +55,11 @@
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
/**
* A class to manage native tombstones.
@@ -75,6 +92,9 @@
}
void onSystemReady() {
+ registerForUserRemoval();
+ registerForPackageRemoval();
+
// Scan existing tombstones.
mHandler.post(() -> {
final File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
@@ -94,8 +114,9 @@
if (filename.endsWith(".pb")) {
handleProtoTombstone(path);
+ BootReceiver.addTombstoneToDropBox(mContext, path, true);
} else {
- BootReceiver.addTombstoneToDropBox(mContext, path);
+ BootReceiver.addTombstoneToDropBox(mContext, path, false);
}
}
@@ -145,18 +166,164 @@
}
}
+ /**
+ * Remove native tombstones matching a user and/or app.
+ *
+ * @param userId user id to filter by, selects all users if empty
+ * @param appId app id to filter by, selects all users if empty
+ */
+ public void purge(Optional<Integer> userId, Optional<Integer> appId) {
+ mHandler.post(() -> {
+ synchronized (mLock) {
+ for (int i = mTombstones.size() - 1; i >= 0; --i) {
+ TombstoneFile tombstone = mTombstones.valueAt(i);
+ if (tombstone.matches(userId, appId)) {
+ tombstone.purge();
+ mTombstones.removeAt(i);
+ }
+ }
+ }
+ });
+ }
+
+ private void purgePackage(int uid, boolean allUsers) {
+ final int appId = UserHandle.getAppId(uid);
+ Optional<Integer> userId;
+ if (allUsers) {
+ userId = Optional.empty();
+ } else {
+ userId = Optional.of(UserHandle.getUserId(uid));
+ }
+ purge(userId, Optional.of(appId));
+ }
+
+ private void purgeUser(int uid) {
+ purge(Optional.of(uid), Optional.empty());
+ }
+
+ private void registerForPackageRemoval() {
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
+ filter.addDataScheme("package");
+ mContext.registerReceiverForAllUsers(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int uid = intent.getIntExtra(Intent.EXTRA_UID, UserHandle.USER_NULL);
+ if (uid == UserHandle.USER_NULL) return;
+
+ final boolean allUsers = intent.getBooleanExtra(
+ Intent.EXTRA_REMOVED_FOR_ALL_USERS, false);
+
+ purgePackage(uid, allUsers);
+ }
+ }, filter, null, mHandler);
+ }
+
+ private void registerForUserRemoval() {
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiverForAllUsers(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (userId < 1) return;
+
+ purgeUser(userId);
+ }
+ }, filter, null, mHandler);
+ }
+
+ /**
+ * Collect native tombstones.
+ *
+ * @param output list to append to
+ * @param callingUid POSIX uid to filter by
+ * @param pid pid to filter by, ignored if zero
+ * @param maxNum maximum number of elements in output
+ */
+ public void collectTombstones(ArrayList<ApplicationExitInfo> output, int callingUid, int pid,
+ int maxNum) {
+ CompletableFuture<Object> future = new CompletableFuture<>();
+
+ if (!UserHandle.isApp(callingUid)) {
+ return;
+ }
+
+ final int userId = UserHandle.getUserId(callingUid);
+ final int appId = UserHandle.getAppId(callingUid);
+
+ mHandler.post(() -> {
+ boolean appendedTombstones = false;
+
+ synchronized (mLock) {
+ final int tombstonesSize = mTombstones.size();
+
+ tombstoneIter:
+ for (int i = 0; i < tombstonesSize; ++i) {
+ TombstoneFile tombstone = mTombstones.valueAt(i);
+ if (tombstone.matches(Optional.of(userId), Optional.of(appId))) {
+ if (pid != 0 && tombstone.mPid != pid) {
+ continue;
+ }
+
+ // Try to attach to an existing REASON_CRASH_NATIVE.
+ final int outputSize = output.size();
+ for (int j = 0; j < outputSize; ++j) {
+ ApplicationExitInfo exitInfo = output.get(j);
+ if (tombstone.matches(exitInfo)) {
+ exitInfo.setNativeTombstoneRetriever(tombstone.getPfdRetriever());
+ continue tombstoneIter;
+ }
+ }
+
+ if (output.size() < maxNum) {
+ appendedTombstones = true;
+ output.add(tombstone.toAppExitInfo());
+ }
+ }
+ }
+ }
+
+ if (appendedTombstones) {
+ Collections.sort(output, (lhs, rhs) -> {
+ // Reports should be ordered with newest reports first.
+ long diff = rhs.getTimestamp() - lhs.getTimestamp();
+ if (diff < 0) {
+ return -1;
+ } else if (diff == 0) {
+ return 0;
+ } else {
+ return 1;
+ }
+ });
+ }
+ future.complete(null);
+ });
+
+ try {
+ future.get();
+ } catch (ExecutionException | InterruptedException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
static class TombstoneFile {
final ParcelFileDescriptor mPfd;
- final @UserIdInt int mUserId;
- final @AppIdInt int mAppId;
+ @UserIdInt int mUserId;
+ @AppIdInt int mAppId;
+
+ int mPid;
+ int mUid;
+ String mProcessName;
+ @CurrentTimeMillisLong long mTimestampMs;
+ String mCrashReason;
boolean mPurged = false;
+ final IParcelFileDescriptorRetriever mRetriever = new ParcelFileDescriptorRetriever();
- TombstoneFile(ParcelFileDescriptor pfd, @UserIdInt int userId, @AppIdInt int appId) {
+ TombstoneFile(ParcelFileDescriptor pfd) {
mPfd = pfd;
- mUserId = userId;
- mAppId = appId;
}
public boolean matches(Optional<Integer> userId, Optional<Integer> appId) {
@@ -175,24 +342,90 @@
return true;
}
+ public boolean matches(ApplicationExitInfo exitInfo) {
+ if (exitInfo.getReason() != REASON_CRASH_NATIVE) {
+ return false;
+ }
+
+ if (exitInfo.getPid() != mPid) {
+ return false;
+ }
+
+ if (exitInfo.getRealUid() != mUid) {
+ return false;
+ }
+
+ if (Math.abs(exitInfo.getTimestamp() - mTimestampMs) > 1000) {
+ return false;
+ }
+
+ return true;
+ }
+
public void dispose() {
IoUtils.closeQuietly(mPfd);
}
+ public void purge() {
+ if (!mPurged) {
+ // There's no way to atomically unlink a specific file for which we have an fd from
+ // a path, which means that we can't safely delete a tombstone without coordination
+ // with tombstoned (which has a risk of deadlock if for example, system_server hangs
+ // with a flock). Do the next best thing, and just truncate the file.
+ //
+ // We don't have to worry about inflicting a SIGBUS on a process that has the
+ // tombstone mmaped, because we only clear if the package has been removed, which
+ // means no one with access to the tombstone should be left.
+ try {
+ Os.ftruncate(mPfd.getFileDescriptor(), 0);
+ } catch (ErrnoException ex) {
+ Slog.e(TAG, "Failed to truncate tombstone", ex);
+ }
+ mPurged = true;
+ }
+ }
+
static Optional<TombstoneFile> parse(ParcelFileDescriptor pfd) {
final FileInputStream is = new FileInputStream(pfd.getFileDescriptor());
final ProtoInputStream stream = new ProtoInputStream(is);
+ int pid = 0;
int uid = 0;
+ String processName = "";
+ String crashReason = "";
String selinuxLabel = "";
try {
while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
switch (stream.getFieldNumber()) {
+ case (int) Tombstone.PID:
+ pid = stream.readInt(Tombstone.PID);
+ break;
+
case (int) Tombstone.UID:
uid = stream.readInt(Tombstone.UID);
break;
+ case (int) Tombstone.PROCESS_NAME:
+ processName = stream.readString(Tombstone.PROCESS_NAME);
+ break;
+
+ case (int) Tombstone.CAUSE:
+ long token = stream.start(Tombstone.CAUSE);
+ cause:
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (stream.getFieldNumber()) {
+ case (int) Cause.HUMAN_READABLE:
+ crashReason = stream.readString(Cause.HUMAN_READABLE);
+ break cause;
+
+ default:
+ break;
+ }
+ }
+ stream.end(token);
+
+
case (int) Tombstone.SELINUX_LABEL:
selinuxLabel = stream.readString(Tombstone.SELINUX_LABEL);
break;
@@ -211,6 +444,14 @@
return Optional.empty();
}
+ long timestampMs = 0;
+ try {
+ StructStat stat = Os.fstat(pfd.getFileDescriptor());
+ timestampMs = stat.st_atim.tv_sec * 1000 + stat.st_atim.tv_nsec / 1000000;
+ } catch (ErrnoException ex) {
+ Slog.e(TAG, "Failed to get timestamp of tombstone", ex);
+ }
+
final int userId = UserHandle.getUserId(uid);
final int appId = UserHandle.getAppId(uid);
@@ -219,7 +460,74 @@
return Optional.empty();
}
- return Optional.of(new TombstoneFile(pfd, userId, appId));
+ TombstoneFile result = new TombstoneFile(pfd);
+
+ result.mUserId = userId;
+ result.mAppId = appId;
+ result.mPid = pid;
+ result.mUid = uid;
+ result.mProcessName = processName;
+ result.mTimestampMs = timestampMs;
+ result.mCrashReason = crashReason;
+
+ return Optional.of(result);
+ }
+
+ public IParcelFileDescriptorRetriever getPfdRetriever() {
+ return mRetriever;
+ }
+
+ public ApplicationExitInfo toAppExitInfo() {
+ ApplicationExitInfo info = new ApplicationExitInfo();
+ info.setPid(mPid);
+ info.setRealUid(mUid);
+ info.setPackageUid(mUid);
+ info.setDefiningUid(mUid);
+ info.setProcessName(mProcessName);
+ info.setReason(ApplicationExitInfo.REASON_CRASH_NATIVE);
+
+ // Signal numbers are architecture-specific!
+ // We choose to provide nothing here, to avoid leading users astray.
+ info.setStatus(0);
+
+ // No way for us to find out.
+ info.setImportance(RunningAppProcessInfo.IMPORTANCE_GONE);
+ info.setPackageName("");
+ info.setProcessStateSummary(null);
+
+ // We could find out, but they didn't get OOM-killed...
+ info.setPss(0);
+ info.setRss(0);
+
+ info.setTimestamp(mTimestampMs);
+ info.setDescription(mCrashReason);
+
+ info.setSubReason(ApplicationExitInfo.SUBREASON_UNKNOWN);
+ info.setNativeTombstoneRetriever(mRetriever);
+
+ return info;
+ }
+
+
+ class ParcelFileDescriptorRetriever extends IParcelFileDescriptorRetriever.Stub {
+ ParcelFileDescriptorRetriever() {}
+
+ public @Nullable ParcelFileDescriptor getPfd() {
+ if (mPurged) {
+ return null;
+ }
+
+ // Reopen the file descriptor as read-only.
+ try {
+ final String path = "/proc/self/fd/" + mPfd.getFd();
+ ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(path),
+ MODE_READ_ONLY);
+ return pfd;
+ } catch (FileNotFoundException ex) {
+ Slog.e(TAG, "failed to reopen file descriptor as read-only", ex);
+ return null;
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6a441f1..4ccd57d 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -56,13 +56,11 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
@@ -2292,25 +2290,6 @@
return attrs.type == TYPE_NOTIFICATION_SHADE;
}
- @Override
- public boolean canBeHiddenByKeyguardLw(WindowState win) {
-
- // Keyguard visibility of window from activities are determined over activity visibility.
- if (win.getAppToken() != null) {
- return false;
- }
- switch (win.getAttrs().type) {
- case TYPE_NOTIFICATION_SHADE:
- case TYPE_STATUS_BAR:
- case TYPE_NAVIGATION_BAR:
- case TYPE_WALLPAPER:
- return false;
- default:
- // Hide only windows below the keyguard host window.
- return getWindowLayerLw(win) < getWindowLayerFromTypeLw(TYPE_NOTIFICATION_SHADE);
- }
- }
-
/** {@inheritDoc} */
@Override
public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index db33e75..b5a9aca 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -672,7 +672,22 @@
/**
* @return whether {@param win} can be hidden by Keyguard
*/
- public boolean canBeHiddenByKeyguardLw(WindowState win);
+ default boolean canBeHiddenByKeyguardLw(WindowState win) {
+ // Keyguard visibility of window from activities are determined over activity visibility.
+ if (win.getAppToken() != null) {
+ return false;
+ }
+ switch (win.getAttrs().type) {
+ case TYPE_NOTIFICATION_SHADE:
+ case TYPE_STATUS_BAR:
+ case TYPE_NAVIGATION_BAR:
+ case TYPE_WALLPAPER:
+ return false;
+ default:
+ // Hide only windows below the keyguard host window.
+ return getWindowLayerLw(win) < getWindowLayerFromTypeLw(TYPE_NOTIFICATION_SHADE);
+ }
+ }
/**
* Called when the system would like to show a UI to indicate that an
diff --git a/services/core/java/com/android/server/security/FileIntegrityService.java b/services/core/java/com/android/server/security/FileIntegrityService.java
index 6ec71b7..74bb993 100644
--- a/services/core/java/com/android/server/security/FileIntegrityService.java
+++ b/services/core/java/com/android/server/security/FileIntegrityService.java
@@ -23,10 +23,8 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
-import android.os.Build;
import android.os.Environment;
import android.os.IBinder;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.security.IFileIntegrityService;
import android.util.Slog;
@@ -60,7 +58,7 @@
private final IBinder mService = new IFileIntegrityService.Stub() {
@Override
public boolean isApkVeritySupported() {
- return FileIntegrityService.isApkVeritySupported();
+ return VerityUtils.isFsVeritySupported();
}
@Override
@@ -69,7 +67,7 @@
checkCallerPermission(packageName);
try {
- if (!isApkVeritySupported()) {
+ if (!VerityUtils.isFsVeritySupported()) {
return false;
}
if (certificateBytes == null) {
@@ -110,11 +108,6 @@
}
};
- public static boolean isApkVeritySupported() {
- return Build.VERSION.FIRST_SDK_INT >= Build.VERSION_CODES.R
- || SystemProperties.getInt("ro.apk_verity.mode", 0) == 2;
- }
-
public FileIntegrityService(final Context context) {
super(context);
try {
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index 09ee001..48a60387 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -17,7 +17,9 @@
package com.android.server.security;
import android.annotation.NonNull;
+import android.os.Build;
import android.os.SharedMemory;
+import android.os.SystemProperties;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -57,6 +59,11 @@
private static final boolean DEBUG = false;
+ public static boolean isFsVeritySupported() {
+ return Build.VERSION.FIRST_SDK_INT >= Build.VERSION_CODES.R
+ || SystemProperties.getInt("ro.apk_verity.mode", 0) == 2;
+ }
+
/** Returns true if the given file looks like containing an fs-verity signature. */
public static boolean isFsveritySignatureFile(File file) {
return file.getName().endsWith(FSVERITY_SIGNATURE_FILE_EXTENSION);
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index 02a597e..6ad30b5 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -19,10 +19,12 @@
import static com.android.server.VcnManagementService.VDBG;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnGatewayConnectionConfig;
+import android.net.vcn.VcnManager.VcnErrorCode;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelUuid;
@@ -30,7 +32,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
-import com.android.server.VcnManagementService.VcnSafeModeCallback;
+import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import java.util.Collections;
@@ -97,7 +99,7 @@
@NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final Dependencies mDeps;
@NonNull private final VcnNetworkRequestListener mRequestListener;
- @NonNull private final VcnSafeModeCallback mVcnSafeModeCallback;
+ @NonNull private final VcnCallback mVcnCallback;
@NonNull
private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections =
@@ -125,14 +127,8 @@
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config,
@NonNull TelephonySubscriptionSnapshot snapshot,
- @NonNull VcnSafeModeCallback vcnSafeModeCallback) {
- this(
- vcnContext,
- subscriptionGroup,
- config,
- snapshot,
- vcnSafeModeCallback,
- new Dependencies());
+ @NonNull VcnCallback vcnCallback) {
+ this(vcnContext, subscriptionGroup, config, snapshot, vcnCallback, new Dependencies());
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -141,13 +137,12 @@
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config,
@NonNull TelephonySubscriptionSnapshot snapshot,
- @NonNull VcnSafeModeCallback vcnSafeModeCallback,
+ @NonNull VcnCallback vcnCallback,
@NonNull Dependencies deps) {
super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
mVcnContext = vcnContext;
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
- mVcnSafeModeCallback =
- Objects.requireNonNull(vcnSafeModeCallback, "Missing vcnSafeModeCallback");
+ mVcnCallback = Objects.requireNonNull(vcnCallback, "Missing vcnCallback");
mDeps = Objects.requireNonNull(deps, "Missing deps");
mRequestListener = new VcnNetworkRequestListener();
@@ -246,7 +241,7 @@
private void handleEnterSafeMode() {
handleTeardown();
- mVcnSafeModeCallback.onEnteredSafeMode();
+ mVcnCallback.onEnteredSafeMode();
}
private void handleNetworkRequested(
@@ -337,6 +332,13 @@
public interface VcnGatewayStatusCallback {
/** Called by a VcnGatewayConnection to indicate that it has entered safe mode. */
void onEnteredSafeMode();
+
+ /** Callback by a VcnGatewayConnection to indicate that an error occurred. */
+ void onGatewayConnectionError(
+ @NonNull int[] networkCapabilities,
+ @VcnErrorCode int errorCode,
+ @Nullable String exceptionClass,
+ @Nullable String exceptionMessage);
}
private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback {
@@ -344,6 +346,16 @@
public void onEnteredSafeMode() {
sendMessage(obtainMessage(MSG_CMD_ENTER_SAFE_MODE));
}
+
+ @Override
+ public void onGatewayConnectionError(
+ @NonNull int[] networkCapabilities,
+ @VcnErrorCode int errorCode,
+ @Nullable String exceptionClass,
+ @Nullable String exceptionMessage) {
+ mVcnCallback.onGatewayConnectionError(
+ networkCapabilities, errorCode, exceptionClass, exceptionMessage);
+ }
}
/** External dependencies used by Vcn, for injection in tests */
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 59cb6ac..9ee072e 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -22,6 +22,9 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.vcn.VcnManager.VCN_ERROR_CODE_CONFIG_ERROR;
+import static android.net.vcn.VcnManager.VCN_ERROR_CODE_INTERNAL_ERROR;
+import static android.net.vcn.VcnManager.VCN_ERROR_CODE_NETWORK_ERROR;
import static com.android.server.VcnManagementService.VDBG;
@@ -52,7 +55,9 @@
import android.net.ipsec.ike.IkeSessionCallback;
import android.net.ipsec.ike.IkeSessionConfiguration;
import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.exceptions.AuthenticationFailedException;
import android.net.ipsec.ike.exceptions.IkeException;
+import android.net.ipsec.ike.exceptions.IkeInternalException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnTransportInfo;
@@ -951,15 +956,68 @@
removeEqualMessages(EVENT_SAFE_MODE_TIMEOUT_EXCEEDED);
}
- private void sessionLost(int token, @Nullable Exception exception) {
+ private void sessionLostWithoutCallback(int token, @Nullable Exception exception) {
sendMessageAndAcquireWakeLock(
EVENT_SESSION_LOST, token, new EventSessionLostInfo(exception));
}
+ private void sessionLost(int token, @Nullable Exception exception) {
+ // Only notify mGatewayStatusCallback if the session was lost with an error. All
+ // authentication and DNS failures are sent through
+ // IkeSessionCallback.onClosedExceptionally(), which calls sessionClosed()
+ if (exception != null) {
+ mGatewayStatusCallback.onGatewayConnectionError(
+ mConnectionConfig.getRequiredUnderlyingCapabilities(),
+ VCN_ERROR_CODE_INTERNAL_ERROR,
+ "java.lang.RuntimeException",
+ "Received "
+ + exception.getClass().getSimpleName()
+ + " with message: "
+ + exception.getMessage());
+ }
+
+ sessionLostWithoutCallback(token, exception);
+ }
+
+ private void notifyStatusCallbackForSessionClosed(@NonNull Exception exception) {
+ final int errorCode;
+ final String exceptionClass;
+ final String exceptionMessage;
+
+ if (exception instanceof AuthenticationFailedException) {
+ errorCode = VCN_ERROR_CODE_CONFIG_ERROR;
+ exceptionClass = exception.getClass().getName();
+ exceptionMessage = exception.getMessage();
+ } else if (exception instanceof IkeInternalException
+ && exception.getCause() instanceof IOException) {
+ errorCode = VCN_ERROR_CODE_NETWORK_ERROR;
+ exceptionClass = "java.io.IOException";
+ exceptionMessage = exception.getCause().getMessage();
+ } else {
+ errorCode = VCN_ERROR_CODE_INTERNAL_ERROR;
+ exceptionClass = "java.lang.RuntimeException";
+ exceptionMessage =
+ "Received "
+ + exception.getClass().getSimpleName()
+ + " with message: "
+ + exception.getMessage();
+ }
+
+ mGatewayStatusCallback.onGatewayConnectionError(
+ mConnectionConfig.getRequiredUnderlyingCapabilities(),
+ errorCode,
+ exceptionClass,
+ exceptionMessage);
+ }
+
private void sessionClosed(int token, @Nullable Exception exception) {
+ if (exception != null) {
+ notifyStatusCallbackForSessionClosed(exception);
+ }
+
// SESSION_LOST MUST be sent before SESSION_CLOSED to ensure that the SM moves to the
// Disconnecting state.
- sessionLost(token, exception);
+ sessionLostWithoutCallback(token, exception);
sendMessageAndAcquireWakeLock(EVENT_SESSION_CLOSED, token);
}
@@ -1084,6 +1142,8 @@
}
protected void handleDisconnectRequested(String msg) {
+ // TODO(b/180526152): notify VcnStatusCallback for Network loss
+
Slog.v(TAG, "Tearing down. Cause: " + msg);
mIsRunning = false;
@@ -1228,6 +1288,8 @@
String reason = ((EventDisconnectRequestedInfo) msg.obj).reason;
if (reason.equals(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)) {
+ // TODO(b/180526152): notify VcnStatusCallback for Network loss
+
// Will trigger EVENT_SESSION_CLOSED immediately.
mIkeSession.kill();
break;
@@ -1573,8 +1635,7 @@
@Override
protected void exitState() {
- // Attempt to set the safe mode alarm - this requires the Vcn Network being validated
- // while in ConnectedState (which cancels the previous alarm)
+ // Will only set a new alarm if no safe mode alarm is currently scheduled.
setSafeModeAlarm();
}
}
@@ -1710,8 +1771,6 @@
}
}
- // TODO: Make a VcnNetworkSpecifier, and match all underlying subscription IDs.
-
return builder.build();
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 2e98c2c..589a8c3 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -318,6 +318,7 @@
public static final String DUMP_CONTAINERS_CMD = "containers";
public static final String DUMP_RECENTS_CMD = "recents";
public static final String DUMP_RECENTS_SHORT_CMD = "r";
+ public static final String DUMP_TOP_RESUMED_ACTIVITY = "top-resumed";
/** This activity is not being relaunched, or being relaunched for a non-resize reason. */
public static final int RELAUNCH_REASON_NONE = 0;
@@ -3756,6 +3757,14 @@
}
}
+ void dumpTopResumedActivityLocked(PrintWriter pw) {
+ pw.println("ACTIVITY MANAGER TOP-RESUMED (dumpsys activity top-resumed)");
+ ActivityRecord topRecord = mRootWindowContainer.getTopResumedActivity();
+ if (topRecord != null) {
+ topRecord.dump(pw, "", true);
+ }
+ }
+
void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage,
@@ -5896,6 +5905,8 @@
if (getRecentTasks() != null) {
getRecentTasks().dump(pw, dumpAll, dumpPackage);
}
+ } else if (DUMP_TOP_RESUMED_ACTIVITY.equals(cmd)) {
+ dumpTopResumedActivityLocked(pw);
}
}
}
diff --git a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
new file mode 100644
index 0000000..5d6d513
--- /dev/null
+++ b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
@@ -0,0 +1,143 @@
+/*
+ * 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.wm;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS;
+import static com.android.server.wm.AnimationAdapterProto.REMOTE;
+import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
+
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.util.proto.ProtoOutputStream;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.server.policy.WindowManagerPolicy;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+class NonAppWindowAnimationAdapter implements AnimationAdapter {
+
+ private final WindowState mWindow;
+ private RemoteAnimationTarget mTarget;
+ private SurfaceControl mCapturedLeash;
+
+ private long mDurationHint;
+ private long mStatusBarTransitionDelay;
+
+ @Override
+ public boolean getShowWallpaper() {
+ return false;
+ }
+
+ NonAppWindowAnimationAdapter(WindowState w,
+ long durationHint, long statusBarTransitionDelay) {
+ mWindow = w;
+ mDurationHint = durationHint;
+ mStatusBarTransitionDelay = statusBarTransitionDelay;
+ }
+
+ /**
+ * Creates and starts remote animations for all the visible non app windows.
+ *
+ * @return RemoteAnimationTarget[] targets for all the visible non app windows
+ */
+ public static RemoteAnimationTarget[] startNonAppWindowAnimationsForKeyguardExit(
+ WindowManagerService service, long durationHint, long statusBarTransitionDelay) {
+ final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
+
+ final WindowManagerPolicy policy = service.mPolicy;
+ service.mRoot.forAllWindows(nonAppWindow -> {
+ if (nonAppWindow.mActivityRecord == null && policy.canBeHiddenByKeyguardLw(nonAppWindow)
+ && nonAppWindow.wouldBeVisibleIfPolicyIgnored() && !nonAppWindow.isVisible()) {
+ final NonAppWindowAnimationAdapter nonAppAdapter = new NonAppWindowAnimationAdapter(
+ nonAppWindow, durationHint, statusBarTransitionDelay);
+ nonAppWindow.startAnimation(nonAppWindow.getPendingTransaction(),
+ nonAppAdapter, false /* hidden */, ANIMATION_TYPE_WINDOW_ANIMATION);
+ targets.add(nonAppAdapter.createRemoteAnimationTarget());
+ }
+ }, true /* traverseTopToBottom */);
+ return targets.toArray(new RemoteAnimationTarget[targets.size()]);
+ }
+
+ /**
+ * Create a remote animation target for this animation adapter.
+ */
+ RemoteAnimationTarget createRemoteAnimationTarget() {
+ mTarget = new RemoteAnimationTarget(-1, -1, getLeash(), false,
+ new Rect(), null, mWindow.getPrefixOrderIndex(), mWindow.getLastSurfacePosition(),
+ mWindow.getBounds(), null, mWindow.getWindowConfiguration(), true, null, null,
+ null);
+ return mTarget;
+ }
+
+ @Override
+ public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t,
+ int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback) {
+ mCapturedLeash = animationLeash;
+ }
+
+ @Override
+ public long getDurationHint() {
+ return mDurationHint;
+ }
+
+ @Override
+ public long getStatusBarTransitionsStartTime() {
+ return SystemClock.uptimeMillis() + mStatusBarTransitionDelay;
+ }
+
+ /**
+ * @return the leash for this animation (only valid after the non app window surface animation
+ * has started).
+ */
+ SurfaceControl getLeash() {
+ return mCapturedLeash;
+ }
+
+ @Override
+ public void onAnimationCancelled(SurfaceControl animationLeash) {
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "onAnimationCancelled");
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.print("token=");
+ pw.println(mWindow.mToken);
+ if (mTarget != null) {
+ pw.print(prefix);
+ pw.println("Target:");
+ mTarget.dump(pw, prefix + " ");
+ } else {
+ pw.print(prefix);
+ pw.println("Target: null");
+ }
+ }
+
+ @Override
+ public void dumpDebug(ProtoOutputStream proto) {
+ final long token = proto.start(REMOTE);
+ if (mTarget != null) {
+ mTarget.dumpDebug(proto, TARGET);
+ }
+ proto.end(token);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 392f27e..42cb96f 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -16,6 +16,9 @@
package com.android.server.wm;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS;
import static com.android.server.wm.AnimationAdapterProto.REMOTE;
import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
@@ -126,7 +129,7 @@
final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
// TODO(bc-unlock): Create the remote non app animation targets (if any)
- final RemoteAnimationTarget[] nonAppTargets = new RemoteAnimationTarget[0];
+ final RemoteAnimationTarget[] nonAppTargets = createNonAppWindowAnimations(transit);
mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
try {
@@ -215,6 +218,17 @@
}, mPendingWallpaperAnimations);
}
+ private RemoteAnimationTarget[] createNonAppWindowAnimations(
+ @WindowManager.TransitionOldType int transit) {
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createNonAppWindowAnimations()");
+ return (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY
+ || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER)
+ ? NonAppWindowAnimationAdapter.startNonAppWindowAnimationsForKeyguardExit(mService,
+ mRemoteAnimationAdapter.getDuration(),
+ mRemoteAnimationAdapter.getStatusBarTransitionDelay())
+ : new RemoteAnimationTarget[0];
+ }
+
private void onAnimationFinished() {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "onAnimationFinished(): mPendingAnimations=%d",
mPendingAnimations.size());
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index dd2dd81..2b09d12 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -326,8 +326,6 @@
"com.android.server.musicrecognition.MusicRecognitionManagerService";
private static final String SYSTEM_CAPTIONS_MANAGER_SERVICE_CLASS =
"com.android.server.systemcaptions.SystemCaptionsManagerService";
- private static final String TEXT_TO_SPEECH_MANAGER_SERVICE_CLASS =
- "com.android.server.texttospeech.TextToSpeechManagerService";
private static final String TIME_ZONE_RULES_MANAGER_SERVICE_CLASS =
"com.android.server.timezone.RulesManagerService$Lifecycle";
private static final String IOT_SERVICE_CLASS =
@@ -1715,7 +1713,6 @@
startAttentionService(context, t);
startRotationResolverService(context, t);
startSystemCaptionsManagerService(context, t);
- startTextToSpeechManagerService(context, t);
// System Speech Recognition Service
if (deviceHasConfigString(context,
@@ -2921,13 +2918,6 @@
t.traceEnd();
}
- private void startTextToSpeechManagerService(@NonNull Context context,
- @NonNull TimingsTraceAndSlog t) {
- t.traceBegin("StartTextToSpeechManagerService");
- mSystemServiceManager.startService(TEXT_TO_SPEECH_MANAGER_SERVICE_CLASS);
- t.traceEnd();
- }
-
private void startContentCaptureService(@NonNull Context context,
@NonNull TimingsTraceAndSlog t) {
// First check if it was explicitly enabled by DeviceConfig
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 1254df9..9109881 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -18,6 +18,7 @@
import static android.app.AlarmManager.ELAPSED_REALTIME;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE;
+import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_COMPAT;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
import static android.app.AlarmManager.FLAG_IDLE_UNTIL;
import static android.app.AlarmManager.FLAG_STANDALONE;
@@ -25,11 +26,14 @@
import static android.app.AlarmManager.RTC;
import static android.app.AlarmManager.RTC_WAKEUP;
import static android.app.AlarmManager.WINDOW_EXACT;
+import static android.app.AlarmManager.WINDOW_HEURISTIC;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
@@ -44,7 +48,7 @@
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.ALLOW_WHILE_IDLE_WINDOW;
+import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA;
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;
@@ -61,6 +65,7 @@
import static com.android.server.alarm.Constants.TEST_CALLING_UID;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -75,20 +80,29 @@
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
+import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.app.AlarmManager;
+import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.IAlarmCompleteListener;
import android.app.IAlarmListener;
+import android.app.IAlarmManager;
import android.app.PendingIntent;
+import android.app.compat.CompatChanges;
import android.app.usage.UsageStatsManagerInternal;
import android.content.Context;
import android.content.Intent;
+import android.content.PermissionChecker;
import android.os.BatteryManager;
+import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -102,9 +116,12 @@
import com.android.dx.mockito.inline.extended.MockedVoidMethod;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IAppOpsCallback;
+import com.android.internal.app.IAppOpsService;
import com.android.server.AlarmManagerInternal;
import com.android.server.AppStateTracker;
import com.android.server.AppStateTrackerImpl;
+import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.usage.AppStandbyInternal;
@@ -125,6 +142,7 @@
import java.util.HashSet;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.LongConsumer;
@Presubmit
@RunWith(AndroidJUnit4.class)
@@ -134,14 +152,21 @@
private static final int TEST_CALLING_USER = UserHandle.getUserId(TEST_CALLING_UID);
private long mAppStandbyWindow;
+ private long mAllowWhileIdleWindow;
private AlarmManagerService mService;
private AppStandbyInternal.AppIdleStateChangeListener mAppStandbyListener;
private AlarmManagerService.ChargingReceiver mChargingReceiver;
+ private IAppOpsCallback mIAppOpsCallback;
+ private IAlarmManager mBinder;
@Mock
private Context mMockContext;
@Mock
private IActivityManager mIActivityManager;
@Mock
+ private IAppOpsService mIAppOpsService;
+ @Mock
+ private DeviceIdleInternal mDeviceIdleInternal;
+ @Mock
private UsageStatsManagerInternal mUsageStatsManagerInternal;
@Mock
private AppStandbyInternal mAppStandbyInternal;
@@ -280,6 +305,11 @@
// "IllegalStateException: Querying activity state off main thread is not allowed."
// when AlarmManager calls DeviceConfig.addOnPropertiesChangedListener().
}
+
+ @Override
+ IAppOpsService getAppOpsService() {
+ return mIAppOpsService;
+ }
}
@Before
@@ -287,15 +317,20 @@
mMockingSession = mockitoSession()
.initMocks(this)
.spyStatic(ActivityManager.class)
+ .mockStatic(CompatChanges.class)
.spyStatic(DeviceConfig.class)
.mockStatic(LocalServices.class)
.spyStatic(Looper.class)
+ .mockStatic(PermissionChecker.class)
.mockStatic(Settings.Global.class)
.mockStatic(ServiceManager.class)
.spyStatic(UserHandle.class)
.strictness(Strictness.WARN)
.startMocking();
+
doReturn(mIActivityManager).when(ActivityManager::getService);
+ doReturn(mDeviceIdleInternal).when(
+ () -> LocalServices.getService(DeviceIdleInternal.class));
doReturn(mActivityManagerInternal).when(
() -> LocalServices.getService(ActivityManagerInternal.class));
doReturn(mAppStateTracker).when(() -> LocalServices.getService(AppStateTracker.class));
@@ -330,6 +365,9 @@
() -> DeviceConfig.getProperties(
eq(DeviceConfig.NAMESPACE_ALARM_MANAGER), ArgumentMatchers.<String>any()));
+ when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(
+ mock(AppOpsManager.class));
+
mInjector = new Injector(mMockContext);
mService = new AlarmManagerService(mMockContext, mInjector);
spyOn(mService);
@@ -346,6 +384,7 @@
// Other boot phases don't matter
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
mAppStandbyWindow = mService.mConstants.APP_STANDBY_WINDOW;
+ mAllowWhileIdleWindow = mService.mConstants.ALLOW_WHILE_IDLE_WINDOW;
ArgumentCaptor<AppStandbyInternal.AppIdleStateChangeListener> captor =
ArgumentCaptor.forClass(AppStandbyInternal.AppIdleStateChangeListener.class);
verify(mAppStandbyInternal).addListener(captor.capture());
@@ -358,6 +397,20 @@
&& filter.hasAction(BatteryManager.ACTION_DISCHARGING)));
mChargingReceiver = chargingReceiverCaptor.getValue();
+ ArgumentCaptor<IBinder> binderCaptor = ArgumentCaptor.forClass(IBinder.class);
+ verify(() -> ServiceManager.addService(eq(Context.ALARM_SERVICE), binderCaptor.capture(),
+ anyBoolean(), anyInt()));
+ mBinder = IAlarmManager.Stub.asInterface(binderCaptor.getValue());
+
+ ArgumentCaptor<IAppOpsCallback> appOpsCallbackCaptor = ArgumentCaptor.forClass(
+ IAppOpsCallback.class);
+ try {
+ verify(mIAppOpsService).startWatchingMode(eq(AppOpsManager.OP_SCHEDULE_EXACT_ALARM),
+ isNull(), appOpsCallbackCaptor.capture());
+ } catch (RemoteException e) {
+ // Not expected on a mock.
+ }
+ mIAppOpsCallback = appOpsCallbackCaptor.getValue();
setTestableQuotas();
}
@@ -389,13 +442,18 @@
private void setTestAlarm(int type, long triggerTime, PendingIntent operation, long interval,
int flags, int callingUid) {
+ setTestAlarm(type, triggerTime, operation, interval, flags, callingUid, null);
+ }
+
+ private void setTestAlarm(int type, long triggerTime, PendingIntent operation, long interval,
+ int flags, int callingUid, Bundle idleOptions) {
mService.setImpl(type, triggerTime, WINDOW_EXACT, interval, operation, null, "test", flags,
- null, null, callingUid, TEST_CALLING_PACKAGE);
+ null, null, callingUid, TEST_CALLING_PACKAGE, idleOptions);
}
private void setTestAlarmWithListener(int type, long triggerTime, IAlarmListener listener) {
mService.setImpl(type, triggerTime, WINDOW_EXACT, 0, null, listener, "test",
- FLAG_STANDALONE, null, null, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
+ FLAG_STANDALONE, null, null, TEST_CALLING_UID, TEST_CALLING_PACKAGE, null);
}
@@ -506,14 +564,27 @@
setDeviceConfigLong(KEY_MIN_INTERVAL, 10);
setDeviceConfigLong(KEY_MAX_INTERVAL, 15);
setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_QUOTA, 20);
+ setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA, 25);
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_QUOTA);
+ assertEquals(25, mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA);
assertEquals(30, mService.mConstants.ALLOW_WHILE_IDLE_WHITELIST_DURATION);
assertEquals(35, mService.mConstants.LISTENER_TIMEOUT);
+
+ // Test safeguards.
+ setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_QUOTA, -3);
+ assertEquals(1, mService.mConstants.ALLOW_WHILE_IDLE_QUOTA);
+ setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_QUOTA, 0);
+ assertEquals(1, mService.mConstants.ALLOW_WHILE_IDLE_QUOTA);
+
+ setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA, -8);
+ assertEquals(1, mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA);
+ setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA, 0);
+ assertEquals(1, mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA);
}
@Test
@@ -559,56 +630,45 @@
assertEquals(mNowElapsedTest + 9, mTestTimer.getElapsed());
}
- private void testQuotasDeferralOnSet(int standbyBucket) throws Exception {
- final int quota = mService.getQuotaForBucketLocked(standbyBucket);
- when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
- anyLong())).thenReturn(standbyBucket);
+ private void testQuotasDeferralOnSet(LongConsumer alarmSetter, int quota, long window)
+ throws Exception {
final long firstTrigger = mNowElapsedTest + 10;
for (int i = 0; i < quota; i++) {
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
- getNewMockPendingIntent());
+ alarmSetter.accept(firstTrigger + i);
mNowElapsedTest = mTestTimer.getElapsed();
mTestTimer.expire();
}
// This one should get deferred on set
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
- getNewMockPendingIntent());
- final long expectedNextTrigger = firstTrigger + mAppStandbyWindow;
+ alarmSetter.accept(firstTrigger + quota);
+ final long expectedNextTrigger = firstTrigger + window;
assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
}
- private void testQuotasDeferralOnExpiration(int standbyBucket) throws Exception {
- final int quota = mService.getQuotaForBucketLocked(standbyBucket);
- when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
- anyLong())).thenReturn(standbyBucket);
+ private void testQuotasDeferralOnExpiration(LongConsumer alarmSetter, int quota, long window)
+ throws Exception {
final long firstTrigger = mNowElapsedTest + 10;
for (int i = 0; i < quota; i++) {
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
- getNewMockPendingIntent());
+ alarmSetter.accept(firstTrigger + i);
}
- // This one should get deferred after the latest alarm expires
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
- getNewMockPendingIntent());
+ // This one should get deferred after the latest alarm expires.
+ alarmSetter.accept(firstTrigger + quota);
for (int i = 0; i < quota; i++) {
mNowElapsedTest = mTestTimer.getElapsed();
mTestTimer.expire();
}
- final long expectedNextTrigger = firstTrigger + mAppStandbyWindow;
+ final long expectedNextTrigger = firstTrigger + window;
assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
}
- private void testQuotasNoDeferral(int standbyBucket) throws Exception {
- final int quota = mService.getQuotaForBucketLocked(standbyBucket);
- when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
- anyLong())).thenReturn(standbyBucket);
+ private void testQuotasNoDeferral(LongConsumer alarmSetter, int quota, long window)
+ throws Exception {
final long firstTrigger = mNowElapsedTest + 10;
for (int i = 0; i < quota; i++) {
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 10 + i,
- getNewMockPendingIntent());
+ alarmSetter.accept(firstTrigger + i);
}
// This delivery time maintains the quota invariant. Should not be deferred.
- final long expectedNextTrigger = firstTrigger + mAppStandbyWindow + 5;
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, expectedNextTrigger, getNewMockPendingIntent());
+ final long expectedNextTrigger = firstTrigger + window + 5;
+ alarmSetter.accept(expectedNextTrigger);
for (int i = 0; i < quota; i++) {
mNowElapsedTest = mTestTimer.getElapsed();
mTestTimer.expire();
@@ -616,64 +676,88 @@
assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
}
+ private void testStandbyQuotasDeferralOnSet(int standbyBucket) throws Exception {
+ final int quota = mService.getQuotaForBucketLocked(standbyBucket);
+ when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
+ anyLong())).thenReturn(standbyBucket);
+ testQuotasDeferralOnSet(trigger -> setTestAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
+ getNewMockPendingIntent()), quota, mAppStandbyWindow);
+ }
+
+ private void testStandbyQuotasDeferralOnExpiration(int standbyBucket) throws Exception {
+ final int quota = mService.getQuotaForBucketLocked(standbyBucket);
+ when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
+ anyLong())).thenReturn(standbyBucket);
+ testQuotasDeferralOnExpiration(trigger -> setTestAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
+ getNewMockPendingIntent()), quota, mAppStandbyWindow);
+ }
+
+ private void testStandbyQuotasNoDeferral(int standbyBucket) throws Exception {
+ final int quota = mService.getQuotaForBucketLocked(standbyBucket);
+ when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
+ anyLong())).thenReturn(standbyBucket);
+ testQuotasNoDeferral(trigger -> setTestAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
+ getNewMockPendingIntent()), quota, mAppStandbyWindow);
+ }
+
@Test
public void testActiveQuota_deferredOnSet() throws Exception {
- testQuotasDeferralOnSet(STANDBY_BUCKET_ACTIVE);
+ testStandbyQuotasDeferralOnSet(STANDBY_BUCKET_ACTIVE);
}
@Test
public void testActiveQuota_deferredOnExpiration() throws Exception {
- testQuotasDeferralOnExpiration(STANDBY_BUCKET_ACTIVE);
+ testStandbyQuotasDeferralOnExpiration(STANDBY_BUCKET_ACTIVE);
}
@Test
public void testActiveQuota_notDeferred() throws Exception {
- testQuotasNoDeferral(STANDBY_BUCKET_ACTIVE);
+ testStandbyQuotasNoDeferral(STANDBY_BUCKET_ACTIVE);
}
@Test
public void testWorkingQuota_deferredOnSet() throws Exception {
- testQuotasDeferralOnSet(STANDBY_BUCKET_WORKING_SET);
+ testStandbyQuotasDeferralOnSet(STANDBY_BUCKET_WORKING_SET);
}
@Test
public void testWorkingQuota_deferredOnExpiration() throws Exception {
- testQuotasDeferralOnExpiration(STANDBY_BUCKET_WORKING_SET);
+ testStandbyQuotasDeferralOnExpiration(STANDBY_BUCKET_WORKING_SET);
}
@Test
public void testWorkingQuota_notDeferred() throws Exception {
- testQuotasNoDeferral(STANDBY_BUCKET_WORKING_SET);
+ testStandbyQuotasNoDeferral(STANDBY_BUCKET_WORKING_SET);
}
@Test
public void testFrequentQuota_deferredOnSet() throws Exception {
- testQuotasDeferralOnSet(STANDBY_BUCKET_FREQUENT);
+ testStandbyQuotasDeferralOnSet(STANDBY_BUCKET_FREQUENT);
}
@Test
public void testFrequentQuota_deferredOnExpiration() throws Exception {
- testQuotasDeferralOnExpiration(STANDBY_BUCKET_FREQUENT);
+ testStandbyQuotasDeferralOnExpiration(STANDBY_BUCKET_FREQUENT);
}
@Test
public void testFrequentQuota_notDeferred() throws Exception {
- testQuotasNoDeferral(STANDBY_BUCKET_FREQUENT);
+ testStandbyQuotasNoDeferral(STANDBY_BUCKET_FREQUENT);
}
@Test
public void testRareQuota_deferredOnSet() throws Exception {
- testQuotasDeferralOnSet(STANDBY_BUCKET_RARE);
+ testStandbyQuotasDeferralOnSet(STANDBY_BUCKET_RARE);
}
@Test
public void testRareQuota_deferredOnExpiration() throws Exception {
- testQuotasDeferralOnExpiration(STANDBY_BUCKET_RARE);
+ testStandbyQuotasDeferralOnExpiration(STANDBY_BUCKET_RARE);
}
@Test
public void testRareQuota_notDeferred() throws Exception {
- testQuotasNoDeferral(STANDBY_BUCKET_RARE);
+ testStandbyQuotasNoDeferral(STANDBY_BUCKET_RARE);
}
@Test
@@ -731,7 +815,7 @@
}
@Test
- public void testQuotaDowngrade() throws Exception {
+ public void testStandbyQuotaDowngrade() throws Exception {
final int workingQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_WORKING_SET);
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
anyLong())).thenReturn(STANDBY_BUCKET_WORKING_SET);
@@ -758,7 +842,7 @@
}
@Test
- public void testQuotaUpgrade() throws Exception {
+ public void testStandbyQuotaUpgrade() throws Exception {
final int frequentQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_FREQUENT);
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
anyLong())).thenReturn(STANDBY_BUCKET_FREQUENT);
@@ -1308,7 +1392,7 @@
public void allowWhileIdleAlarmsWhileDeviceIdle() throws Exception {
doReturn(0).when(mService).fuzzForDuration(anyLong());
- setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + ALLOW_WHILE_IDLE_WINDOW + 1000,
+ setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + mAllowWhileIdleWindow + 1000,
getNewMockPendingIntent());
assertNotNull(mService.mPendingIdleUntil);
@@ -1323,7 +1407,7 @@
// This one should get deferred on set.
setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
getNewMockPendingIntent(), false);
- final long expectedNextTrigger = firstTrigger + ALLOW_WHILE_IDLE_WINDOW;
+ final long expectedNextTrigger = firstTrigger + mAllowWhileIdleWindow;
assertEquals("Incorrect trigger when no quota left", expectedNextTrigger,
mTestTimer.getElapsed());
@@ -1449,61 +1533,24 @@
when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
TEST_CALLING_PACKAGE)).thenReturn(true);
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);
- long expectedNextTrigger = firstTrigger + ALLOW_WHILE_IDLE_WINDOW;
- assertEquals("Incorrect trigger when no quota available", expectedNextTrigger,
- mTestTimer.getElapsed());
+
+ testQuotasDeferralOnSet(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
+ getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
// 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());
+ testQuotasDeferralOnExpiration(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP,
+ trigger, getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
// 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 delivery time maintains the quota invariant. Should not be deferred.
- expectedNextTrigger = firstTrigger + ALLOW_WHILE_IDLE_WINDOW + 5;
- setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, expectedNextTrigger,
- getNewMockPendingIntent(), false);
- for (int i = 0; i < quota; i++) {
- mNowElapsedTest = mTestTimer.getElapsed();
- mTestTimer.expire();
- }
- assertEquals("Incorrect trigger when no quota available", expectedNextTrigger,
- mTestTimer.getElapsed());
+ testQuotasNoDeferral(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
+ getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
}
@Test
@@ -1545,6 +1592,259 @@
}
@Test
+ public void canScheduleExactAlarms() throws RemoteException {
+ doReturn(PermissionChecker.PERMISSION_GRANTED).when(
+ () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
+ assertTrue(mBinder.canScheduleExactAlarms());
+
+ doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
+ () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
+ assertFalse(mBinder.canScheduleExactAlarms());
+
+ doReturn(PermissionChecker.PERMISSION_SOFT_DENIED).when(
+ () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
+ assertFalse(mBinder.canScheduleExactAlarms());
+ }
+
+ @Test
+ public void noPermissionCheckWhenChangeDisabled() throws RemoteException {
+ doReturn(false).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ // alarm clock
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, 0,
+ getNewMockPendingIntent(), null, null, null,
+ mock(AlarmManager.AlarmClockInfo.class));
+
+ // exact, allow-while-idle
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
+ FLAG_ALLOW_WHILE_IDLE, getNewMockPendingIntent(), null, null, null, null);
+
+ // inexact, allow-while-idle
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_HEURISTIC, 0,
+ FLAG_ALLOW_WHILE_IDLE, getNewMockPendingIntent(), null, null, null, null);
+
+ verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM), never());
+ verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
+ }
+
+ @Test
+ public void exactAllowWhileIdleBinderCallWhenChangeDisabled() throws Exception {
+ doReturn(false).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
+ FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
+
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
+ eq(alarmPi), isNull(), isNull(),
+ eq(FLAG_ALLOW_WHILE_IDLE_COMPAT | FLAG_STANDALONE), isNull(), isNull(),
+ eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
+
+ final Bundle idleOptions = bundleCaptor.getValue();
+ final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
+ }
+
+ @Test
+ public void inexactAllowWhileIdleBinderCallWhenChangeDisabled() throws Exception {
+ doReturn(false).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_HEURISTIC, 0,
+ FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
+
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), anyLong(), eq(0L),
+ eq(alarmPi), isNull(), isNull(), eq(FLAG_ALLOW_WHILE_IDLE_COMPAT), isNull(),
+ isNull(), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
+
+ final Bundle idleOptions = bundleCaptor.getValue();
+ final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
+ }
+
+ @Test
+ public void alarmClockBinderCallWhenChangeDisabled() throws Exception {
+ doReturn(false).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class);
+ mBinder.set(TEST_CALLING_PACKAGE, RTC_WAKEUP, 1234, WINDOW_EXACT, 0, 0,
+ alarmPi, null, null, null, alarmClock);
+
+ verify(mService).setImpl(eq(RTC_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
+ eq(alarmPi), isNull(), isNull(), eq(FLAG_STANDALONE | FLAG_WAKE_FROM_IDLE),
+ isNull(), eq(alarmClock), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), isNull());
+ }
+
+ @Test
+ public void alarmClockBinderCall() throws RemoteException {
+ doReturn(true).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ doReturn(PermissionChecker.PERMISSION_GRANTED).when(
+ () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
+
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class);
+ mBinder.set(TEST_CALLING_PACKAGE, RTC_WAKEUP, 1234, WINDOW_EXACT, 0, 0,
+ alarmPi, null, null, null, alarmClock);
+
+ // Correct permission checks are invoked.
+ verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
+ verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
+
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mService).setImpl(eq(RTC_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
+ eq(alarmPi), isNull(), isNull(), eq(FLAG_STANDALONE | FLAG_WAKE_FROM_IDLE),
+ isNull(), eq(alarmClock), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE),
+ bundleCaptor.capture());
+
+ final Bundle idleOptions = bundleCaptor.getValue();
+ final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
+ }
+
+ @Test
+ public void exactAllowWhileIdleBinderCallWithPermission() throws RemoteException {
+ doReturn(true).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ // Permission check is granted by default by the mock.
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
+ FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
+
+ verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
+ verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
+
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
+ eq(alarmPi), isNull(), isNull(),
+ eq(FLAG_ALLOW_WHILE_IDLE | FLAG_STANDALONE), isNull(), isNull(),
+ eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
+
+ final Bundle idleOptions = bundleCaptor.getValue();
+ final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
+ }
+
+ @Test
+ public void exactAllowWhileIdleBinderCallWithAllowlist() throws RemoteException {
+ doReturn(true).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+ // If permission is denied, only then allowlist will be checked.
+ doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(
+ () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
+ when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
+
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
+ FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
+
+ verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM));
+ verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(Process.myUid()));
+
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
+ eq(alarmPi), isNull(), isNull(),
+ eq(FLAG_ALLOW_WHILE_IDLE_COMPAT | FLAG_STANDALONE), isNull(), isNull(),
+ eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
+
+ final Bundle idleOptions = bundleCaptor.getValue();
+ final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
+ }
+
+ @Test
+ public void inexactAllowWhileIdleBinderCallWithAllowlist() throws RemoteException {
+ doReturn(true).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 4321, WINDOW_HEURISTIC, 0,
+ FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
+
+ verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM), never());
+ verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(Process.myUid()));
+
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(4321L), anyLong(), eq(0L),
+ eq(alarmPi), isNull(), isNull(), eq(FLAG_ALLOW_WHILE_IDLE_COMPAT), isNull(),
+ isNull(), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
+
+ final Bundle idleOptions = bundleCaptor.getValue();
+ final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
+ }
+
+ @Test
+ public void inexactAllowWhileIdleBinderCallWithoutAllowlist() throws RemoteException {
+ doReturn(true).when(
+ () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION),
+ anyString(), any(UserHandle.class)));
+
+ when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(false);
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 4321, WINDOW_HEURISTIC, 0,
+ FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
+
+ verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext,
+ Manifest.permission.SCHEDULE_EXACT_ALARM), never());
+ verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(Process.myUid()));
+
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(4321L), anyLong(), eq(0L),
+ eq(alarmPi), isNull(), isNull(), eq(FLAG_ALLOW_WHILE_IDLE_COMPAT), isNull(),
+ isNull(), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture());
+
+ final Bundle idleOptions = bundleCaptor.getValue();
+ final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType");
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED, type);
+ }
+
+ @Test
+ public void idleOptionsSentOnExpiration() throws Exception {
+ final long triggerTime = mNowElapsedTest + 5000;
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ final Bundle idleOptions = new Bundle();
+ idleOptions.putChar("TEST_CHAR_KEY", 'x');
+ idleOptions.putInt("TEST_INT_KEY", 53);
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi, 0, 0, TEST_CALLING_UID,
+ idleOptions);
+
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+
+ verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class),
+ any(), any(Handler.class), isNull(), eq(idleOptions));
+ }
+
+ @Test
public void alarmStoreMigration() {
setDeviceConfigBoolean(KEY_LAZY_BATCHING, false);
final int numAlarms = 10;
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
index 42fa3d4..12894ae 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
@@ -69,12 +69,12 @@
mock(PendingIntent.class));
return new Alarm(ELAPSED_REALTIME_WAKEUP, whenElapsed, whenElapsed, 0, 0,
mock(PendingIntent.class), null, null, null, 0, info, TEST_CALLING_UID,
- TEST_CALLING_PACKAGE);
+ TEST_CALLING_PACKAGE, null);
}
private static Alarm createAlarm(int type, long whenElapsed, long windowLength, int flags) {
return new Alarm(type, whenElapsed, whenElapsed, windowLength, 0, mock(PendingIntent.class),
- null, null, null, flags, null, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
+ null, null, null, flags, null, TEST_CALLING_UID, TEST_CALLING_PACKAGE, null);
}
private void addAlarmsToStore(Alarm... alarms) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java
index e80f065..b64528c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java
@@ -17,10 +17,17 @@
package com.android.server.alarm;
import static android.app.AlarmManager.ELAPSED_REALTIME;
+import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE;
+import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_COMPAT;
+import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
+import static android.app.AlarmManager.FLAG_STANDALONE;
+import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE;
+import static android.app.AlarmManager.RTC_WAKEUP;
import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX;
import static com.android.server.alarm.Alarm.NUM_POLICIES;
import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;
+import static com.android.server.alarm.AlarmManagerService.isExemptFromAppStandby;
import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE;
import static com.android.server.alarm.Constants.TEST_CALLING_UID;
@@ -28,7 +35,9 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import android.app.AlarmManager;
import android.app.PendingIntent;
import android.platform.test.annotations.Presubmit;
@@ -43,15 +52,29 @@
@RunWith(AndroidJUnit4.class)
public class AlarmTest {
- private Alarm createDefaultAlarm(long requestedElapsed, long windowLength) {
+ private Alarm createDefaultAlarm(long requestedElapsed, long windowLength, int flags) {
return new Alarm(ELAPSED_REALTIME, 0, requestedElapsed, windowLength, 0,
- mock(PendingIntent.class), null, null, null, 0, null, TEST_CALLING_UID,
- TEST_CALLING_PACKAGE);
+ createAlarmSender(), null, null, null, flags, null, TEST_CALLING_UID,
+ TEST_CALLING_PACKAGE, null);
+ }
+
+ private Alarm createAlarmClock(long requestedRtc) {
+ final AlarmManager.AlarmClockInfo info = mock(AlarmManager.AlarmClockInfo.class);
+ return new Alarm(RTC_WAKEUP, requestedRtc, requestedRtc, 0, 0, createAlarmSender(),
+ null, null, null, FLAG_WAKE_FROM_IDLE | FLAG_STANDALONE, info, TEST_CALLING_UID,
+ TEST_CALLING_PACKAGE, null);
+ }
+
+ private PendingIntent createAlarmSender() {
+ final PendingIntent alarmPi = mock(PendingIntent.class);
+ when(alarmPi.getCreatorPackage()).thenReturn(TEST_CALLING_PACKAGE);
+ when(alarmPi.getCreatorUid()).thenReturn(TEST_CALLING_UID);
+ return alarmPi;
}
@Test
public void initSetsOnlyRequesterPolicy() {
- final Alarm a = createDefaultAlarm(4567, 2);
+ final Alarm a = createDefaultAlarm(4567, 2, 0);
for (int i = 0; i < NUM_POLICIES; i++) {
if (i == REQUESTER_POLICY_INDEX) {
@@ -86,7 +109,7 @@
@Test
public void whenElapsed() {
- final Alarm a = createDefaultAlarm(0, 0);
+ final Alarm a = createDefaultAlarm(0, 0, 0);
final long[][] uniqueData = generatePolicyTestMatrix(NUM_POLICIES);
for (int i = 0; i < NUM_POLICIES; i++) {
@@ -104,7 +127,7 @@
@Test
public void maxWhenElapsed() {
- final Alarm a = createDefaultAlarm(10, 12);
+ final Alarm a = createDefaultAlarm(10, 12, 0);
assertEquals(22, a.getMaxWhenElapsed());
a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 15);
@@ -128,7 +151,7 @@
@Test
public void setPolicyElapsedExact() {
- final Alarm exactAlarm = createDefaultAlarm(10, 0);
+ final Alarm exactAlarm = createDefaultAlarm(10, 0, 0);
assertTrue(exactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4));
assertTrue(exactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10));
@@ -143,7 +166,7 @@
@Test
public void setPolicyElapsedInexact() {
- final Alarm inexactAlarm = createDefaultAlarm(10, 5);
+ final Alarm inexactAlarm = createDefaultAlarm(10, 5, 0);
assertTrue(inexactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4));
assertTrue(inexactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10));
@@ -154,4 +177,20 @@
assertFalse(inexactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 8));
}
+
+ @Test
+ public void isExemptFromStandby() {
+ final long anything = 35412; // Arbitrary number, doesn't matter for this test.
+
+ assertFalse("Basic alarm exempt", isExemptFromAppStandby(
+ createDefaultAlarm(anything, anything, 0)));
+ assertFalse("FLAG_ALLOW_WHILE_IDLE_COMPAT exempt", isExemptFromAppStandby(
+ createDefaultAlarm(anything, anything, FLAG_ALLOW_WHILE_IDLE_COMPAT)));
+
+ assertTrue("ALLOW_WHILE_IDLE not exempt", isExemptFromAppStandby(
+ createDefaultAlarm(anything, anything, FLAG_ALLOW_WHILE_IDLE)));
+ assertTrue("ALLOW_WHILE_IDLE_UNRESTRICTED not exempt", isExemptFromAppStandby(
+ createDefaultAlarm(anything, anything, FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED)));
+ assertTrue("Alarm clock not exempt", isExemptFromAppStandby(createAlarmClock(anything)));
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java
index 5bb6a42..0e795a9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java
@@ -45,7 +45,7 @@
}
uidAlarms.add(new Alarm(
removeIt ? RTC : RTC_WAKEUP,
- 0, 0, 0, 0, null, null, null, null, 0, null, uid, name));
+ 0, 0, 0, 0, null, null, null, null, 0, null, uid, name, null));
return all;
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index fcbd897..1958cb0 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -58,6 +58,7 @@
private int mMyPhysicalAddress = 0;
private HdmiPortInfo[] mHdmiPortInfo = null;
private HdmiCecController.HdmiCecCallback mCallback = null;
+ private int mCecVersion = HdmiControlManager.HDMI_CEC_VERSION_2_0;
@Override
public String nativeInit() {
@@ -96,7 +97,7 @@
@Override
public int nativeGetVersion() {
- return HdmiControlManager.HDMI_CEC_VERSION_2_0;
+ return mCecVersion;
}
@Override
@@ -132,6 +133,10 @@
mPortConnectionStatus.put(port, connected);
}
+ public void setCecVersion(@HdmiControlManager.HdmiCecVersion int cecVersion) {
+ mCecVersion = cecVersion;
+ }
+
public void onCecMessage(HdmiCecMessage hdmiCecMessage) {
if (mCallback == null) {
return;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index be584d7..462f3e3 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -49,6 +49,7 @@
import androidx.test.filters.SmallTest;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -622,6 +623,45 @@
assertEquals(runnerUid, Binder.getCallingWorkSourceUid());
}
+ @Ignore("b/180499471")
+ @Test
+ public void initCecVersion_limitToMinimumSupportedVersion() {
+ mHdmiControlService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ mNativeWrapper.setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+
+ mHdmiControlService.initService();
+ assertThat(mHdmiControlService.getCecVersion()).isEqualTo(
+ HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+ }
+
+ @Ignore("b/180499471")
+ @Test
+ public void initCecVersion_limitToAtLeast1_4() {
+ mHdmiControlService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ mNativeWrapper.setCecVersion(0x0);
+
+ mHdmiControlService.initService();
+ assertThat(mHdmiControlService.getCecVersion()).isEqualTo(
+ HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+ }
+
+ @Ignore("b/180499471")
+ @Test
+ public void initCecVersion_useHighestMatchingVersion() {
+ mHdmiControlService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ mNativeWrapper.setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_2_0);
+
+ mHdmiControlService.initService();
+ assertThat(mHdmiControlService.getCecVersion()).isEqualTo(
+ HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ }
+
private static class VolumeControlFeatureCallback extends
IHdmiCecVolumeControlFeatureListener.Stub {
boolean mCallbackReceived = false;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 15e045c..2fdd63e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -16,8 +16,11 @@
package com.android.server.wm;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
@@ -94,10 +97,19 @@
mController = new RemoteAnimationController(mWm, mAdapter, mHandler);
}
+ private WindowState createAppOverlayWindow() {
+ final WindowState win = createWindow(null /* parent */, TYPE_APPLICATION_OVERLAY,
+ "testOverlayWindow");
+ win.mActivityRecord = null;
+ win.mHasSurface = true;
+ return win;
+ }
+
@Test
public void testRun() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
mDisplayContent.mOpeningApps.add(win.mActivityRecord);
+ final WindowState overlayWin = createAppOverlayWindow();
try {
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
@@ -109,12 +121,12 @@
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
- final ArgumentCaptor<RemoteAnimationTarget[]> nonApsCaptor =
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
- appsCaptor.capture(), wallpapersCaptor.capture(), nonApsCaptor.capture(),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -130,6 +142,7 @@
finishedCaptor.getValue().onAnimationFinished();
verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION),
eq(adapter));
+ assertEquals(0, nonAppsCaptor.getValue().length);
} finally {
mDisplayContent.mOpeningApps.clear();
}
@@ -424,6 +437,89 @@
}
}
+ @Test
+ public void testNonAppIncluded_keygaurdGoingAway() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mDisplayContent.mOpeningApps.add(win.mActivityRecord);
+ // Add overlay window hidden by the keyguard.
+ final WindowState overlayWin = createAppOverlayWindow();
+ overlayWin.hide(false /* doAnimation */, false /* requestAnim */);
+ try {
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+ win.mActivityRecord, new Point(50, 100), null,
+ new Rect(50, 100, 150, 150), null).mAdapter;
+ adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
+ mFinishedCallback);
+ mController.goodToGo(TRANSIT_OLD_KEYGUARD_GOING_AWAY);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_KEYGUARD_GOING_AWAY),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
+ finishedCaptor.capture());
+ assertEquals(1, appsCaptor.getValue().length);
+ final RemoteAnimationTarget app = appsCaptor.getValue()[0];
+ assertEquals(new Point(50, 100), app.position);
+ assertEquals(new Rect(50, 100, 150, 150), app.sourceContainerBounds);
+ assertEquals(win.mActivityRecord.getPrefixOrderIndex(), app.prefixOrderIndex);
+ assertEquals(win.mActivityRecord.getTask().mTaskId, app.taskId);
+ assertEquals(mMockLeash, app.leash);
+ assertEquals(false, app.isTranslucent);
+ verify(mMockTransaction).setPosition(mMockLeash, app.position.x, app.position.y);
+ verify(mMockTransaction).setWindowCrop(mMockLeash, 100, 50);
+
+ finishedCaptor.getValue().onAnimationFinished();
+ verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION),
+ eq(adapter));
+ assertEquals(1, nonAppsCaptor.getValue().length);
+ } finally {
+ mDisplayContent.mOpeningApps.clear();
+ }
+ }
+
+ @Test
+ public void testNonAppIncluded_keygaurdGoingAwayToWallpaper() throws Exception {
+ final WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class),
+ true, mDisplayContent, true /* ownerCanManageAppTokens */);
+ spyOn(mDisplayContent.mWallpaperController);
+ doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mDisplayContent.mOpeningApps.add(win.mActivityRecord);
+ // Add overlay window hidden by the keyguard.
+ final WindowState overlayWin = createAppOverlayWindow();
+ overlayWin.hide(false /* doAnimation */, false /* requestAnim */);
+ try {
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+ win.mActivityRecord, new Point(50, 100), null,
+ new Rect(50, 100, 150, 150), null).mAdapter;
+ adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
+ mFinishedCallback);
+ mController.goodToGo(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
+ finishedCaptor.capture());
+ assertEquals(1, wallpapersCaptor.getValue().length);
+ assertEquals(1, nonAppsCaptor.getValue().length);
+ } finally {
+ mDisplayContent.mOpeningApps.clear();
+ }
+ }
+
private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) {
verify(binder, atLeast(0)).asBinder();
verifyNoMoreInteractions(binder);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index e4b865f..86d8eee 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -91,11 +91,6 @@
return attrs.type == TYPE_NOTIFICATION_SHADE;
}
- @Override
- public boolean canBeHiddenByKeyguardLw(WindowState win) {
- return false;
- }
-
/**
* Sets a runnable to run when adding a splash screen which gets executed after the window has
* been added but before returning the surface.
diff --git a/services/texttospeech/Android.bp b/services/texttospeech/Android.bp
deleted file mode 100644
index bacc932..0000000
--- a/services/texttospeech/Android.bp
+++ /dev/null
@@ -1,13 +0,0 @@
-filegroup {
- name: "services.texttospeech-sources",
- srcs: ["java/**/*.java"],
- path: "java",
- visibility: ["//frameworks/base/services"],
-}
-
-java_library_static {
- name: "services.texttospeech",
- defaults: ["platform_service_defaults"],
- srcs: [":services.texttospeech-sources"],
- libs: ["services.core"],
-}
\ No newline at end of file
diff --git a/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java b/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java
deleted file mode 100644
index f805904..0000000
--- a/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java
+++ /dev/null
@@ -1,184 +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.server.texttospeech;
-
-import static com.android.internal.infra.AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS;
-
-import android.annotation.NonNull;
-import android.annotation.UserIdInt;
-import android.app.AppGlobals;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
-import android.os.IBinder.DeathRecipient;
-import android.os.RemoteException;
-import android.speech.tts.ITextToSpeechService;
-import android.speech.tts.ITextToSpeechSession;
-import android.speech.tts.ITextToSpeechSessionCallback;
-import android.speech.tts.TextToSpeech;
-import android.util.Slog;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.infra.ServiceConnector;
-import com.android.server.infra.AbstractPerUserSystemService;
-
-import java.util.NoSuchElementException;
-
-/**
- * Manages per-user text to speech session activated by {@link TextToSpeechManagerService}.
- * Creates {@link TtsClient} interface object with direct connection to
- * {@link android.speech.tts.TextToSpeechService} provider.
- *
- * @see ITextToSpeechSession
- * @see TextToSpeech
- */
-final class TextToSpeechManagerPerUserService extends
- AbstractPerUserSystemService<TextToSpeechManagerPerUserService,
- TextToSpeechManagerService> {
-
- private static final String TAG = TextToSpeechManagerPerUserService.class.getSimpleName();
-
- TextToSpeechManagerPerUserService(
- @NonNull TextToSpeechManagerService master,
- @NonNull Object lock, @UserIdInt int userId) {
- super(master, lock, userId);
- }
-
- void createSessionLocked(String engine, ITextToSpeechSessionCallback sessionCallback) {
- TextToSpeechSessionConnection.start(getContext(), mUserId, engine, sessionCallback);
- }
-
- @GuardedBy("mLock")
- @Override // from PerUserSystemService
- @NonNull
- protected ServiceInfo newServiceInfoLocked(
- @SuppressWarnings("unused") @NonNull ComponentName serviceComponent)
- throws PackageManager.NameNotFoundException {
- try {
- return AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
- PackageManager.GET_META_DATA, mUserId);
- } catch (RemoteException e) {
- throw new PackageManager.NameNotFoundException(
- "Could not get service for " + serviceComponent);
- }
- }
-
- private static class TextToSpeechSessionConnection extends
- ServiceConnector.Impl<ITextToSpeechService> {
-
- private final String mEngine;
- private final ITextToSpeechSessionCallback mCallback;
- private final DeathRecipient mUnbindOnDeathHandler;
-
- static void start(Context context, @UserIdInt int userId, String engine,
- ITextToSpeechSessionCallback callback) {
- new TextToSpeechSessionConnection(context, userId, engine, callback).start();
- }
-
- private TextToSpeechSessionConnection(Context context, @UserIdInt int userId, String engine,
- ITextToSpeechSessionCallback callback) {
- super(context,
- new Intent(TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE).setPackage(engine),
- Context.BIND_AUTO_CREATE,
- userId,
- ITextToSpeechService.Stub::asInterface);
- mEngine = engine;
- mCallback = callback;
- mUnbindOnDeathHandler = () -> unbindEngine("client process death is reported");
- }
-
- private void start() {
- Slog.d(TAG, "Trying to start connection to TTS engine: " + mEngine);
-
- connect()
- .thenAccept(
- serviceBinder -> {
- if (serviceBinder != null) {
- Slog.d(TAG,
- "Connected successfully to TTS engine: " + mEngine);
- try {
- mCallback.onConnected(new ITextToSpeechSession.Stub() {
- @Override
- public void disconnect() {
- unbindEngine("client disconnection request");
- }
- }, serviceBinder.asBinder());
-
- mCallback.asBinder().linkToDeath(mUnbindOnDeathHandler, 0);
- } catch (RemoteException ex) {
- Slog.w(TAG, "Error notifying the client on connection", ex);
-
- unbindEngine(
- "failed communicating with the client - process "
- + "is dead");
- }
- } else {
- Slog.w(TAG, "Failed to obtain TTS engine binder");
- runSessionCallbackMethod(
- () -> mCallback.onError("Failed creating TTS session"));
- }
- })
- .exceptionally(ex -> {
- Slog.w(TAG, "TTS engine binding error", ex);
- runSessionCallbackMethod(
- () -> mCallback.onError(
- "Failed creating TTS session: " + ex.getCause()));
-
- return null;
- });
- }
-
- @Override // from ServiceConnector.Impl
- protected void onServiceConnectionStatusChanged(
- ITextToSpeechService service, boolean connected) {
- if (!connected) {
- Slog.w(TAG, "Disconnected from TTS engine");
- runSessionCallbackMethod(mCallback::onDisconnected);
-
- try {
- mCallback.asBinder().unlinkToDeath(mUnbindOnDeathHandler, 0);
- } catch (NoSuchElementException ex) {
- Slog.d(TAG, "The death recipient was not linked.");
- }
- }
- }
-
- @Override // from ServiceConnector.Impl
- protected long getAutoDisconnectTimeoutMs() {
- return PERMANENT_BOUND_TIMEOUT_MS;
- }
-
- private void unbindEngine(String reason) {
- Slog.d(TAG, "Unbinding TTS engine: " + mEngine + ". Reason: " + reason);
- unbind();
- }
- }
-
- static void runSessionCallbackMethod(ThrowingRunnable callbackRunnable) {
- try {
- callbackRunnable.runOrThrow();
- } catch (RemoteException ex) {
- Slog.w(TAG, "Failed running callback method", ex);
- }
- }
-
- interface ThrowingRunnable {
- void runOrThrow() throws RemoteException;
- }
-}
diff --git a/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerService.java b/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerService.java
deleted file mode 100644
index 9015563..0000000
--- a/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerService.java
+++ /dev/null
@@ -1,77 +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.server.texttospeech;
-
-import static com.android.server.texttospeech.TextToSpeechManagerPerUserService.runSessionCallbackMethod;
-
-import android.annotation.NonNull;
-import android.annotation.UserIdInt;
-import android.content.Context;
-import android.os.UserHandle;
-import android.speech.tts.ITextToSpeechManager;
-import android.speech.tts.ITextToSpeechSessionCallback;
-
-import com.android.server.infra.AbstractMasterSystemService;
-
-
-/**
- * A service that allows secured synthesizing of text to speech audio. Upon request creates a
- * session
- * that is managed by {@link TextToSpeechManagerPerUserService}.
- *
- * @see ITextToSpeechManager
- */
-public final class TextToSpeechManagerService extends
- AbstractMasterSystemService<TextToSpeechManagerService,
- TextToSpeechManagerPerUserService> {
-
- private static final String TAG = TextToSpeechManagerService.class.getSimpleName();
-
- public TextToSpeechManagerService(@NonNull Context context) {
- super(context, /* serviceNameResolver= */ null,
- /* disallowProperty = */null);
- }
-
- @Override // from SystemService
- public void onStart() {
- publishBinderService(Context.TEXT_TO_SPEECH_MANAGER_SERVICE,
- new TextToSpeechManagerServiceStub());
- }
-
- @Override
- protected TextToSpeechManagerPerUserService newServiceLocked(
- @UserIdInt int resolvedUserId, boolean disabled) {
- return new TextToSpeechManagerPerUserService(this, mLock, resolvedUserId);
- }
-
- private final class TextToSpeechManagerServiceStub extends ITextToSpeechManager.Stub {
- @Override
- public void createSession(String engine,
- ITextToSpeechSessionCallback sessionCallback) {
- synchronized (mLock) {
- TextToSpeechManagerPerUserService perUserService = getServiceForUserLocked(
- UserHandle.getCallingUserId());
- if (perUserService != null) {
- perUserService.createSessionLocked(engine, sessionCallback);
- } else {
- runSessionCallbackMethod(
- () -> sessionCallback.onError("Service is not available for user"));
- }
- }
- }
- }
-}
diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
index 1a90fc3..7087676 100644
--- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -33,6 +34,7 @@
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
import android.net.vcn.VcnManager.VcnStatusCallback;
+import android.net.vcn.VcnManager.VcnStatusCallbackBinder;
import android.net.vcn.VcnManager.VcnUnderlyingNetworkPolicyListener;
import android.os.ParcelUuid;
@@ -40,11 +42,15 @@
import org.junit.Test;
import org.mockito.ArgumentCaptor;
+import java.net.UnknownHostException;
import java.util.UUID;
import java.util.concurrent.Executor;
public class VcnManagerTest {
private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
+ private static final int[] UNDERLYING_NETWORK_CAPABILITIES = {
+ NetworkCapabilities.NET_CAPABILITY_IMS, NetworkCapabilities.NET_CAPABILITY_INTERNET
+ };
private static final Executor INLINE_EXECUTOR = Runnable::run;
private IVcnManagementService mMockVcnManagementService;
@@ -144,14 +150,8 @@
public void testRegisterVcnStatusCallback() throws Exception {
mVcnManager.registerVcnStatusCallback(SUB_GROUP, INLINE_EXECUTOR, mMockStatusCallback);
- ArgumentCaptor<IVcnStatusCallback> captor =
- ArgumentCaptor.forClass(IVcnStatusCallback.class);
verify(mMockVcnManagementService)
- .registerVcnStatusCallback(eq(SUB_GROUP), captor.capture(), any());
-
- IVcnStatusCallback callbackWrapper = captor.getValue();
- callbackWrapper.onEnteredSafeMode();
- verify(mMockStatusCallback).onEnteredSafeMode();
+ .registerVcnStatusCallback(eq(SUB_GROUP), notNull(), any());
}
@Test(expected = IllegalStateException.class)
@@ -195,4 +195,24 @@
public void testUnregisterNullVcnStatusCallback() throws Exception {
mVcnManager.unregisterVcnStatusCallback(null);
}
+
+ @Test
+ public void testVcnStatusCallbackBinder() throws Exception {
+ IVcnStatusCallback cbBinder =
+ new VcnStatusCallbackBinder(INLINE_EXECUTOR, mMockStatusCallback);
+
+ cbBinder.onEnteredSafeMode();
+ verify(mMockStatusCallback).onEnteredSafeMode();
+
+ cbBinder.onGatewayConnectionError(
+ UNDERLYING_NETWORK_CAPABILITIES,
+ VcnManager.VCN_ERROR_CODE_NETWORK_ERROR,
+ "java.net.UnknownHostException",
+ "exception_message");
+ verify(mMockStatusCallback)
+ .onGatewayConnectionError(
+ eq(UNDERLYING_NETWORK_CAPABILITIES),
+ eq(VcnManager.VCN_ERROR_CODE_NETWORK_ERROR),
+ any(UnknownHostException.class));
+ }
}
diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkSpecifierTest.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkSpecifierTest.java
new file mode 100644
index 0000000..2110d6e
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkSpecifierTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.net.vcn;
+
+import static com.android.testutils.ParcelUtils.assertParcelSane;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.net.TelephonyNetworkSpecifier;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnUnderlyingNetworkSpecifierTest {
+ private static final int[] TEST_SUB_IDS = new int[] {1, 2, 3, 5};
+
+ @Test
+ public void testGetSubIds() {
+ final VcnUnderlyingNetworkSpecifier specifier =
+ new VcnUnderlyingNetworkSpecifier(TEST_SUB_IDS);
+
+ assertEquals(TEST_SUB_IDS, specifier.getSubIds());
+ }
+
+ @Test
+ public void testParceling() {
+ final VcnUnderlyingNetworkSpecifier specifier =
+ new VcnUnderlyingNetworkSpecifier(TEST_SUB_IDS);
+ assertParcelSane(specifier, 1);
+ }
+
+ @Test
+ public void testCanBeSatisfiedByTelephonyNetworkSpecifier() {
+ final TelephonyNetworkSpecifier telSpecifier =
+ new TelephonyNetworkSpecifier(TEST_SUB_IDS[0]);
+
+ final VcnUnderlyingNetworkSpecifier specifier =
+ new VcnUnderlyingNetworkSpecifier(TEST_SUB_IDS);
+ assertTrue(specifier.canBeSatisfiedBy(telSpecifier));
+ }
+}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 124ec30..45b2381 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -75,7 +75,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.LocationPermissionChecker;
-import com.android.server.VcnManagementService.VcnSafeModeCallback;
+import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.VcnManagementService.VcnStatusCallbackInfo;
import com.android.server.vcn.TelephonySubscriptionTracker;
import com.android.server.vcn.Vcn;
@@ -156,8 +156,8 @@
private final LocationPermissionChecker mLocationPermissionChecker =
mock(LocationPermissionChecker.class);
- private final ArgumentCaptor<VcnSafeModeCallback> mSafeModeCallbackCaptor =
- ArgumentCaptor.forClass(VcnSafeModeCallback.class);
+ private final ArgumentCaptor<VcnCallback> mVcnCallbackCaptor =
+ ArgumentCaptor.forClass(VcnCallback.class);
private final VcnManagementService mVcnMgmtSvc;
@@ -721,7 +721,7 @@
verify(mMockPolicyListener).onPolicyChanged();
}
- private void verifyVcnSafeModeCallback(
+ private void verifyVcnCallback(
@NonNull ParcelUuid subGroup, @NonNull TelephonySubscriptionSnapshot snapshot)
throws Exception {
verify(mMockDeps)
@@ -730,22 +730,22 @@
eq(subGroup),
eq(TEST_VCN_CONFIG),
eq(snapshot),
- mSafeModeCallbackCaptor.capture());
+ mVcnCallbackCaptor.capture());
mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
- VcnSafeModeCallback safeModeCallback = mSafeModeCallbackCaptor.getValue();
- safeModeCallback.onEnteredSafeMode();
+ VcnCallback vcnCallback = mVcnCallbackCaptor.getValue();
+ vcnCallback.onEnteredSafeMode();
verify(mMockPolicyListener).onPolicyChanged();
}
@Test
- public void testVcnSafeModeCallbackOnEnteredSafeMode() throws Exception {
+ public void testVcnCallbackOnEnteredSafeMode() throws Exception {
TelephonySubscriptionSnapshot snapshot =
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
- verifyVcnSafeModeCallback(TEST_UUID_1, snapshot);
+ verifyVcnCallback(TEST_UUID_1, snapshot);
}
private void triggerVcnStatusCallbackOnEnteredSafeMode(
@@ -771,7 +771,7 @@
// Trigger systemReady() to set up LocationPermissionChecker
mVcnMgmtSvc.systemReady();
- verifyVcnSafeModeCallback(subGroup, snapshot);
+ verifyVcnCallback(subGroup, snapshot);
}
@Test
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index b62a0b8..69c21b9 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -20,6 +20,9 @@
import static android.net.IpSecManager.DIRECTION_OUT;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.vcn.VcnManager.VCN_ERROR_CODE_CONFIG_ERROR;
+import static android.net.vcn.VcnManager.VCN_ERROR_CODE_INTERNAL_ERROR;
+import static android.net.vcn.VcnManager.VCN_ERROR_CODE_NETWORK_ERROR;
import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration;
import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
@@ -39,6 +42,11 @@
import android.net.LinkProperties;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
+import android.net.ipsec.ike.exceptions.AuthenticationFailedException;
+import android.net.ipsec.ike.exceptions.IkeException;
+import android.net.ipsec.ike.exceptions.IkeInternalException;
+import android.net.ipsec.ike.exceptions.TemporaryFailureException;
+import android.net.vcn.VcnManager.VcnErrorCode;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -48,6 +56,8 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import java.io.IOException;
+import java.net.UnknownHostException;
import java.util.Collections;
/** Tests for VcnGatewayConnection.ConnectedState */
@@ -208,6 +218,25 @@
// Since network never validated, verify mSafeModeTimeoutAlarm not canceled
verifyNoMoreInteractions(mSafeModeTimeoutAlarm);
+
+ // The child session was closed without exception, so verify that the GatewayStatusCallback
+ // was not notified
+ verifyNoMoreInteractions(mGatewayStatusCallback);
+ }
+
+ @Test
+ public void testChildSessionClosedExceptionallyNotifiesGatewayStatusCallback()
+ throws Exception {
+ final IkeInternalException exception = new IkeInternalException(mock(IOException.class));
+ getChildSessionCallback().onClosedExceptionally(exception);
+ mTestLooper.dispatchAll();
+
+ verify(mGatewayStatusCallback)
+ .onGatewayConnectionError(
+ eq(mConfig.getRequiredUnderlyingCapabilities()),
+ eq(VCN_ERROR_CODE_INTERNAL_ERROR),
+ any(),
+ any());
}
@Test
@@ -223,5 +252,42 @@
// Since network never validated, verify mSafeModeTimeoutAlarm not canceled
verifyNoMoreInteractions(mSafeModeTimeoutAlarm);
+
+ // IkeSession closed with no error, so verify that the GatewayStatusCallback was not
+ // notified
+ verifyNoMoreInteractions(mGatewayStatusCallback);
+ }
+
+ private void verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback(
+ IkeException cause, @VcnErrorCode int expectedErrorType) {
+ getIkeSessionCallback().onClosedExceptionally(cause);
+ mTestLooper.dispatchAll();
+
+ verify(mIkeSession).close();
+
+ verify(mGatewayStatusCallback)
+ .onGatewayConnectionError(
+ eq(mConfig.getRequiredUnderlyingCapabilities()),
+ eq(expectedErrorType),
+ any(),
+ any());
+ }
+
+ @Test
+ public void testIkeSessionClosedExceptionallyAuthenticationFailure() throws Exception {
+ verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback(
+ new AuthenticationFailedException("vcn test"), VCN_ERROR_CODE_CONFIG_ERROR);
+ }
+
+ @Test
+ public void testIkeSessionClosedExceptionallyDnsFailure() throws Exception {
+ verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback(
+ new IkeInternalException(new UnknownHostException()), VCN_ERROR_CODE_NETWORK_ERROR);
+ }
+
+ @Test
+ public void testIkeSessionClosedExceptionallyInternalFailure() throws Exception {
+ verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback(
+ new TemporaryFailureException("vcn test"), VCN_ERROR_CODE_INTERNAL_ERROR);
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
index 8e142c0..9d33682 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -34,7 +34,7 @@
import android.os.ParcelUuid;
import android.os.test.TestLooper;
-import com.android.server.VcnManagementService.VcnSafeModeCallback;
+import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
@@ -56,7 +56,7 @@
private VcnContext mVcnContext;
private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
private VcnNetworkProvider mVcnNetworkProvider;
- private VcnSafeModeCallback mVcnSafeModeCallback;
+ private VcnCallback mVcnCallback;
private Vcn.Dependencies mDeps;
private ArgumentCaptor<VcnGatewayStatusCallback> mGatewayStatusCallbackCaptor;
@@ -72,7 +72,7 @@
mVcnContext = mock(VcnContext.class);
mSubscriptionSnapshot = mock(TelephonySubscriptionSnapshot.class);
mVcnNetworkProvider = mock(VcnNetworkProvider.class);
- mVcnSafeModeCallback = mock(VcnSafeModeCallback.class);
+ mVcnCallback = mock(VcnCallback.class);
mDeps = mock(Vcn.Dependencies.class);
mTestLooper = new TestLooper();
@@ -104,7 +104,7 @@
TEST_SUB_GROUP,
mConfig,
mSubscriptionSnapshot,
- mVcnSafeModeCallback,
+ mVcnCallback,
mDeps);
}
@@ -179,6 +179,6 @@
verify(gatewayConnection).teardownAsynchronously();
}
verify(mVcnNetworkProvider).unregisterListener(requestListener);
- verify(mVcnSafeModeCallback).onEnteredSafeMode();
+ verify(mVcnCallback).onEnteredSafeMode();
}
}