Merge "Clean up after enabling per-window-input-rotation"
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 82759f7..31f7f6e 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -120,6 +120,7 @@
":framework-connectivity-sources",
":framework-graphics-srcs",
":framework-mediaprovider-sources",
+ ":framework-nearby-sources",
":framework-permission-sources",
":framework-permission-s-sources",
":framework-scheduling-sources",
@@ -165,6 +166,7 @@
":framework-graphics{.public.stubs.source}",
":framework-media{.public.stubs.source}",
":framework-mediaprovider{.public.stubs.source}",
+ ":framework-nearby{.public.stubs.source}",
":framework-permission{.public.stubs.source}",
":framework-permission-s{.public.stubs.source}",
":framework-scheduling{.public.stubs.source}",
@@ -203,6 +205,7 @@
":framework-graphics{.public.annotations.zip}",
":framework-media{.public.annotations.zip}",
":framework-mediaprovider{.public.annotations.zip}",
+ ":framework-nearby{.public.annotations.zip}",
":framework-permission{.public.annotations.zip}",
":framework-permission-s{.public.annotations.zip}",
":framework-scheduling{.public.annotations.zip}",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 1904c1f..cc118f3 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -244,6 +244,7 @@
"framework-graphics.stubs",
"framework-media.stubs",
"framework-mediaprovider.stubs",
+ "framework-nearby.stubs",
"framework-permission.stubs",
"framework-permission-s.stubs",
"framework-scheduling.stubs",
@@ -264,6 +265,7 @@
"framework-graphics.stubs.system",
"framework-media.stubs.system",
"framework-mediaprovider.stubs.system",
+ "framework-nearby.stubs.system",
"framework-permission.stubs.system",
"framework-permission-s.stubs.system",
"framework-scheduling.stubs.system",
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
index 98b5938..1c40a06 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
@@ -25,7 +25,6 @@
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
-import android.window.TaskSnapshot;
import android.app.IActivityTaskManager;
import android.content.ComponentName;
import android.content.Context;
@@ -41,6 +40,7 @@
import android.view.IRecentsAnimationController;
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationTarget;
+import android.window.TaskSnapshot;
import androidx.test.filters.LargeTest;
import androidx.test.runner.lifecycle.Stage;
@@ -210,7 +210,8 @@
}
@Override
- public void onAnimationCanceled(TaskSnapshot taskSnapshot) throws RemoteException {
+ public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots)
+ throws RemoteException {
Assume.assumeNoException(
new AssertionError("onAnimationCanceled should not be called"));
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index dfe2101..630d5ce 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -85,6 +85,17 @@
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public static final long DISALLOW_DEADLINES_FOR_PREFETCH_JOBS = 194532703L;
+ /**
+ * Whether to throw an exception when an app provides an invalid priority value via
+ * {@link Builder#setPriority(int)}. Legacy apps may be incorrectly using the API and
+ * so the call will silently fail for them if they continue using the API.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ public static final long THROW_ON_INVALID_PRIORITY_VALUE = 140852299L;
+
/** @hide */
@IntDef(prefix = { "NETWORK_TYPE_" }, value = {
NETWORK_TYPE_NONE,
@@ -207,6 +218,67 @@
public static final int DEFAULT_BACKOFF_POLICY = BACKOFF_POLICY_EXPONENTIAL;
/**
+ * Job has minimal value to the user. The user has absolutely no expectation
+ * or knowledge of this task and it has no bearing on the user's perception of
+ * the app whatsoever. JobScheduler <i>may</i> decide to defer these tasks while
+ * there are higher priority tasks in order to ensure there is sufficient quota
+ * available for the higher priority tasks.
+ * A sample task of min priority: uploading analytics
+ */
+ public static final int PRIORITY_MIN = 100;
+
+ /**
+ * Low priority. The task provides some benefit to users, but is not critical
+ * and is more of a nice-to-have. This is more important than minimum priority
+ * jobs and will be prioritized ahead of them, but may still be deferred in lieu
+ * of higher priority jobs. JobScheduler <i>may</i> decide to defer these tasks
+ * while there are higher priority tasks in order to ensure there is sufficient
+ * quota available for the higher priority tasks.
+ * A sample task of low priority: prefetching data the user hasn't requested
+ */
+ public static final int PRIORITY_LOW = 200;
+
+ /**
+ * Default value for all regular jobs. As noted in {@link JobScheduler},
+ * these jobs have a general maximum execution time of 10 minutes.
+ * Receives the standard job management policy.
+ */
+ public static final int PRIORITY_DEFAULT = 300;
+
+ /**
+ * This task should be ordered ahead of most other tasks. It may be
+ * deferred a little, but if it doesn't run at some point, the user may think
+ * something is wrong. Assuming all constraints remain satisfied
+ * (including ideal system load conditions), these jobs will have a maximum
+ * execution time of at least 4 minutes. Setting all of your jobs to high
+ * priority will not be beneficial to your app and in fact may hurt its
+ * performance in the long run.
+ */
+ public static final int PRIORITY_HIGH = 400;
+
+ /**
+ * This task should be run ahead of all other tasks. Only Expedited Jobs
+ * {@link Builder#setExpedited(boolean)} can have this priority and as such,
+ * are subject to the same maximum execution time details noted in
+ * {@link Builder#setExpedited(boolean)}.
+ * A sample task of max priority: receiving a text message and processing it to
+ * show a notification
+ */
+ public static final int PRIORITY_MAX = 500;
+
+ /** @hide */
+ @IntDef(prefix = {"PRIORITY_"}, value = {
+ PRIORITY_MIN,
+ PRIORITY_LOW,
+ PRIORITY_DEFAULT,
+ PRIORITY_HIGH,
+ PRIORITY_MAX,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Priority {
+ }
+
+ /**
* Default of {@link #getBias}.
* @hide
*/
@@ -359,6 +431,8 @@
private final long initialBackoffMillis;
private final int backoffPolicy;
private final int mBias;
+ @Priority
+ private final int mPriority;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final int flags;
@@ -410,6 +484,14 @@
return mBias;
}
+ /**
+ * @see JobInfo.Builder#setPriority(int)
+ */
+ @Priority
+ public int getPriority() {
+ return mPriority;
+ }
+
/** @hide */
public int getFlags() {
return flags;
@@ -746,6 +828,9 @@
if (mBias != j.mBias) {
return false;
}
+ if (mPriority != j.mPriority) {
+ return false;
+ }
if (flags != j.flags) {
return false;
}
@@ -791,6 +876,7 @@
hashCode = 31 * hashCode + Long.hashCode(initialBackoffMillis);
hashCode = 31 * hashCode + backoffPolicy;
hashCode = 31 * hashCode + mBias;
+ hashCode = 31 * hashCode + mPriority;
hashCode = 31 * hashCode + flags;
return hashCode;
}
@@ -830,6 +916,7 @@
hasEarlyConstraint = in.readInt() == 1;
hasLateConstraint = in.readInt() == 1;
mBias = in.readInt();
+ mPriority = in.readInt();
flags = in.readInt();
}
@@ -861,6 +948,7 @@
hasEarlyConstraint = b.mHasEarlyConstraint;
hasLateConstraint = b.mHasLateConstraint;
mBias = b.mBias;
+ mPriority = b.mPriority;
flags = b.mFlags;
}
@@ -906,6 +994,7 @@
out.writeInt(hasEarlyConstraint ? 1 : 0);
out.writeInt(hasLateConstraint ? 1 : 0);
out.writeInt(mBias);
+ out.writeInt(mPriority);
out.writeInt(this.flags);
}
@@ -1024,6 +1113,8 @@
private ClipData mClipData;
private int mClipGrantFlags;
private int mBias = BIAS_DEFAULT;
+ @Priority
+ private int mPriority = PRIORITY_DEFAULT;
private int mFlags;
// Requirements.
private int mConstraintFlags;
@@ -1100,6 +1191,7 @@
// mBackoffPolicySet isn't set but it's fine since this is copying from an already valid
// job.
mBackoffPolicy = job.getBackoffPolicy();
+ mPriority = job.getPriority();
}
/** @hide */
@@ -1109,11 +1201,36 @@
return this;
}
- /** @hide */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public Builder setPriority(int priority) {
- // No-op for invalid calls. This wasn't a supported API before Tiramisu, so anyone
- // calling this that isn't targeting T isn't guaranteed a behavior change.
+ /**
+ * Indicate the priority for this job. The priority set here will be used to sort jobs
+ * for the calling app and apply slightly different policies based on the priority.
+ * The priority will <b>NOT</b> be used as a global sorting value to sort between
+ * different app's jobs. Use this to inform the system about which jobs it should try
+ * to run before other jobs. Giving the same priority to all of your jobs will result
+ * in them all being treated the same. The priorities each have slightly different
+ * behaviors, as noted in their relevant javadoc.
+ *
+ * <b>NOTE:</b> Setting all of your jobs to high priority will not be
+ * beneficial to your app and in fact may hurt its performance in the
+ * long run.
+ *
+ * In order to prevent starvation, repeatedly retried jobs (because of failures) will slowly
+ * have their priorities lowered.
+ *
+ * @see JobInfo#getPriority()
+ */
+ @NonNull
+ public Builder setPriority(@Priority int priority) {
+ if (priority > PRIORITY_MAX || priority < PRIORITY_MIN) {
+ if (Compatibility.isChangeEnabled(THROW_ON_INVALID_PRIORITY_VALUE)) {
+ throw new IllegalArgumentException("Invalid priority value");
+ }
+ // No-op for invalid calls of apps that are targeting S-. This was an unsupported
+ // API before Tiramisu, so anyone calling this that isn't targeting T isn't
+ // guaranteed a behavior change.
+ return this;
+ }
+ mPriority = priority;
return this;
}
@@ -1637,7 +1754,17 @@
public Builder setExpedited(boolean expedited) {
if (expedited) {
mFlags |= FLAG_EXPEDITED;
+ if (mPriority == PRIORITY_DEFAULT) {
+ // The default priority for EJs is MAX, but only change this if .setPriority()
+ // hasn't been called yet.
+ mPriority = PRIORITY_MAX;
+ }
} else {
+ if (mPriority == PRIORITY_MAX && (mFlags & FLAG_EXPEDITED) != 0) {
+ // Reset the priority for the job, but only change this if .setPriority()
+ // hasn't been called yet.
+ mPriority = PRIORITY_DEFAULT;
+ }
mFlags &= (~FLAG_EXPEDITED);
}
return this;
@@ -1664,7 +1791,18 @@
public Builder setImportantWhileForeground(boolean importantWhileForeground) {
if (importantWhileForeground) {
mFlags |= FLAG_IMPORTANT_WHILE_FOREGROUND;
+ if (mPriority == PRIORITY_DEFAULT) {
+ // The default priority for important-while-foreground is HIGH, but only change
+ // this if .setPriority() hasn't been called yet.
+ mPriority = PRIORITY_HIGH;
+ }
} else {
+ if (mPriority == PRIORITY_HIGH
+ && (mFlags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0) {
+ // Reset the priority for the job, but only change this if .setPriority()
+ // hasn't been called yet.
+ mPriority = PRIORITY_DEFAULT;
+ }
mFlags &= (~FLAG_IMPORTANT_WHILE_FOREGROUND);
}
return this;
@@ -1812,12 +1950,42 @@
}
}
- if ((flags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0 && hasEarlyConstraint) {
- throw new IllegalArgumentException(
- "An important while foreground job cannot have a time delay");
+ if ((flags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0) {
+ if (hasEarlyConstraint) {
+ throw new IllegalArgumentException(
+ "An important while foreground job cannot have a time delay");
+ }
+ if (mPriority != PRIORITY_HIGH && mPriority != PRIORITY_DEFAULT) {
+ throw new IllegalArgumentException(
+ "An important while foreground job must be high or default priority."
+ + " Don't mark unimportant tasks as important while foreground.");
+ }
}
- if ((flags & FLAG_EXPEDITED) != 0) {
+ final boolean isExpedited = (flags & FLAG_EXPEDITED) != 0;
+ switch (mPriority) {
+ case PRIORITY_MAX:
+ if (!isExpedited) {
+ throw new IllegalArgumentException("Only expedited jobs can have max priority");
+ }
+ break;
+ case PRIORITY_HIGH:
+ if ((flags & FLAG_PREFETCH) != 0) {
+ throw new IllegalArgumentException("Prefetch jobs cannot be high priority");
+ }
+ if (isPeriodic) {
+ throw new IllegalArgumentException("Periodic jobs cannot be high priority");
+ }
+ break;
+ case PRIORITY_DEFAULT:
+ case PRIORITY_LOW:
+ case PRIORITY_MIN:
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid priority level provided: " + mPriority);
+ }
+
+ if (isExpedited) {
if (hasEarlyConstraint) {
throw new IllegalArgumentException("An expedited job cannot have a time delay");
}
@@ -1827,6 +1995,11 @@
if (isPeriodic) {
throw new IllegalArgumentException("An expedited job cannot be periodic");
}
+ if (mPriority != PRIORITY_MAX && mPriority != PRIORITY_HIGH) {
+ throw new IllegalArgumentException(
+ "An expedited job must be high or max priority. Don't use expedited jobs"
+ + " for unimportant tasks.");
+ }
if ((constraintFlags & ~CONSTRAINT_FLAG_STORAGE_NOT_LOW) != 0
|| (flags & ~(FLAG_EXPEDITED | FLAG_EXEMPT_FROM_APP_STANDBY)) != 0) {
throw new IllegalArgumentException(
@@ -1863,4 +2036,24 @@
}
return bias + " [UNKNOWN]";
}
+
+ /**
+ * Convert a priority integer into a human readable string for debugging.
+ * @hide
+ */
+ public static String getPriorityString(@Priority int priority) {
+ switch (priority) {
+ case PRIORITY_MIN:
+ return priority + " [MIN]";
+ case PRIORITY_LOW:
+ return priority + " [LOW]";
+ case PRIORITY_DEFAULT:
+ return priority + " [DEFAULT]";
+ case PRIORITY_HIGH:
+ return priority + " [HIGH]";
+ case PRIORITY_MAX:
+ return priority + " [MAX]";
+ }
+ return priority + " [UNKNOWN]";
+ }
}
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
index 968c6e5..0f36d32 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -1,5 +1,6 @@
package com.android.server.usage;
+import android.annotation.CurrentTimeMillisLong;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.usage.AppStandbyInfo;
@@ -23,7 +24,7 @@
try {
final Class<?> clazz = Class.forName("com.android.server.usage.AppStandbyController",
true, loader);
- final Constructor<?> ctor = clazz.getConstructor(Context.class);
+ final Constructor<?> ctor = clazz.getConstructor(Context.class);
return (AppStandbyInternal) ctor.newInstance(context);
} catch (NoSuchMethodException | InstantiationException
| IllegalAccessException | InvocationTargetException | ClassNotFoundException e) {
@@ -71,6 +72,16 @@
long getTimeSinceLastJobRun(String packageName, int userId);
+ void setEstimatedLaunchTime(String packageName, int userId,
+ @CurrentTimeMillisLong long launchTimeMs);
+
+ /**
+ * Returns the saved estimated launch time for the app. Will return {@code Long#MAX_VALUE} if no
+ * value is saved.
+ */
+ @CurrentTimeMillisLong
+ long getEstimatedLaunchTime(String packageName, int userId);
+
/**
* Returns the time (in milliseconds) since the app was last interacted with by the user.
* This can be larger than the current elapsedRealtime, in case it happened before boot or
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index e4b1e3e..78140dc 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -21,6 +21,7 @@
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -438,6 +439,7 @@
case Constants.KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS:
case Constants.KEY_RUNTIME_MIN_GUARANTEE_MS:
case Constants.KEY_RUNTIME_MIN_EJ_GUARANTEE_MS:
+ case Constants.KEY_RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS:
if (!runtimeUpdated) {
mConstants.updateRuntimeConstantsLocked();
runtimeUpdated = true;
@@ -504,6 +506,8 @@
"runtime_free_quota_max_limit_ms";
private static final String KEY_RUNTIME_MIN_GUARANTEE_MS = "runtime_min_guarantee_ms";
private static final String KEY_RUNTIME_MIN_EJ_GUARANTEE_MS = "runtime_min_ej_guarantee_ms";
+ private static final String KEY_RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS =
+ "runtime_min_high_priority_guarantee_ms";
private static final int DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT = 5;
private static final long DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = 31 * MINUTE_IN_MILLIS;
@@ -525,6 +529,8 @@
public static final long DEFAULT_RUNTIME_MIN_GUARANTEE_MS = 10 * MINUTE_IN_MILLIS;
@VisibleForTesting
public static final long DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS = 3 * MINUTE_IN_MILLIS;
+ @VisibleForTesting
+ static final long DEFAULT_RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS = 5 * MINUTE_IN_MILLIS;
private static final boolean DEFAULT_USE_TARE_POLICY = false;
/**
@@ -612,6 +618,12 @@
public long RUNTIME_MIN_EJ_GUARANTEE_MS = DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS;
/**
+ * The minimum amount of time we try to guarantee high priority jobs will run for.
+ */
+ public long RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS =
+ DEFAULT_RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS;
+
+ /**
* If true, use TARE policy for job limiting. If false, use quotas.
*/
public boolean USE_TARE_POLICY = DEFAULT_USE_TARE_POLICY;
@@ -686,12 +698,18 @@
DeviceConfig.Properties properties = DeviceConfig.getProperties(
DeviceConfig.NAMESPACE_JOB_SCHEDULER,
KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
- KEY_RUNTIME_MIN_GUARANTEE_MS, KEY_RUNTIME_MIN_EJ_GUARANTEE_MS);
+ KEY_RUNTIME_MIN_GUARANTEE_MS, KEY_RUNTIME_MIN_EJ_GUARANTEE_MS,
+ KEY_RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS);
// Make sure min runtime for regular jobs is at least 10 minutes.
RUNTIME_MIN_GUARANTEE_MS = Math.max(10 * MINUTE_IN_MILLIS,
properties.getLong(
KEY_RUNTIME_MIN_GUARANTEE_MS, DEFAULT_RUNTIME_MIN_GUARANTEE_MS));
+ // Make sure min runtime for high priority jobs is at least 4 minutes.
+ RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS = Math.max(4 * MINUTE_IN_MILLIS,
+ properties.getLong(
+ KEY_RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS,
+ DEFAULT_RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS));
// Make sure min runtime for expedited jobs is at least one minute.
RUNTIME_MIN_EJ_GUARANTEE_MS = Math.max(MINUTE_IN_MILLIS,
properties.getLong(
@@ -739,6 +757,8 @@
pw.print(KEY_RUNTIME_MIN_GUARANTEE_MS, RUNTIME_MIN_GUARANTEE_MS).println();
pw.print(KEY_RUNTIME_MIN_EJ_GUARANTEE_MS, RUNTIME_MIN_EJ_GUARANTEE_MS).println();
+ pw.print(KEY_RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS,
+ RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS).println();
pw.print(KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, RUNTIME_FREE_QUOTA_MAX_LIMIT_MS)
.println();
@@ -775,7 +795,31 @@
@VisibleForTesting
class PendingJobComparator implements Comparator<JobStatus> {
- private final SparseLongArray mEarliestRegEnqueueTimeCache = new SparseLongArray();
+ private static final int EJ_PRIORITY_MODIFIER = 10;
+
+ /** Cache of the earliest non-PRIORITY_MAX enqueue time found per UID. */
+ private final SparseLongArray mEarliestNonMaxEnqueueTimeCache = new SparseLongArray();
+ /**
+ * Cache of the last enqueue time of each priority for each UID. The SparseArray is keyed
+ * by UID and the SparseLongArray is keyed by the priority.
+ */
+ private final SparseArray<SparseLongArray> mLastPriorityEnqueueTimeCache =
+ new SparseArray<>();
+ /**
+ * The earliest enqueue time each UID's priority's jobs should use. The SparseArray is keyed
+ * by UID and the SparseLongArray is keyed by the value returned from
+ * {@link #getPriorityIndex(int, boolean)}.
+ */
+ private final SparseArray<SparseLongArray> mEarliestAllowedEnqueueTimes =
+ new SparseArray<>();
+
+ private int getPriorityIndex(int priority, boolean isEJ) {
+ // We need to separate HIGH priority EJs from HIGH priority regular jobs.
+ if (isEJ) {
+ return priority * EJ_PRIORITY_MODIFIER;
+ }
+ return priority;
+ }
/**
* Refresh sorting determinants based on the current state of {@link #mPendingJobs}.
@@ -783,17 +827,82 @@
@GuardedBy("mLock")
@VisibleForTesting
void refreshLocked() {
- mEarliestRegEnqueueTimeCache.clear();
+ mEarliestNonMaxEnqueueTimeCache.clear();
for (int i = 0; i < mPendingJobs.size(); ++i) {
final JobStatus job = mPendingJobs.get(i);
final int uid = job.getSourceUid();
- if (!job.isRequestedExpeditedJob()) {
+ if (job.getEffectivePriority() < JobInfo.PRIORITY_MAX) {
final long earliestEnqueueTime =
- mEarliestRegEnqueueTimeCache.get(uid, Long.MAX_VALUE);
- mEarliestRegEnqueueTimeCache.put(uid,
+ mEarliestNonMaxEnqueueTimeCache.get(uid, Long.MAX_VALUE);
+ mEarliestNonMaxEnqueueTimeCache.put(uid,
Math.min(earliestEnqueueTime, job.enqueueTime));
}
+
+ final int pIdx =
+ getPriorityIndex(job.getEffectivePriority(), job.isRequestedExpeditedJob());
+ SparseLongArray lastPriorityEnqueueTime = mLastPriorityEnqueueTimeCache.get(uid);
+ if (lastPriorityEnqueueTime == null) {
+ lastPriorityEnqueueTime = new SparseLongArray();
+ mLastPriorityEnqueueTimeCache.put(uid, lastPriorityEnqueueTime);
+ }
+ lastPriorityEnqueueTime.put(pIdx,
+ Math.max(job.enqueueTime, lastPriorityEnqueueTime.get(pIdx, 0)));
}
+
+ // Move lower priority jobs behind higher priority jobs (instead of moving higher
+ // priority jobs ahead of lower priority jobs), except for EJs.
+ for (int i = 0; i < mLastPriorityEnqueueTimeCache.size(); ++i) {
+ final int uid = mLastPriorityEnqueueTimeCache.keyAt(i);
+ SparseLongArray lastEnqueueTimes = mLastPriorityEnqueueTimeCache.valueAt(i);
+ SparseLongArray earliestAllowedEnqueueTimes = new SparseLongArray();
+ mEarliestAllowedEnqueueTimes.put(uid, earliestAllowedEnqueueTimes);
+ long earliestAllowedEnqueueTime = mEarliestNonMaxEnqueueTimeCache.get(uid,
+ lastEnqueueTimes.get(getPriorityIndex(JobInfo.PRIORITY_MAX, true), -1));
+ earliestAllowedEnqueueTimes.put(getPriorityIndex(JobInfo.PRIORITY_MAX, true),
+ earliestAllowedEnqueueTime);
+ earliestAllowedEnqueueTime = 1
+ + Math.max(earliestAllowedEnqueueTime,
+ lastEnqueueTimes.get(getPriorityIndex(JobInfo.PRIORITY_HIGH, true), -1));
+ earliestAllowedEnqueueTimes.put(getPriorityIndex(JobInfo.PRIORITY_HIGH, true),
+ earliestAllowedEnqueueTime);
+ earliestAllowedEnqueueTime++;
+ for (int p = JobInfo.PRIORITY_HIGH; p >= JobInfo.PRIORITY_MIN; --p) {
+ final int pIdx = getPriorityIndex(p, false);
+ earliestAllowedEnqueueTimes.put(pIdx, earliestAllowedEnqueueTime);
+ final long lastEnqueueTime = lastEnqueueTimes.get(pIdx, -1);
+ if (lastEnqueueTime != -1) {
+ // Add additional millisecond for the next priority to ensure sorting is
+ // stable/accurate when comparing to other apps.
+ earliestAllowedEnqueueTime = 1
+ + Math.max(earliestAllowedEnqueueTime, lastEnqueueTime);
+ }
+ }
+ }
+
+ // Clear intermediate state that we don't need to reduce steady state memory usage.
+ mLastPriorityEnqueueTimeCache.clear();
+ }
+
+ @ElapsedRealtimeLong
+ private long getEffectiveEnqueueTime(@NonNull JobStatus job) {
+ // Move lower priority jobs behind higher priority jobs (instead of moving higher
+ // priority jobs ahead of lower priority jobs), except for MAX EJs.
+ final int uid = job.getSourceUid();
+ if (job.isRequestedExpeditedJob()
+ && job.getEffectivePriority() == JobInfo.PRIORITY_MAX) {
+ return Math.min(job.enqueueTime,
+ mEarliestNonMaxEnqueueTimeCache.get(uid, Long.MAX_VALUE));
+ }
+ final int priorityIdx =
+ getPriorityIndex(job.getEffectivePriority(), job.isRequestedExpeditedJob());
+ final SparseLongArray earliestAllowedEnqueueTimes =
+ mEarliestAllowedEnqueueTimes.get(uid);
+ if (earliestAllowedEnqueueTimes == null) {
+ // We're probably trying to insert directly without refreshing the internal arrays.
+ // Since we haven't seen this UID before, we can just use the job's enqueue time.
+ return job.enqueueTime;
+ }
+ return Math.max(job.enqueueTime, earliestAllowedEnqueueTimes.get(priorityIdx));
}
@Override
@@ -816,38 +925,39 @@
// expedited job quota.
return o1EJ ? -1 : 1;
}
- }
- if (o1EJ || o2EJ) {
- // We MUST prioritize EJs ahead of regular jobs within a single app. Since we do
- // that, in order to satisfy the transitivity constraint of the comparator, if
- // any UID has an EJ, we must ensure that the EJ is ordered ahead of the regular
- // job of a different app IF the app with an EJ had another job that came before
- // the differing app. For example, if app A has regJob1 at t1 and eJob3 at t3 and
- // app B has regJob2 at t2, eJob3 must be ordered before regJob2 because it will be
- // ordered before regJob1.
- // Regular jobs don't need to jump the line.
+ if (o1.getEffectivePriority() != o2.getEffectivePriority()) {
+ // Use the priority set by an app for intra-app job ordering. Higher
+ // priority should be before lower priority.
+ return o2.getEffectivePriority() - o1.getEffectivePriority();
+ }
+ } else {
+ // TODO: see if we can simplify this using explicit topological sorting
+ // Since we order jobs within a UID by the job's priority, in order to satisfy the
+ // transitivity constraint of the comparator, we must ensure consistent/appropriate
+ // ordering between apps as well. That is, if a job is ordered before or behind
+ // another job because of its priority, that ordering must translate to the
+ // relative ordering against other jobs.
+ // The effective ordering implementation here is to use HIGH priority EJs as a
+ // pivot point. MAX priority EJs are moved *ahead* of HIGH priority EJs. All
+ // regular jobs are moved *behind* HIGH priority EJs. The intention for moving jobs
+ // "behind" the EJs instead of moving all high priority jobs before lower priority
+ // jobs is to reduce any potential abuse (or just unfortunate execution) cases where
+ // there are early low priority jobs that don't get to run because so many of the
+ // app's high priority jobs are pushed before low priority job. This may still
+ // happen because of the job ordering mechanism, but moving jobs back prevents
+ // one app's jobs from always being at the front (due to the early scheduled low
+ // priority job and our base case of sorting by enqueue time).
- final long uid1EarliestRegEnqueueTime = Math.min(o1.enqueueTime,
- mEarliestRegEnqueueTimeCache.get(o1.getSourceUid(), Long.MAX_VALUE));
- final long uid2EarliestRegEnqueueTime = Math.min(o2.enqueueTime,
- mEarliestRegEnqueueTimeCache.get(o2.getSourceUid(), Long.MAX_VALUE));
+ final long o1EffectiveEnqueueTime = getEffectiveEnqueueTime(o1);
+ final long o2EffectiveEnqueueTime = getEffectiveEnqueueTime(o2);
- if (o1EJ && o2EJ) {
- if (uid1EarliestRegEnqueueTime < uid2EarliestRegEnqueueTime) {
- return -1;
- } else if (uid1EarliestRegEnqueueTime > uid2EarliestRegEnqueueTime) {
- return 1;
- }
- } else if (o1EJ && uid1EarliestRegEnqueueTime <= o2.enqueueTime) {
- // Include = to ensure that if we sorted an EJ ahead of a regular job at time X
- // then we make sure to sort it ahead of all regular jobs at time X.
+ if (o1EffectiveEnqueueTime < o2EffectiveEnqueueTime) {
return -1;
- } else if (o2EJ && uid2EarliestRegEnqueueTime <= o1.enqueueTime) {
- // Include = to ensure that if we sorted an EJ ahead of a regular job at time X
- // then we make sure to sort it ahead of all regular jobs at time X.
+ } else if (o1EffectiveEnqueueTime > o2EffectiveEnqueueTime) {
return 1;
}
}
+
if (o1.enqueueTime < o2.enqueueTime) {
return -1;
}
@@ -2313,8 +2423,8 @@
private void postProcessLocked() {
noteJobsPending(newReadyJobs);
mPendingJobs.addAll(newReadyJobs);
+ mPendingJobComparator.refreshLocked();
if (mPendingJobs.size() > 1) {
- mPendingJobComparator.refreshLocked();
mPendingJobs.sort(mPendingJobComparator);
}
@@ -2442,8 +2552,8 @@
}
noteJobsPending(runnableJobs);
mPendingJobs.addAll(runnableJobs);
+ mPendingJobComparator.refreshLocked();
if (mPendingJobs.size() > 1) {
- mPendingJobComparator.refreshLocked();
mPendingJobs.sort(mPendingJobComparator);
}
} else {
@@ -2653,6 +2763,8 @@
return job.getEffectiveStandbyBucket() != RESTRICTED_INDEX
? mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS
: Math.min(mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 5 * MINUTE_IN_MILLIS);
+ } else if (job.getEffectivePriority() == JobInfo.PRIORITY_HIGH) {
+ return mConstants.RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS;
} else {
return mConstants.RUNTIME_MIN_GUARANTEE_MS;
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 5bdee5e..b44178f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -16,6 +16,8 @@
package com.android.server.job;
+import static android.app.job.JobInfo.getPriorityString;
+
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
@@ -378,18 +380,40 @@
@EconomicPolicy.AppAction
private static int getStartActionId(@NonNull JobStatus job) {
- if (job.startedAsExpeditedJob || job.shouldTreatAsExpeditedJob()) {
- return JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START;
+ switch (job.getEffectivePriority()) {
+ case JobInfo.PRIORITY_MAX:
+ return JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START;
+ case JobInfo.PRIORITY_HIGH:
+ return JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START;
+ case JobInfo.PRIORITY_LOW:
+ return JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START;
+ case JobInfo.PRIORITY_MIN:
+ return JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START;
+ default:
+ Slog.wtf(TAG, "Unknown priority: " + getPriorityString(job.getEffectivePriority()));
+ // Intentional fallthrough
+ case JobInfo.PRIORITY_DEFAULT:
+ return JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_START;
}
- return JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_START;
}
@EconomicPolicy.AppAction
private static int getRunningActionId(@NonNull JobStatus job) {
- if (job.startedAsExpeditedJob || job.shouldTreatAsExpeditedJob()) {
- return JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING;
+ switch (job.getEffectivePriority()) {
+ case JobInfo.PRIORITY_MAX:
+ return JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING;
+ case JobInfo.PRIORITY_HIGH:
+ return JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING;
+ case JobInfo.PRIORITY_LOW:
+ return JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING;
+ case JobInfo.PRIORITY_MIN:
+ return JobSchedulerEconomicPolicy.ACTION_JOB_MIN_RUNNING;
+ default:
+ Slog.wtf(TAG, "Unknown priority: " + getPriorityString(job.getEffectivePriority()));
+ // Intentional fallthrough
+ case JobInfo.PRIORITY_DEFAULT:
+ return JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING;
}
- return JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING;
}
/**
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index b1ea14d..a8dd752 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -335,7 +335,7 @@
}
/** Version of the db schema. */
- private static final int JOBS_FILE_VERSION = 0;
+ private static final int JOBS_FILE_VERSION = 1;
/** Tag corresponds to constraints this job needs. */
private static final String XML_TAG_PARAMS_CONSTRAINTS = "constraints";
/** Tag corresponds to execution parameters. */
@@ -548,6 +548,7 @@
out.attribute(null, "sourceUserId", String.valueOf(jobStatus.getSourceUserId()));
out.attribute(null, "uid", Integer.toString(jobStatus.getUid()));
out.attribute(null, "bias", String.valueOf(jobStatus.getBias()));
+ out.attribute(null, "priority", String.valueOf(jobStatus.getEffectivePriority()));
out.attribute(null, "flags", String.valueOf(jobStatus.getFlags()));
if (jobStatus.getInternalFlags() != 0) {
out.attribute(null, "internalFlags", String.valueOf(jobStatus.getInternalFlags()));
@@ -771,10 +772,11 @@
String tagName = parser.getName();
if ("job-info".equals(tagName)) {
final List<JobStatus> jobs = new ArrayList<JobStatus>();
+ final int version;
// Read in version info.
try {
- int version = Integer.parseInt(parser.getAttributeValue(null, "version"));
- if (version != JOBS_FILE_VERSION) {
+ version = Integer.parseInt(parser.getAttributeValue(null, "version"));
+ if (version > JOBS_FILE_VERSION || version < 0) {
Slog.d(TAG, "Invalid version number, aborting jobs file read.");
return null;
}
@@ -789,7 +791,7 @@
tagName = parser.getName();
// Start reading job.
if ("job".equals(tagName)) {
- JobStatus persistedJob = restoreJobFromXml(rtcIsGood, parser);
+ JobStatus persistedJob = restoreJobFromXml(rtcIsGood, parser, version);
if (persistedJob != null) {
if (DEBUG) {
Slog.d(TAG, "Read out " + persistedJob);
@@ -812,8 +814,8 @@
* will take the parser into the body of the job tag.
* @return Newly instantiated job holding all the information we just read out of the xml tag.
*/
- private JobStatus restoreJobFromXml(boolean rtcIsGood, XmlPullParser parser)
- throws XmlPullParserException, IOException {
+ private JobStatus restoreJobFromXml(boolean rtcIsGood, XmlPullParser parser,
+ int schemaVersion) throws XmlPullParserException, IOException {
JobInfo.Builder jobBuilder;
int uid, sourceUserId;
long lastSuccessfulRunTime;
@@ -826,12 +828,21 @@
jobBuilder.setPersisted(true);
uid = Integer.parseInt(parser.getAttributeValue(null, "uid"));
- String val = parser.getAttributeValue(null, "bias");
- if (val == null) {
+ String val;
+ if (schemaVersion == 0) {
val = parser.getAttributeValue(null, "priority");
- }
- if (val != null) {
- jobBuilder.setBias(Integer.parseInt(val));
+ if (val != null) {
+ jobBuilder.setBias(Integer.parseInt(val));
+ }
+ } else if (schemaVersion >= 1) {
+ val = parser.getAttributeValue(null, "bias");
+ if (val != null) {
+ jobBuilder.setBias(Integer.parseInt(val));
+ }
+ val = parser.getAttributeValue(null, "priority");
+ if (val != null) {
+ jobBuilder.setPriority(Integer.parseInt(val));
+ }
}
val = parser.getAttributeValue(null, "flags");
if (val != null) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
index 56aa590..7d12b95 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
@@ -4,6 +4,7 @@
"name": "CtsJobSchedulerTestCases",
"options": [
{"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "android.platform.test.annotations.LargeTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.LargeTest"}
]
@@ -13,6 +14,7 @@
"options": [
{"include-filter": "com.android.server.job"},
{"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "android.platform.test.annotations.LargeTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
},
@@ -21,6 +23,7 @@
"options": [
{"include-filter": "com.android.server.job"},
{"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "android.platform.test.annotations.LargeTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index dee716f..f74a4fa 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -22,6 +22,7 @@
import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+import android.annotation.ElapsedRealtimeLong;
import android.app.AppGlobals;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
@@ -349,6 +350,7 @@
public int overrideState = JobStatus.OVERRIDE_NONE;
// When this job was enqueued, for ordering. (in elapsedRealtimeMillis)
+ @ElapsedRealtimeLong
public long enqueueTime;
// Metrics about queue latency. (in uptimeMillis)
@@ -928,6 +930,30 @@
return job.getBias();
}
+ /**
+ * Returns the priority of the job, which may be adjusted due to various factors.
+ * @see JobInfo.Builder#setPriority(int)
+ */
+ @JobInfo.Priority
+ public int getEffectivePriority() {
+ final int rawPriority = job.getPriority();
+ if (numFailures < 2) {
+ return rawPriority;
+ }
+ // Slowly decay priority of jobs to prevent starvation of other jobs.
+ if (isRequestedExpeditedJob()) {
+ // EJs can't fall below HIGH priority.
+ return JobInfo.PRIORITY_HIGH;
+ }
+ // Set a maximum priority based on the number of failures.
+ final int dropPower = numFailures / 2;
+ switch (dropPower) {
+ case 1: return Math.min(JobInfo.PRIORITY_DEFAULT, rawPriority);
+ case 2: return Math.min(JobInfo.PRIORITY_LOW, rawPriority);
+ default: return JobInfo.PRIORITY_MIN;
+ }
+ }
+
public int getFlags() {
return job.getFlags();
}
@@ -1951,6 +1977,14 @@
pw.print("Bias: ");
pw.println(JobInfo.getBiasString(job.getBias()));
}
+ pw.print("Priority: ");
+ pw.print(JobInfo.getPriorityString(job.getPriority()));
+ final int effectivePriority = getEffectivePriority();
+ if (effectivePriority != job.getPriority()) {
+ pw.print(" effective=");
+ pw.print(JobInfo.getPriorityString(effectivePriority));
+ }
+ pw.println();
if (job.getFlags() != 0) {
pw.print("Flags: ");
pw.println(Integer.toHexString(job.getFlags()));
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
index 6232dfb..393f368 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
@@ -25,8 +25,12 @@
import android.annotation.CurrentTimeMillisLong;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
+import android.app.usage.UsageStatsManagerInternal;
+import android.app.usage.UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener;
import android.content.Context;
+import android.os.Handler;
import android.os.Looper;
+import android.os.Message;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.util.ArraySet;
@@ -38,7 +42,9 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.SomeArgs;
import com.android.server.JobSchedulerBackgroundThread;
+import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
import com.android.server.utils.AlarmQueue;
@@ -53,6 +59,9 @@
|| Log.isLoggable(TAG, Log.DEBUG);
private final PcConstants mPcConstants;
+ private final PcHandler mHandler;
+
+ private final UsageStatsManagerInternal mUsageStatsManagerInternal;
@GuardedBy("mLock")
private final SparseArrayMap<String, ArraySet<JobStatus>> mTrackedJobs = new SparseArrayMap<>();
@@ -72,11 +81,34 @@
@CurrentTimeMillisLong
private long mLaunchTimeThresholdMs = PcConstants.DEFAULT_LAUNCH_TIME_THRESHOLD_MS;
+ @SuppressWarnings("FieldCanBeLocal")
+ private final EstimatedLaunchTimeChangedListener mEstimatedLaunchTimeChangedListener =
+ new EstimatedLaunchTimeChangedListener() {
+ @Override
+ public void onEstimatedLaunchTimeChanged(int userId, @NonNull String packageName,
+ @CurrentTimeMillisLong long newEstimatedLaunchTime) {
+ final SomeArgs args = SomeArgs.obtain();
+ args.arg1 = packageName;
+ args.argi1 = userId;
+ args.argl1 = newEstimatedLaunchTime;
+ mHandler.obtainMessage(MSG_PROCESS_UPDATED_ESTIMATED_LAUNCH_TIME, args)
+ .sendToTarget();
+ }
+ };
+
+ private static final int MSG_RETRIEVE_ESTIMATED_LAUNCH_TIME = 0;
+ private static final int MSG_PROCESS_UPDATED_ESTIMATED_LAUNCH_TIME = 1;
+
public PrefetchController(JobSchedulerService service) {
super(service);
mPcConstants = new PcConstants();
+ mHandler = new PcHandler(mContext.getMainLooper());
mThresholdAlarmListener = new ThresholdAlarmListener(
mContext, JobSchedulerBackgroundThread.get().getLooper());
+ mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class);
+
+ mUsageStatsManagerInternal
+ .registerLaunchTimeChangedListener(mEstimatedLaunchTimeChangedListener);
}
@Override
@@ -146,11 +178,14 @@
@CurrentTimeMillisLong
private long getNextEstimatedLaunchTimeLocked(int userId, @NonNull String pkgName,
@CurrentTimeMillisLong long now) {
- Long nextEstimatedLaunchTime = mEstimatedLaunchTimes.get(userId, pkgName);
+ final Long nextEstimatedLaunchTime = mEstimatedLaunchTimes.get(userId, pkgName);
if (nextEstimatedLaunchTime == null || nextEstimatedLaunchTime < now) {
- // TODO(194532703): get estimated time from UsageStats
- nextEstimatedLaunchTime = now + 2 * HOUR_IN_MILLIS;
- mEstimatedLaunchTimes.add(userId, pkgName, nextEstimatedLaunchTime);
+ // Don't query usage stats here because it may have to read from disk.
+ mHandler.obtainMessage(MSG_RETRIEVE_ESTIMATED_LAUNCH_TIME, userId, 0, pkgName)
+ .sendToTarget();
+ // Store something in the cache so we don't keep posting retrieval messages.
+ mEstimatedLaunchTimes.add(userId, pkgName, Long.MAX_VALUE);
+ return Long.MAX_VALUE;
}
return nextEstimatedLaunchTime;
}
@@ -170,6 +205,42 @@
return changed;
}
+ private void processUpdatedEstimatedLaunchTime(int userId, @NonNull String pkgName,
+ @CurrentTimeMillisLong long newEstimatedLaunchTime) {
+ if (DEBUG) {
+ Slog.d(TAG, "Estimated launch time for " + packageToString(userId, pkgName)
+ + " changed to " + newEstimatedLaunchTime
+ + " ("
+ + TimeUtils.formatDuration(newEstimatedLaunchTime - sSystemClock.millis())
+ + " from now)");
+ }
+
+ synchronized (mLock) {
+ final ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, pkgName);
+ if (jobs == null) {
+ if (DEBUG) {
+ Slog.i(TAG,
+ "Not caching launch time since we haven't seen any prefetch"
+ + " jobs for " + packageToString(userId, pkgName));
+ }
+ } else {
+ // Don't bother caching the value unless the app has scheduled prefetch jobs
+ // before. This is based on the assumption that if an app has scheduled a
+ // prefetch job before, then it will probably schedule another one again.
+ mEstimatedLaunchTimes.add(userId, pkgName, newEstimatedLaunchTime);
+
+ if (!jobs.isEmpty()) {
+ final long now = sSystemClock.millis();
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ updateThresholdAlarmLocked(userId, pkgName, now, nowElapsed);
+ if (maybeUpdateConstraintForPkgLocked(now, nowElapsed, userId, pkgName)) {
+ mStateChangedListener.onControllerStateChanged(jobs);
+ }
+ }
+ }
+ }
+ }
+
@GuardedBy("mLock")
private boolean updateConstraintLocked(@NonNull JobStatus jobStatus,
@CurrentTimeMillisLong long now, @ElapsedRealtimeLong long nowElapsed) {
@@ -289,6 +360,49 @@
}
}
+ private class PcHandler extends Handler {
+ PcHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_RETRIEVE_ESTIMATED_LAUNCH_TIME:
+ final int userId = msg.arg1;
+ final String pkgName = (String) msg.obj;
+ // It's okay to get the time without holding the lock since all updates to
+ // the local cache go through the handler (and therefore will be sequential).
+ final long nextEstimatedLaunchTime = mUsageStatsManagerInternal
+ .getEstimatedPackageLaunchTime(pkgName, userId);
+ if (DEBUG) {
+ Slog.d(TAG, "Retrieved launch time for "
+ + packageToString(userId, pkgName)
+ + " of " + nextEstimatedLaunchTime
+ + " (" + TimeUtils.formatDuration(
+ nextEstimatedLaunchTime - sSystemClock.millis())
+ + " from now)");
+ }
+ synchronized (mLock) {
+ final Long curEstimatedLaunchTime =
+ mEstimatedLaunchTimes.get(userId, pkgName);
+ if (curEstimatedLaunchTime == null
+ || nextEstimatedLaunchTime != curEstimatedLaunchTime) {
+ processUpdatedEstimatedLaunchTime(
+ userId, pkgName, nextEstimatedLaunchTime);
+ }
+ }
+ break;
+
+ case MSG_PROCESS_UPDATED_ESTIMATED_LAUNCH_TIME:
+ final SomeArgs args = (SomeArgs) msg.obj;
+ processUpdatedEstimatedLaunchTime(args.argi1, (String) args.arg1, args.argl1);
+ args.recycle();
+ break;
+ }
+ }
+ }
+
@VisibleForTesting
class PcConstants {
private boolean mShouldReevaluateConstraints = false;
@@ -366,7 +480,8 @@
final String pkgName = mEstimatedLaunchTimes.keyAt(u, p);
final long estimatedLaunchTime = mEstimatedLaunchTimes.valueAt(u, p);
- pw.print("<" + userId + ">" + pkgName + ": ");
+ pw.print(packageToString(userId, pkgName));
+ pw.print(": ");
pw.print(estimatedLaunchTime);
pw.print(" (");
TimeUtils.formatDuration(estimatedLaunchTime - now, pw,
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 31da526..29c1108 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -882,6 +882,7 @@
if (isQuotaFreeLocked(standbyBucket)) return true;
ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
+ // TODO: use a higher minimum remaining time for jobs with MINIMUM priority
return getRemainingExecutionTimeLocked(stats) > 0
&& isUnderJobCountQuotaLocked(stats, standbyBucket)
&& isUnderSessionCountQuotaLocked(stats, standbyBucket);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
index a9b0465..40244e8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
@@ -16,28 +16,30 @@
package com.android.server.job.restrictions;
+import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.os.PowerManager;
import android.os.PowerManager.OnThermalStatusChangedListener;
import android.util.IndentingPrintWriter;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.controllers.JobStatus;
public class ThermalStatusRestriction extends JobRestriction {
private static final String TAG = "ThermalStatusRestriction";
- /** The threshold at which we start restricting non-EJ jobs. */
- private static final int REGULAR_JOB_THRESHOLD = PowerManager.THERMAL_STATUS_SEVERE;
+ /** The threshold at which we start restricting low and min priority jobs. */
+ private static final int LOW_PRIORITY_THRESHOLD = PowerManager.THERMAL_STATUS_LIGHT;
+ /** The threshold at which we start restricting higher priority jobs. */
+ private static final int HIGHER_PRIORITY_THRESHOLD = PowerManager.THERMAL_STATUS_MODERATE;
/** The lowest threshold at which we start restricting jobs. */
- private static final int LOWER_THRESHOLD = REGULAR_JOB_THRESHOLD;
+ private static final int LOWER_THRESHOLD = LOW_PRIORITY_THRESHOLD;
/** The threshold at which we start restricting ALL jobs. */
- private static final int UPPER_THRESHOLD = PowerManager.THERMAL_STATUS_CRITICAL;
+ private static final int UPPER_THRESHOLD = PowerManager.THERMAL_STATUS_SEVERE;
private volatile int mThermalStatus = PowerManager.THERMAL_STATUS_NONE;
- private PowerManager mPowerManager;
-
public ThermalStatusRestriction(JobSchedulerService service) {
super(service, JobParameters.STOP_REASON_DEVICE_STATE,
JobParameters.INTERNAL_STOP_REASON_DEVICE_THERMAL);
@@ -45,9 +47,10 @@
@Override
public void onSystemServicesReady() {
- mPowerManager = mService.getContext().getSystemService(PowerManager.class);
+ final PowerManager powerManager =
+ mService.getTestableContext().getSystemService(PowerManager.class);
// Use MainExecutor
- mPowerManager.addThermalStatusListener(new OnThermalStatusChangedListener() {
+ powerManager.addThermalStatusListener(new OnThermalStatusChangedListener() {
@Override
public void onThermalStatusChanged(int status) {
// This is called on the main thread. Do not do any slow operations in it.
@@ -83,12 +86,28 @@
if (mThermalStatus >= UPPER_THRESHOLD) {
return true;
}
- if (mThermalStatus >= REGULAR_JOB_THRESHOLD) {
- return !job.shouldTreatAsExpeditedJob();
+ final int priority = job.getEffectivePriority();
+ if (mThermalStatus >= HIGHER_PRIORITY_THRESHOLD) {
+ // For moderate throttling, only let expedited jobs and high priority regular jobs that
+ // are already running run.
+ return !job.shouldTreatAsExpeditedJob()
+ && !(priority == JobInfo.PRIORITY_HIGH
+ && mService.isCurrentlyRunningLocked(job));
+ }
+ if (mThermalStatus >= LOW_PRIORITY_THRESHOLD) {
+ // For light throttling, throttle all min priority jobs and all low priority jobs that
+ // aren't already running.
+ return (priority == JobInfo.PRIORITY_LOW && !mService.isCurrentlyRunningLocked(job))
+ || priority == JobInfo.PRIORITY_MIN;
}
return false;
}
+ @VisibleForTesting
+ int getThermalStatus() {
+ return mThermalStatus;
+ }
+
@Override
public void dumpConstants(IndentingPrintWriter pw) {
pw.print("Thermal status: ");
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index 1ad7407..1cd873c 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -44,7 +44,6 @@
import android.net.Uri;
import android.os.BatteryManagerInternal;
import android.os.Binder;
-import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -59,6 +58,7 @@
import android.util.SparseSetArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.server.LocalServices;
@@ -245,9 +245,8 @@
private static final int MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER = 0;
private static final int MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT = 1;
private static final int MSG_PROCESS_USAGE_EVENT = 2;
- private static final int MSG_MAYBE_FOCE_RECLAIM = 3;
+ private static final int MSG_MAYBE_FORCE_RECLAIM = 3;
private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*";
- private static final String KEY_PKG = "pkg";
/**
* Initializes the system service.
@@ -364,7 +363,7 @@
if (newBatteryLevel > mCurrentBatteryLevel) {
mAgent.distributeBasicIncomeLocked(newBatteryLevel);
} else if (newBatteryLevel < mCurrentBatteryLevel) {
- mHandler.obtainMessage(MSG_MAYBE_FOCE_RECLAIM).sendToTarget();
+ mHandler.obtainMessage(MSG_MAYBE_FORCE_RECLAIM).sendToTarget();
}
mCurrentBatteryLevel = newBatteryLevel;
}
@@ -463,12 +462,11 @@
Slog.d(TAG, userId + ":" + pkgName + " affordability changed to "
+ affordabilityNote.isCurrentlyAffordable());
}
- Message msg = mHandler.obtainMessage(
- MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER, userId, 0, affordabilityNote);
- Bundle data = new Bundle();
- data.putString(KEY_PKG, pkgName);
- msg.setData(data);
- msg.sendToTarget();
+ final SomeArgs args = SomeArgs.obtain();
+ args.argi1 = userId;
+ args.arg1 = pkgName;
+ args.arg2 = affordabilityNote;
+ mHandler.obtainMessage(MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER, args).sendToTarget();
}
@GuardedBy("mLock")
@@ -684,8 +682,8 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_MAYBE_FOCE_RECLAIM: {
- removeMessages(MSG_MAYBE_FOCE_RECLAIM);
+ case MSG_MAYBE_FORCE_RECLAIM: {
+ removeMessages(MSG_MAYBE_FORCE_RECLAIM);
synchronized (mLock) {
maybeForceReclaimLocked();
}
@@ -693,16 +691,19 @@
break;
case MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER: {
- Bundle data = msg.getData();
- final int userId = msg.arg1;
- final String pkgName = data.getString(KEY_PKG);
+ final SomeArgs args = (SomeArgs) msg.obj;
+ final int userId = args.argi1;
+ final String pkgName = (String) args.arg1;
final Agent.ActionAffordabilityNote affordabilityNote =
- (Agent.ActionAffordabilityNote) msg.obj;
+ (Agent.ActionAffordabilityNote) args.arg2;
+
final EconomyManagerInternal.AffordabilityChangeListener listener =
affordabilityNote.getListener();
listener.onAffordabilityChanged(userId, pkgName,
affordabilityNote.getActionBill(),
affordabilityNote.isCurrentlyAffordable());
+
+ args.recycle();
}
break;
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/README.md b/apex/jobscheduler/service/java/com/android/server/tare/README.md
new file mode 100644
index 0000000..a4933a1
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/README.md
@@ -0,0 +1,44 @@
+Welcome to The Android Resource Economy (TARE for short). If you're reading this, you may be
+wondering what all of this code is for and what it means. TARE is an attempt to apply economic
+principles to resource (principally battery) management. It acknowledges that battery is a limited
+resource on mobile devices and that the system must allocate and apportion those resources
+accordingly. Every action (running a job, firing an alarm, using the network, using the CPU,
+etc.) has a cost. Once that action has been performed and that bit of battery has been drained, it's
+no longer available for someone else (another app) to use until the user charges the device again.
+
+The key tenets of TARE are:
+
+1. Charge for actions --- when an app performs an action, reduce its access to resources in the
+ future. This should help remind everyone that everything they do has a cost.
+1. Reward for good actions --- reward and encourage behavior that provides value to the user
+1. Fine bad actions --- fine and discourage behavior that is bad for the user
+
+# Details
+
+To achieve the goal laid out by TARE, we introduce the concept of Android Resource Credits
+(ARCs for short).
+
+## How do ARCs work?
+
+ARCs are required to perform any action while in the background. Some actions may have a fixed cost.
+Others may be more dynamic (some may even allow apps to bid higher ARCs for some actions to have
+them prioritized). If the app doesn't have enough ARCs, the action can't be performed. Apps are
+granted ARCs (below a certain threshold) as the device charges. Apps are also granted ARCs for
+providing user value (eg. for doing things that engage the user).
+
+ARCs will be used across the entire system as one unified concept. When an app performs an action,
+it pulls from the same account, regardless of the action. This means that apps can choose to do more
+of one action in lieu of being able to do as much of another. For example, an app can choose to use
+all of its ARCs for jobs if it doesn't want to schedule any alarms.
+
+### Scaling
+
+With the ARC system, we can limit the total number of ARCs in circulation, thus limiting how much
+total work can be done, regardless of how many apps the user has installed.
+
+# Definitions
+
+* ARC: Android Resource Credits are the "currency" units used as an abstraction layer over the real
+ battery drain. They allow the system to standardize costs and prices across various devices.
+* NARC: The smallest unit of an ARC. A narc is 1 nano-ARC.
+* Satiated: used to refer to when the device is fully charged (at 100% battery level)
\ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index 187422b..8b17512 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -32,6 +32,8 @@
import static com.android.server.usage.AppStandbyController.isUserUsage;
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.ElapsedRealtimeLong;
import android.app.usage.AppStandbyInfo;
import android.app.usage.UsageStatsManager;
import android.os.SystemClock;
@@ -115,6 +117,8 @@
// Reason why the app was last marked for restriction.
private static final String ATTR_LAST_RESTRICTION_ATTEMPT_REASON =
"lastRestrictionAttemptReason";
+ // The next estimated launch time of the app, in ms since epoch.
+ private static final String ATTR_NEXT_ESTIMATED_APP_LAUNCH_TIME = "nextEstimatedAppLaunchTime";
// device on time = mElapsedDuration + (timeNow - mElapsedSnapshot)
private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration
@@ -151,6 +155,9 @@
int lastInformedBucket;
// The last time a job was run for this app, using elapsed timebase
long lastJobRunTime;
+ // The estimated time the app will be launched next, in milliseconds since epoch.
+ @CurrentTimeMillisLong
+ long nextEstimatedLaunchTime;
// When should the bucket active state timeout, in elapsed timebase, if greater than
// lastUsedElapsedTime.
// This is used to keep the app in a high bucket regardless of other timeouts and
@@ -411,6 +418,17 @@
}
/**
+ * Marks the next time the app is expected to be launched, in the current millis timebase.
+ */
+ public void setEstimatedLaunchTime(String packageName, int userId,
+ @ElapsedRealtimeLong long nowElapsed, @CurrentTimeMillisLong long launchTime) {
+ ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+ AppUsageHistory appUsageHistory =
+ getPackageHistory(userHistory, packageName, nowElapsed, true);
+ appUsageHistory.nextEstimatedLaunchTime = launchTime;
+ }
+
+ /**
* Marks the last time a job was run, with the given elapsedRealtime. The time stored is
* based on the elapsed timebase.
* @param packageName
@@ -443,6 +461,23 @@
}
/**
+ * Returns the next estimated launch time of this app. Will return {@link Long#MAX_VALUE} if
+ * there's no estimated time.
+ */
+ @CurrentTimeMillisLong
+ public long getEstimatedLaunchTime(String packageName, int userId, long nowElapsed) {
+ ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+ AppUsageHistory appUsageHistory =
+ getPackageHistory(userHistory, packageName, nowElapsed, false);
+ // Don't adjust the default, else it'll wrap around to a positive value
+ if (appUsageHistory == null
+ || appUsageHistory.nextEstimatedLaunchTime < System.currentTimeMillis()) {
+ return Long.MAX_VALUE;
+ }
+ return appUsageHistory.nextEstimatedLaunchTime;
+ }
+
+ /**
* Returns the time since the last job was run for this app. This can be larger than the
* current elapsedRealtime, in case it happened before boot or a really large value if no jobs
* were ever run.
@@ -671,6 +706,8 @@
Slog.wtf(TAG, "Unable to read last restrict reason", nfe);
}
}
+ appUsageHistory.nextEstimatedLaunchTime = getLongValue(parser,
+ ATTR_NEXT_ESTIMATED_APP_LAUNCH_TIME, 0);
appUsageHistory.lastInformedBucket = -1;
userHistory.put(packageName, appUsageHistory);
}
@@ -753,6 +790,10 @@
}
xml.attribute(null, ATTR_LAST_RESTRICTION_ATTEMPT_REASON,
Integer.toHexString(history.lastRestrictReason));
+ if (history.nextEstimatedLaunchTime > 0) {
+ xml.attribute(null, ATTR_NEXT_ESTIMATED_APP_LAUNCH_TIME,
+ Long.toString(history.nextEstimatedLaunchTime));
+ }
xml.endTag(null, TAG_PACKAGE);
}
@@ -779,6 +820,7 @@
idpw.println(" App Standby States:");
idpw.increaseIndent();
ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId);
+ final long now = System.currentTimeMillis();
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long totalElapsedTime = getElapsedTime(elapsedRealtime);
final long screenOnTime = getScreenOnTime(elapsedRealtime);
@@ -819,6 +861,10 @@
idpw.print(" lastRestrictReason="
+ UsageStatsManager.reasonToString(appUsageHistory.lastRestrictReason));
}
+ if (appUsageHistory.nextEstimatedLaunchTime > 0) {
+ idpw.print(" nextEstimatedLaunchTime=");
+ TimeUtils.formatDuration(appUsageHistory.nextEstimatedLaunchTime - now, idpw);
+ }
idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n"));
idpw.println();
}
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 096211b..abbae4e 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -54,6 +54,7 @@
import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
+import android.annotation.CurrentTimeMillisLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -1087,6 +1088,24 @@
}
@Override
+ public void setEstimatedLaunchTime(String packageName, int userId,
+ @CurrentTimeMillisLong long launchTime) {
+ final long nowElapsed = mInjector.elapsedRealtime();
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.setEstimatedLaunchTime(packageName, userId, nowElapsed, launchTime);
+ }
+ }
+
+ @Override
+ @CurrentTimeMillisLong
+ public long getEstimatedLaunchTime(String packageName, int userId) {
+ final long elapsedRealtime = mInjector.elapsedRealtime();
+ synchronized (mAppIdleLock) {
+ return mAppIdleHistory.getEstimatedLaunchTime(packageName, userId, elapsedRealtime);
+ }
+ }
+
+ @Override
public long getTimeSinceLastUsedByUser(String packageName, int userId) {
final long elapsedRealtime = mInjector.elapsedRealtime();
synchronized (mAppIdleLock) {
diff --git a/apex/media/framework/java/android/media/MediaCommunicationManager.java b/apex/media/framework/java/android/media/MediaCommunicationManager.java
index 0f00dbb..8ee9616 100644
--- a/apex/media/framework/java/android/media/MediaCommunicationManager.java
+++ b/apex/media/framework/java/android/media/MediaCommunicationManager.java
@@ -34,6 +34,8 @@
import android.util.Log;
import android.view.KeyEvent;
+import androidx.annotation.RequiresApi;
+
import com.android.internal.annotations.GuardedBy;
import com.android.modules.annotation.MinSdk;
import com.android.modules.utils.build.SdkLevel;
@@ -49,6 +51,7 @@
* that applications have published to express their ongoing media playback state.
*/
@MinSdk(Build.VERSION_CODES.S)
+@RequiresApi(Build.VERSION_CODES.S)
@SystemService(Context.MEDIA_COMMUNICATION_SERVICE)
public class MediaCommunicationManager {
private static final String TAG = "MediaCommunicationManager";
diff --git a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
index 75a56b72..d7ad97d 100644
--- a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
+++ b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
@@ -23,6 +23,8 @@
import android.content.Context;
import android.os.Build;
+import androidx.annotation.RequiresApi;
+
import com.android.modules.annotation.MinSdk;
import com.android.modules.utils.build.SdkLevel;
@@ -32,6 +34,7 @@
* @hide
*/
@MinSdk(Build.VERSION_CODES.S)
+@RequiresApi(Build.VERSION_CODES.S)
@SystemApi(client = Client.MODULE_LIBRARIES)
public class MediaFrameworkInitializer {
private MediaFrameworkInitializer() {
diff --git a/apex/media/framework/java/android/media/MediaParceledListSlice.java b/apex/media/framework/java/android/media/MediaParceledListSlice.java
index 47ac193..fb36e80 100644
--- a/apex/media/framework/java/android/media/MediaParceledListSlice.java
+++ b/apex/media/framework/java/android/media/MediaParceledListSlice.java
@@ -18,9 +18,12 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import androidx.annotation.RequiresApi;
+
import java.util.Collections;
import java.util.List;
@@ -41,6 +44,7 @@
* @hide
*/
@Deprecated
+@RequiresApi(Build.VERSION_CODES.S)
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final class MediaParceledListSlice<T extends Parcelable>
extends BaseMediaParceledListSlice<T> {
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index 8cc3bc0..b6f85c7 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -199,6 +199,7 @@
*
* </pre>
*/
+@RequiresApi(Build.VERSION_CODES.R)
public final class MediaParser {
/**
diff --git a/apex/media/framework/java/android/media/MediaTranscodingManager.java b/apex/media/framework/java/android/media/MediaTranscodingManager.java
index 7e4799c..3bfffbcd 100644
--- a/apex/media/framework/java/android/media/MediaTranscodingManager.java
+++ b/apex/media/framework/java/android/media/MediaTranscodingManager.java
@@ -33,6 +33,8 @@
import android.system.Os;
import android.util.Log;
+import androidx.annotation.RequiresApi;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.annotation.MinSdk;
@@ -85,6 +87,7 @@
@hide
*/
@MinSdk(Build.VERSION_CODES.S)
+@RequiresApi(Build.VERSION_CODES.S)
@SystemApi
public final class MediaTranscodingManager {
private static final String TAG = "MediaTranscodingManager";
diff --git a/apex/media/service/lint-baseline.xml b/apex/media/service/lint-baseline.xml
new file mode 100644
index 0000000..05ce17c
--- /dev/null
+++ b/apex/media/service/lint-baseline.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.1.0-dev">
+
+ <issue
+ id="NewApi"
+ message="Call requires API level S (current min is 29): `MediaParceledListSlice`"
+ errorLine1=" new MediaParceledListSlice<>(getSession2TokensLocked(ALL.getIdentifier()));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/apex/media/service/java/com/android/server/media/MediaCommunicationService.java"
+ line="242"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level S (current min is 29): `MediaParceledListSlice`"
+ errorLine1=" userSession2Tokens = new MediaParceledListSlice<>(getSession2TokensLocked(userId));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/apex/media/service/java/com/android/server/media/MediaCommunicationService.java"
+ line="243"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level S (current min is 29): `MediaParceledListSlice`"
+ errorLine1=" MediaParceledListSlice parceledListSlice = new MediaParceledListSlice<>(result);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/apex/media/service/java/com/android/server/media/MediaCommunicationService.java"
+ line="386"
+ column="60"/>
+ </issue>
+
+</issues>
diff --git a/api/Android.bp b/api/Android.bp
index 7590177..1bc50bd 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -100,6 +100,7 @@
":framework-graphics{.public.api.txt}",
":framework-media{.public.api.txt}",
":framework-mediaprovider{.public.api.txt}",
+ ":framework-nearby{.public.api.txt}",
":framework-permission{.public.api.txt}",
":framework-permission-s{.public.api.txt}",
":framework-scheduling{.public.api.txt}",
@@ -160,6 +161,7 @@
":framework-graphics{.public.stubs.source}",
":framework-media{.public.stubs.source}",
":framework-mediaprovider{.public.stubs.source}",
+ ":framework-nearby{.public.stubs.source}",
":framework-permission{.public.stubs.source}",
":framework-permission-s{.public.stubs.source}",
":framework-scheduling{.public.stubs.source}",
@@ -187,6 +189,7 @@
":framework-graphics{.public.removed-api.txt}",
":framework-media{.public.removed-api.txt}",
":framework-mediaprovider{.public.removed-api.txt}",
+ ":framework-nearby{.public.removed-api.txt}",
":framework-permission{.public.removed-api.txt}",
":framework-permission-s{.public.removed-api.txt}",
":framework-scheduling{.public.removed-api.txt}",
@@ -228,6 +231,7 @@
":framework-graphics{.system.api.txt}",
":framework-media{.system.api.txt}",
":framework-mediaprovider{.system.api.txt}",
+ ":framework-nearby{.system.api.txt}",
":framework-permission{.system.api.txt}",
":framework-permission-s{.system.api.txt}",
":framework-scheduling{.system.api.txt}",
@@ -287,6 +291,7 @@
":framework-graphics{.system.removed-api.txt}",
":framework-media{.system.removed-api.txt}",
":framework-mediaprovider{.system.removed-api.txt}",
+ ":framework-nearby{.system.removed-api.txt}",
":framework-permission{.system.removed-api.txt}",
":framework-permission-s{.system.removed-api.txt}",
":framework-scheduling{.system.removed-api.txt}",
@@ -328,6 +333,7 @@
":framework-graphics{.module-lib.api.txt}",
":framework-media{.module-lib.api.txt}",
":framework-mediaprovider{.module-lib.api.txt}",
+ ":framework-nearby{.module-lib.api.txt}",
":framework-permission{.module-lib.api.txt}",
":framework-permission-s{.module-lib.api.txt}",
":framework-scheduling{.module-lib.api.txt}",
@@ -389,6 +395,7 @@
":framework-graphics{.module-lib.removed-api.txt}",
":framework-media{.module-lib.removed-api.txt}",
":framework-mediaprovider{.module-lib.removed-api.txt}",
+ ":framework-nearby{.module-lib.removed-api.txt}",
":framework-permission{.module-lib.removed-api.txt}",
":framework-permission-s{.module-lib.removed-api.txt}",
":framework-scheduling{.module-lib.removed-api.txt}",
@@ -508,6 +515,7 @@
":framework-graphics.stubs{.jar}",
":framework-media.stubs{.jar}",
":framework-mediaprovider.stubs{.jar}",
+ ":framework-nearby.stubs{.jar}",
":framework-permission.stubs{.jar}",
":framework-permission-s.stubs{.jar}",
":framework-scheduling.stubs{.jar}",
diff --git a/boot/Android.bp b/boot/Android.bp
index 049c802..d88e839 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -107,6 +107,11 @@
apex: "com.android.wifi",
module: "com.android.wifi-bootclasspath-fragment",
},
+ // only used for auto
+ {
+ apex: "com.android.car.framework",
+ module: "com.android.car.framework-bootclasspath-fragment",
+ },
],
// Additional information needed by hidden api processing.
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index 1e72ddf..0b439df 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -545,6 +545,8 @@
mWm.setAnimationScales(oldAnims);
}
}
+ // Exit from here instead of going down the path of normal shutdown which is slow.
+ System.exit(0);
}
private static String readLogcat(long startTimeMs) {
diff --git a/core/api/current.txt b/core/api/current.txt
index a35c85f..7370af3 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -2994,6 +2994,7 @@
}
public final class AccessibilityGestureEvent implements android.os.Parcelable {
+ ctor public AccessibilityGestureEvent(int, int, @NonNull java.util.List<android.view.MotionEvent>);
method public int describeContents();
method @NonNull public static String gestureIdToString(int);
method public int getDisplayId();
@@ -3016,6 +3017,7 @@
method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
method @NonNull public final android.accessibilityservice.AccessibilityService.SoftKeyboardController getSoftKeyboardController();
method @NonNull public final java.util.List<android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction> getSystemActions();
+ method @NonNull public final android.accessibilityservice.TouchInteractionController getTouchInteractionController(int);
method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
method @NonNull public final android.util.SparseArray<java.util.List<android.view.accessibility.AccessibilityWindowInfo>> getWindowsOnAllDisplays();
method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
@@ -3250,6 +3252,31 @@
method public boolean willContinue();
}
+ public final class TouchInteractionController {
+ method public void addListener(@Nullable java.util.concurrent.Executor, @NonNull android.accessibilityservice.TouchInteractionController.Listener);
+ method public int getDisplayId();
+ method public int getMaxPointerCount();
+ method public int getState();
+ method public void performClick();
+ method public void performLongClickAndStartDrag();
+ method public void removeAllListeners();
+ method public boolean removeListener(@NonNull android.accessibilityservice.TouchInteractionController.Listener);
+ method public void requestDelegating();
+ method public void requestDragging(int);
+ method public void requestTouchExploration();
+ method @NonNull public static String stateToString(int);
+ field public static final int STATE_CLEAR = 0; // 0x0
+ field public static final int STATE_DELEGATING = 4; // 0x4
+ field public static final int STATE_DRAGGING = 3; // 0x3
+ field public static final int STATE_TOUCH_EXPLORING = 2; // 0x2
+ field public static final int STATE_TOUCH_INTERACTING = 1; // 0x1
+ }
+
+ public static interface TouchInteractionController.Listener {
+ method public void onMotionEvent(@NonNull android.view.MotionEvent);
+ method public void onStateChanged(int);
+ }
+
}
package android.accounts {
@@ -5604,6 +5631,11 @@
method @Deprecated public android.view.Window startActivity(String, android.content.Intent);
}
+ public class LocaleManager {
+ method @NonNull public android.os.LocaleList getApplicationLocales();
+ method public void setApplicationLocales(@NonNull android.os.LocaleList);
+ }
+
public class MediaRouteActionProvider extends android.view.ActionProvider {
ctor public MediaRouteActionProvider(android.content.Context);
method public android.view.View onCreateActionView();
@@ -6189,10 +6221,12 @@
method public long[] getVibrationPattern();
method public boolean hasUserSetImportance();
method public boolean hasUserSetSound();
+ method public boolean isBlockable();
method public boolean isConversation();
method public boolean isDemoted();
method public boolean isImportantConversation();
method public void setAllowBubbles(boolean);
+ method public void setBlockable(boolean);
method public void setBypassDnd(boolean);
method public void setConversationId(@NonNull String, @NonNull String);
method public void setDescription(String);
@@ -7971,6 +8005,7 @@
method public static final long getMinPeriodMillis();
method public long getMinimumNetworkChunkBytes();
method @Deprecated public int getNetworkType();
+ method public int getPriority();
method @Nullable public android.net.NetworkRequest getRequiredNetwork();
method @NonNull public android.content.ComponentName getService();
method @NonNull public android.os.Bundle getTransientExtras();
@@ -7999,6 +8034,11 @@
field public static final int NETWORK_TYPE_NONE = 0; // 0x0
field public static final int NETWORK_TYPE_NOT_ROAMING = 3; // 0x3
field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2
+ field public static final int PRIORITY_DEFAULT = 300; // 0x12c
+ field public static final int PRIORITY_HIGH = 400; // 0x190
+ field public static final int PRIORITY_LOW = 200; // 0xc8
+ field public static final int PRIORITY_MAX = 500; // 0x1f4
+ field public static final int PRIORITY_MIN = 100; // 0x64
}
public static final class JobInfo.Builder {
@@ -8018,6 +8058,7 @@
method public android.app.job.JobInfo.Builder setPeriodic(long, long);
method @RequiresPermission(android.Manifest.permission.RECEIVE_BOOT_COMPLETED) public android.app.job.JobInfo.Builder setPersisted(boolean);
method public android.app.job.JobInfo.Builder setPrefetch(boolean);
+ method @NonNull public android.app.job.JobInfo.Builder setPriority(int);
method public android.app.job.JobInfo.Builder setRequiredNetwork(@Nullable android.net.NetworkRequest);
method public android.app.job.JobInfo.Builder setRequiredNetworkType(int);
method public android.app.job.JobInfo.Builder setRequiresBatteryNotLow(boolean);
@@ -9161,8 +9202,10 @@
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean requestMtu(int);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic, boolean);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setPreferredPhy(int, int, int);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeDescriptor(android.bluetooth.BluetoothGattDescriptor);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int writeCharacteristic(@NonNull android.bluetooth.BluetoothGattCharacteristic, @NonNull byte[], int);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeDescriptor(android.bluetooth.BluetoothGattDescriptor);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int writeDescriptor(@NonNull android.bluetooth.BluetoothGattDescriptor, @NonNull byte[]);
field public static final int CONNECTION_PRIORITY_BALANCED = 0; // 0x0
field public static final int CONNECTION_PRIORITY_HIGH = 1; // 0x1
field public static final int CONNECTION_PRIORITY_LOW_POWER = 2; // 0x2
@@ -9181,11 +9224,14 @@
public abstract class BluetoothGattCallback {
ctor public BluetoothGattCallback();
- method public void onCharacteristicChanged(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic);
- method public void onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
+ method @Deprecated public void onCharacteristicChanged(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic);
+ method public void onCharacteristicChanged(@NonNull android.bluetooth.BluetoothGatt, @NonNull android.bluetooth.BluetoothGattCharacteristic, @NonNull byte[]);
+ method @Deprecated public void onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
+ method public void onCharacteristicRead(@NonNull android.bluetooth.BluetoothGatt, @NonNull android.bluetooth.BluetoothGattCharacteristic, @NonNull byte[], int);
method public void onCharacteristicWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
method public void onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int);
- method public void onDescriptorRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
+ method @Deprecated public void onDescriptorRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
+ method public void onDescriptorRead(@NonNull android.bluetooth.BluetoothGatt, @NonNull android.bluetooth.BluetoothGattDescriptor, int, @NonNull byte[]);
method public void onDescriptorWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
method public void onMtuChanged(android.bluetooth.BluetoothGatt, int, int);
method public void onPhyRead(android.bluetooth.BluetoothGatt, int, int, int);
@@ -9202,20 +9248,20 @@
method public int describeContents();
method public android.bluetooth.BluetoothGattDescriptor getDescriptor(java.util.UUID);
method public java.util.List<android.bluetooth.BluetoothGattDescriptor> getDescriptors();
- method public Float getFloatValue(int, int);
+ method @Deprecated public Float getFloatValue(int, int);
method public int getInstanceId();
- method public Integer getIntValue(int, int);
+ method @Deprecated public Integer getIntValue(int, int);
method public int getPermissions();
method public int getProperties();
method public android.bluetooth.BluetoothGattService getService();
- method public String getStringValue(int);
+ method @Deprecated public String getStringValue(int);
method public java.util.UUID getUuid();
- method public byte[] getValue();
+ method @Deprecated public byte[] getValue();
method public int getWriteType();
- method public boolean setValue(byte[]);
- method public boolean setValue(int, int, int);
- method public boolean setValue(int, int, int, int);
- method public boolean setValue(String);
+ method @Deprecated public boolean setValue(byte[]);
+ method @Deprecated public boolean setValue(int, int, int);
+ method @Deprecated public boolean setValue(int, int, int, int);
+ method @Deprecated public boolean setValue(String);
method public void setWriteType(int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothGattCharacteristic> CREATOR;
@@ -9255,8 +9301,8 @@
method public android.bluetooth.BluetoothGattCharacteristic getCharacteristic();
method public int getPermissions();
method public java.util.UUID getUuid();
- method public byte[] getValue();
- method public boolean setValue(byte[]);
+ method @Deprecated public byte[] getValue();
+ method @Deprecated public boolean setValue(byte[]);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothGattDescriptor> CREATOR;
field public static final byte[] DISABLE_NOTIFICATION_VALUE;
@@ -9544,8 +9590,12 @@
field public static final int ERROR_BLUETOOTH_NOT_ALLOWED = 2; // 0x2
field public static final int ERROR_BLUETOOTH_NOT_ENABLED = 1; // 0x1
field public static final int ERROR_DEVICE_NOT_BONDED = 3; // 0x3
- field public static final int ERROR_FEATURE_NOT_SUPPORTED = 8; // 0x8
+ field public static final int ERROR_FEATURE_NOT_SUPPORTED = 10; // 0xa
+ field public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 101; // 0x65
+ field public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 102; // 0x66
field public static final int ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION = 6; // 0x6
+ field public static final int ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION = 8; // 0x8
+ field public static final int ERROR_PROFILE_SERVICE_NOT_BOUND = 9; // 0x9
field public static final int ERROR_UNKNOWN = 2147483647; // 0x7fffffff
field public static final int SUCCESS = 0; // 0x0
}
@@ -10708,6 +10758,7 @@
field public static final String KEYGUARD_SERVICE = "keyguard";
field public static final String LAUNCHER_APPS_SERVICE = "launcherapps";
field @UiContext public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
+ field public static final String LOCALE_SERVICE = "locale";
field public static final String LOCATION_SERVICE = "location";
field public static final String MEDIA_COMMUNICATION_SERVICE = "media_communication";
field public static final String MEDIA_METRICS_SERVICE = "media_metrics";
@@ -18330,6 +18381,7 @@
field public static final int CONTROL_SCENE_MODE_THEATRE = 7; // 0x7
field public static final int CONTROL_VIDEO_STABILIZATION_MODE_OFF = 0; // 0x0
field public static final int CONTROL_VIDEO_STABILIZATION_MODE_ON = 1; // 0x1
+ field public static final int CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION = 2; // 0x2
field public static final int DISTORTION_CORRECTION_MODE_FAST = 1; // 0x1
field public static final int DISTORTION_CORRECTION_MODE_HIGH_QUALITY = 2; // 0x2
field public static final int DISTORTION_CORRECTION_MODE_OFF = 0; // 0x0
@@ -31639,6 +31691,7 @@
method @Nullable public android.os.PersistableBundle readPersistableBundle();
method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader);
method @Nullable public java.io.Serializable readSerializable();
+ method @Nullable public <T extends java.io.Serializable> T readSerializable(@Nullable ClassLoader, @NonNull Class<T>);
method @NonNull public android.util.Size readSize();
method @NonNull public android.util.SizeF readSizeF();
method @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader);
@@ -32246,6 +32299,7 @@
field public static final String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
field public static final String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
field public static final String DISALLOW_USER_SWITCH = "no_user_switch";
+ field public static final String DISALLOW_WIFI_TETHERING = "no_wifi_tethering";
field public static final String ENSURE_VERIFY_APPS = "ensure_verify_apps";
field public static final String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
field public static final int QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED = 1; // 0x1
@@ -43637,8 +43691,10 @@
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiSettingEnabled();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVtSettingEnabled();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public void registerImsStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsStateCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void registerMmTelCapabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
+ method public void unregisterImsStateCallback(@NonNull android.telephony.ims.ImsStateCallback);
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void unregisterMmTelCapabilityCallback(@NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback);
field public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; // 0x1
field public static final int WIFI_MODE_WIFI_ONLY = 0; // 0x0
@@ -43655,7 +43711,9 @@
method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @NonNull public android.telephony.ims.RcsUceAdapter getUceAdapter();
method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE", "android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE"}) public void registerImsStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsStateCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
+ method public void unregisterImsStateCallback(@NonNull android.telephony.ims.ImsStateCallback);
field public static final String ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN = "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN";
}
@@ -43854,6 +43912,19 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsRegistrationAttributes> CREATOR;
}
+ public abstract class ImsStateCallback {
+ ctor public ImsStateCallback();
+ method public abstract void onAvailable();
+ method public abstract void onError();
+ method public abstract void onUnavailable(int);
+ field public static final int REASON_IMS_SERVICE_DISCONNECTED = 3; // 0x3
+ field public static final int REASON_IMS_SERVICE_NOT_READY = 6; // 0x6
+ field public static final int REASON_NO_IMS_SERVICE_CONFIGURED = 4; // 0x4
+ field public static final int REASON_SUBSCRIPTION_INACTIVE = 5; // 0x5
+ field public static final int REASON_UNKNOWN_PERMANENT_ERROR = 2; // 0x2
+ field public static final int REASON_UNKNOWN_TEMPORARY_ERROR = 1; // 0x1
+ }
+
public class RcsUceAdapter {
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException;
}
@@ -52201,6 +52272,7 @@
method public boolean setComposingText(CharSequence, int);
method public default boolean setImeConsumesInput(boolean);
method public boolean setSelection(int, int);
+ method @Nullable public default android.view.inputmethod.TextSnapshot takeSnapshot();
field public static final int CURSOR_UPDATE_IMMEDIATE = 1; // 0x1
field public static final int CURSOR_UPDATE_MONITOR = 2; // 0x2
field public static final int GET_EXTRACTED_TEXT_MONITOR = 1; // 0x1
@@ -52413,6 +52485,16 @@
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.SurroundingText> CREATOR;
}
+ public final class TextSnapshot {
+ ctor public TextSnapshot(@NonNull android.view.inputmethod.SurroundingText, @IntRange(from=0xffffffff) int, @IntRange(from=0xffffffff) int, int);
+ method @IntRange(from=0xffffffff) public int getCompositionEnd();
+ method @IntRange(from=0xffffffff) public int getCompositionStart();
+ method public int getCursorCapsMode();
+ method @IntRange(from=0xffffffff) public int getSelectionEnd();
+ method @IntRange(from=0xffffffff) public int getSelectionStart();
+ method @NonNull public android.view.inputmethod.SurroundingText getSurroundingText();
+ }
+
}
package android.view.inspector {
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index cc30db3..1c3c7ea 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -134,6 +134,11 @@
public class AudioManager {
method public void adjustStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
method public void adjustSuggestedStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
+ method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setA2dpSuspended(boolean);
+ method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setBluetoothHeadsetProperties(@NonNull String, boolean, boolean);
+ method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setHfpEnabled(boolean);
+ method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setHfpSamplingRate(int);
+ method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setHfpVolume(int);
method public void setStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
field public static final int FLAG_FROM_KEY = 4096; // 0x1000
}
@@ -313,6 +318,10 @@
method @Nullable public android.os.IBinder getOrThrow() throws android.os.StatsServiceManager.ServiceNotFoundException;
}
+ public class SystemConfigManager {
+ method @NonNull public java.util.List<android.content.ComponentName> getEnabledComponentOverrides(@NonNull String);
+ }
+
}
package android.os.storage {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 0fb8bf4..27b2b20 100755
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -118,6 +118,7 @@
field public static final String HDMI_CEC = "android.permission.HDMI_CEC";
field @Deprecated public static final String HIDE_NON_SYSTEM_OVERLAY_WINDOWS = "android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS";
field public static final String INJECT_EVENTS = "android.permission.INJECT_EVENTS";
+ field public static final String INSTALL_DPC_PACKAGES = "android.permission.INSTALL_DPC_PACKAGES";
field public static final String INSTALL_DYNAMIC_SYSTEM = "android.permission.INSTALL_DYNAMIC_SYSTEM";
field public static final String INSTALL_GRANT_RUNTIME_PERMISSIONS = "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS";
field public static final String INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE = "android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE";
@@ -744,6 +745,11 @@
field public static final int PIN = 1; // 0x1
}
+ public class LocaleManager {
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_APP_SPECIFIC_LOCALES) public android.os.LocaleList getApplicationLocales(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void setApplicationLocales(@NonNull String, @NonNull android.os.LocaleList);
+ }
+
public class Notification implements android.os.Parcelable {
field public static final String CATEGORY_CAR_EMERGENCY = "car_emergency";
field public static final String CATEGORY_CAR_INFORMATION = "car_information";
@@ -778,7 +784,6 @@
method public int getUserLockedFields();
method public boolean isDeleted();
method public void populateFromXml(org.xmlpull.v1.XmlPullParser);
- method public void setBlockable(boolean);
method public org.json.JSONObject toJson() throws org.json.JSONException;
method public void writeXml(org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
field public static final int USER_LOCKED_SOUND = 32; // 0x20
@@ -7261,6 +7266,8 @@
method public int getHierarchy();
method public long getInnerFec();
method @NonNull public int[] getInterleaving();
+ method public int getIsdbtMode();
+ method public int getIsdbtPartialReceptionFlag();
method @IntRange(from=0, to=255) @NonNull public int[] getIsdbtSegment();
method @NonNull public boolean[] getLayerErrors();
method public int getLnbVoltage();
@@ -7299,6 +7306,8 @@
field public static final int FRONTEND_STATUS_TYPE_GUARD_INTERVAL = 26; // 0x1a
field public static final int FRONTEND_STATUS_TYPE_HIERARCHY = 19; // 0x13
field public static final int FRONTEND_STATUS_TYPE_INTERLEAVINGS = 30; // 0x1e
+ field public static final int FRONTEND_STATUS_TYPE_ISDBT_MODE = 37; // 0x25
+ field public static final int FRONTEND_STATUS_TYPE_ISDBT_PARTIAL_RECEPTION_FLAG = 38; // 0x26
field public static final int FRONTEND_STATUS_TYPE_ISDBT_SEGMENTS = 31; // 0x1f
field public static final int FRONTEND_STATUS_TYPE_IS_LINEAR = 35; // 0x23
field public static final int FRONTEND_STATUS_TYPE_IS_MISO_ENABLED = 34; // 0x22
@@ -7431,15 +7440,20 @@
method public int getGuardIntervalCapability();
method public int getModeCapability();
method public int getModulationCapability();
+ method public int getTimeInterleaveModeCapability();
+ method public boolean isFullSegmentSupported();
+ method public boolean isSegmentAutoSupported();
}
public class IsdbtFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
method @NonNull public static android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder builder();
method public int getBandwidth();
- method public int getCodeRate();
+ method @Deprecated public int getCodeRate();
method public int getGuardInterval();
+ method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.IsdbtLayerSettings[] getLayerSettings();
method public int getMode();
- method public int getModulation();
+ method @Deprecated public int getModulation();
+ method public int getPartialReceptionFlag();
method public int getServiceAreaId();
method public int getType();
field public static final int BANDWIDTH_6MHZ = 8; // 0x8
@@ -7458,20 +7472,55 @@
field public static final int MODULATION_MOD_DQPSK = 2; // 0x2
field public static final int MODULATION_MOD_QPSK = 4; // 0x4
field public static final int MODULATION_UNDEFINED = 0; // 0x0
+ field public static final int PARTIAL_RECEPTION_FLAG_FALSE = 2; // 0x2
+ field public static final int PARTIAL_RECEPTION_FLAG_TRUE = 4; // 0x4
+ field public static final int PARTIAL_RECEPTION_FLAG_UNDEFINED = 0; // 0x0
+ field public static final int TIME_INTERLEAVE_MODE_1_0 = 2; // 0x2
+ field public static final int TIME_INTERLEAVE_MODE_1_16 = 16; // 0x10
+ field public static final int TIME_INTERLEAVE_MODE_1_4 = 4; // 0x4
+ field public static final int TIME_INTERLEAVE_MODE_1_8 = 8; // 0x8
+ field public static final int TIME_INTERLEAVE_MODE_2_0 = 32; // 0x20
+ field public static final int TIME_INTERLEAVE_MODE_2_2 = 64; // 0x40
+ field public static final int TIME_INTERLEAVE_MODE_2_4 = 128; // 0x80
+ field public static final int TIME_INTERLEAVE_MODE_2_8 = 256; // 0x100
+ field public static final int TIME_INTERLEAVE_MODE_3_0 = 512; // 0x200
+ field public static final int TIME_INTERLEAVE_MODE_3_1 = 1024; // 0x400
+ field public static final int TIME_INTERLEAVE_MODE_3_2 = 2048; // 0x800
+ field public static final int TIME_INTERLEAVE_MODE_3_4 = 4096; // 0x1000
+ field public static final int TIME_INTERLEAVE_MODE_AUTO = 1; // 0x1
+ field public static final int TIME_INTERLEAVE_MODE_UNDEFINED = 0; // 0x0
}
public static class IsdbtFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setBandwidth(int);
- method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setCodeRate(int);
+ method @Deprecated @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setCodeRate(int);
method @Deprecated @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setFrequency(int);
method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setFrequencyLong(long);
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setGuardInterval(int);
+ method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setLayerSettings(@NonNull android.media.tv.tuner.frontend.IsdbtFrontendSettings.IsdbtLayerSettings[]);
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setMode(int);
- method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setModulation(int);
+ method @Deprecated @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setModulation(int);
+ method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setPartialReceptionFlag(int);
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setServiceAreaId(int);
}
+ public static final class IsdbtFrontendSettings.IsdbtLayerSettings {
+ method @NonNull public static android.media.tv.tuner.frontend.IsdbtFrontendSettings.IsdbtLayerSettings.Builder builder();
+ method public int getCodeRate();
+ method public int getModulation();
+ method @IntRange(from=0, to=255) public int getNumOfSegment();
+ method public int getTimeInterleaveMode();
+ }
+
+ public static final class IsdbtFrontendSettings.IsdbtLayerSettings.Builder {
+ method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.IsdbtLayerSettings build();
+ method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.IsdbtLayerSettings.Builder setCodeRate(int);
+ method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.IsdbtLayerSettings.Builder setModulation(int);
+ method @IntRange(from=0, to=255) @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.IsdbtLayerSettings.Builder setNumOfSegment(int);
+ method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.IsdbtLayerSettings.Builder setTimeInterleaveMode(int);
+ }
+
public interface OnTuneEventListener {
method public void onTuneEvent(int);
field public static final int SIGNAL_LOCKED = 0; // 0x0
@@ -8736,7 +8785,6 @@
public class SystemConfigManager {
method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Set<java.lang.String> getDisabledUntilUsedPreinstalledCarrierApps();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
- method @NonNull public java.util.List<java.lang.String> getEnabledComponentOverrides(@NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public int[] getSystemPermissionUids(@NonNull String);
}
@@ -13910,7 +13958,9 @@
method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void createSipDelegate(@NonNull android.telephony.ims.DelegateRequest, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.DelegateConnectionStateCallback, @NonNull android.telephony.ims.stub.DelegateConnectionMessageCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void destroySipDelegate(@NonNull android.telephony.ims.SipDelegateConnection, int);
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public boolean isSupported() throws android.telephony.ims.ImsException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public void registerImsStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsStateCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void triggerFullNetworkRegistration(@NonNull android.telephony.ims.SipDelegateConnection, @IntRange(from=100, to=699) int, @Nullable String);
+ method public void unregisterImsStateCallback(@NonNull android.telephony.ims.ImsStateCallback);
field public static final int DENIED_REASON_INVALID = 4; // 0x4
field public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1; // 0x1
field public static final int DENIED_REASON_NOT_ALLOWED = 2; // 0x2
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 72e93f8..da9fd3e 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -114,6 +114,7 @@
method @RequiresPermission(android.Manifest.permission.RESET_APP_ERRORS) public void resetAppErrors();
method public static void resumeAppSwitches() throws android.os.RemoteException;
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setStopBackgroundUsersOnSwitch(int);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean stopUser(int, boolean);
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.DUMP) public void waitForBroadcastIdle();
@@ -128,6 +129,9 @@
field public static final int PROCESS_CAPABILITY_NONE = 0; // 0x0
field public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4; // 0x4
field public static final int PROCESS_STATE_TOP = 2; // 0x2
+ field public static final int STOP_BG_USERS_ON_SWITCH_DEFAULT = -1; // 0xffffffff
+ field public static final int STOP_BG_USERS_ON_SWITCH_FALSE = 0; // 0x0
+ field public static final int STOP_BG_USERS_ON_SWITCH_TRUE = 1; // 0x1
}
public static class ActivityManager.RunningAppProcessInfo implements android.os.Parcelable {
@@ -257,7 +261,8 @@
public class DreamManager {
method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isDreaming();
- method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void setActiveDream(@NonNull android.content.ComponentName);
+ method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void setActiveDream(@Nullable android.content.ComponentName);
+ method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void setDreamOverlay(@Nullable android.content.ComponentName);
method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void startDream(@NonNull android.content.ComponentName);
method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void stopDream();
}
@@ -276,13 +281,17 @@
method @RequiresPermission(anyOf={android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS, "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"}) public boolean setLock(int, @Nullable byte[], int, @Nullable byte[]);
}
+ public class LocaleManager {
+ method @Nullable public android.os.LocaleList getSystemLocales();
+ method public void setSystemLocales(@NonNull android.os.LocaleList);
+ }
+
public class Notification implements android.os.Parcelable {
method public boolean shouldShowForegroundImmediately();
}
public final class NotificationChannel implements android.os.Parcelable {
method public int getOriginalImportance();
- method public boolean isBlockable();
method public boolean isImportanceLockedByCriticalDeviceFunction();
method public boolean isImportanceLockedByOEM();
method public void lockFields(int);
@@ -2365,6 +2374,17 @@
}
+package android.service.dreams {
+
+ public abstract class DreamOverlayService extends android.app.Service {
+ ctor public DreamOverlayService();
+ method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method public abstract void onStartDream(@NonNull android.view.WindowManager.LayoutParams);
+ method public final void requestExit();
+ }
+
+}
+
package android.service.notification {
@Deprecated public abstract class ConditionProviderService extends android.app.Service {
@@ -2812,11 +2832,9 @@
public final class SurfaceControl implements android.os.Parcelable {
ctor public SurfaceControl(@NonNull android.view.SurfaceControl, @NonNull String);
- method public static long acquireFrameRateFlexibilityToken();
method @NonNull public static android.os.IBinder getInternalDisplayToken();
method public boolean isSameSurface(@NonNull android.view.SurfaceControl);
method public static void overrideHdrTypes(@NonNull android.os.IBinder, @NonNull int[]);
- method public static void releaseFrameRateFlexibilityToken(long);
}
public class SurfaceControlViewHost {
diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
index 768ec38..3c9b232 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
@@ -150,7 +150,12 @@
private final int mDisplayId;
private List<MotionEvent> mMotionEvents = new ArrayList<>();
- /** @hide */
+/**
+ * Constructs an AccessibilityGestureEvent to be dispatched to an accessibility service.
+ * @param gestureId the id number of the gesture.
+ * @param displayId the display on which this gesture was performed.
+ * @param motionEvents the motion events that lead to this gesture.
+ */
public AccessibilityGestureEvent(
int gestureId, int displayId, @NonNull List<MotionEvent> motionEvents) {
mGestureId = gestureId;
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 61d2b4b..0f852b4 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -53,6 +53,7 @@
import android.util.SparseArray;
import android.view.Display;
import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
@@ -65,6 +66,7 @@
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.pooled.PooledLambda;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -582,6 +584,10 @@
/** Magnification changed callbacks for different displays */
void onMagnificationChanged(int displayId, @NonNull Region region,
float scale, float centerX, float centerY);
+ /** Callbacks for receiving motion events. */
+ void onMotionEvent(MotionEvent event);
+ /** Callback for tuch state changes. */
+ void onTouchStateChanged(int displayId, int state);
void onSoftKeyboardShowModeChanged(int showMode);
void onPerformGestureResult(int sequence, boolean completedSuccessfully);
void onFingerprintCapturingGesturesChanged(boolean active);
@@ -720,6 +726,12 @@
/** List of magnification controllers, mapping from displayId -> MagnificationController. */
private final SparseArray<MagnificationController> mMagnificationControllers =
new SparseArray<>(0);
+ /**
+ * List of touch interaction controllers, mapping from displayId -> TouchInteractionController.
+ */
+ private final SparseArray<TouchInteractionController> mTouchInteractionControllers =
+ new SparseArray<>(0);
+
private SoftKeyboardController mSoftKeyboardController;
private final SparseArray<AccessibilityButtonController> mAccessibilityButtonControllers =
new SparseArray<>(0);
@@ -1194,6 +1206,10 @@
getFingerprintGestureController().onGesture(gesture);
}
+ int getConnectionId() {
+ return mConnectionId;
+ }
+
/**
* Used to control and query the state of display magnification.
*/
@@ -2210,6 +2226,16 @@
}
@Override
+ public void onMotionEvent(MotionEvent event) {
+ AccessibilityService.this.onMotionEvent(event);
+ }
+
+ @Override
+ public void onTouchStateChanged(int displayId, int state) {
+ AccessibilityService.this.onTouchStateChanged(displayId, state);
+ }
+
+ @Override
public void onSoftKeyboardShowModeChanged(int showMode) {
AccessibilityService.this.onSoftKeyboardShowModeChanged(showMode);
}
@@ -2372,6 +2398,21 @@
}
@Override
+ public void onMotionEvent(MotionEvent event) {
+ final Message message = PooledLambda.obtainMessage(
+ Callbacks::onMotionEvent, mCallback, event);
+ mCaller.sendMessage(message);
+ }
+
+ @Override
+ public void onTouchStateChanged(int displayId, int state) {
+ final Message message = PooledLambda.obtainMessage(Callbacks::onTouchStateChanged,
+ mCallback,
+ displayId, state);
+ mCaller.sendMessage(message);
+ }
+
+ @Override
public void executeMessage(Message message) {
switch (message.what) {
case DO_ON_ACCESSIBILITY_EVENT: {
@@ -2523,7 +2564,7 @@
}
return;
}
- default :
+ default:
Log.w(LOG_TAG, "Unknown message type " + message.what);
}
}
@@ -2748,4 +2789,47 @@
}
}
}
+
+ /**
+ * Returns the touch interaction controller for the specified logical display, which may be used
+ * to detect gestures and otherwise control touch interactions. If
+ * {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is disabled the
+ * controller's methods will have no effect.
+ *
+ * @param displayId The logical display id, use {@link Display#DEFAULT_DISPLAY} for default
+ * display.
+ * @return the TouchExploration controller
+ */
+ @NonNull
+ public final TouchInteractionController getTouchInteractionController(int displayId) {
+ synchronized (mLock) {
+ TouchInteractionController controller = mTouchInteractionControllers.get(displayId);
+ if (controller == null) {
+ controller = new TouchInteractionController(this, mLock, displayId);
+ mTouchInteractionControllers.put(displayId, controller);
+ }
+ return controller;
+ }
+ }
+
+ void onMotionEvent(MotionEvent event) {
+ TouchInteractionController controller;
+ synchronized (mLock) {
+ int displayId = event.getDisplayId();
+ controller = mTouchInteractionControllers.get(displayId);
+ }
+ if (controller != null) {
+ controller.onMotionEvent(event);
+ }
+ }
+
+ void onTouchStateChanged(int displayId, int state) {
+ TouchInteractionController controller;
+ synchronized (mLock) {
+ controller = mTouchInteractionControllers.get(displayId);
+ }
+ if (controller != null) {
+ controller.onStateChanged(state);
+ }
+ }
}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index 58b0d19..651c50f 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -22,6 +22,7 @@
import android.view.accessibility.AccessibilityWindowInfo;
import android.accessibilityservice.AccessibilityGestureEvent;
import android.view.KeyEvent;
+import android.view.MotionEvent;
/**
* Top-level interface to an accessibility service component.
@@ -44,6 +45,10 @@
void onMagnificationChanged(int displayId, in Region region, float scale, float centerX, float centerY);
+ void onMotionEvent(in MotionEvent event);
+
+ void onTouchStateChanged(int displayId, int state);
+
void onSoftKeyboardShowModeChanged(int showMode);
void onPerformGestureResult(int sequence, boolean completedSuccessfully);
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 6c360e5..81457eb 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -122,4 +122,16 @@
oneway void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
int processId, long threadId, int callingUid, in Bundle serializedCallingStackInBundle);
+
+ void setServiceDetectsGesturesEnabled(int displayId, boolean mode);
+
+ void requestTouchExploration(int displayId);
+
+ void requestDragging(int displayId, int pointerId);
+
+ void requestDelegating(int displayId);
+
+ void onDoubleTap(int displayId);
+
+ void onDoubleTapAndHold(int displayId);
}
diff --git a/core/java/android/accessibilityservice/TouchInteractionController.java b/core/java/android/accessibilityservice/TouchInteractionController.java
new file mode 100644
index 0000000..d9be49a
--- /dev/null
+++ b/core/java/android/accessibilityservice/TouchInteractionController.java
@@ -0,0 +1,425 @@
+/*
+ * 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.accessibilityservice;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.view.MotionEvent;
+import android.view.accessibility.AccessibilityInteractionClient;
+
+import java.util.concurrent.Executor;
+
+/**
+ * This class allows a service to handle touch exploration and the detection of specialized
+ * accessibility gestures. The service receives motion events and can match those motion events
+ * against the gestures it supports. The service can also request the framework enter three other
+ * states of operation for the duration of this interaction. Upon entering any of these states the
+ * framework will take over and the service will not receive motion events until the start of a new
+ * interaction. The states are as follows:
+ *
+ * <ul>
+ * <li>The service can tell the framework that this interaction is touch exploration. The user is
+ * trying to explore the screen rather than manipulate it. The framework will then convert the
+ * motion events to hover events to support touch exploration.
+ * <li>The service can tell the framework that this interaction is a dragging interaction where
+ * two fingers are used to execute a one-finger gesture such as scrolling the screen. The
+ * service must specify which of the two fingers should be passed through to rest of the input
+ * pipeline.
+ * <li>Finally, the service can request that the framework delegate this interaction, meaning pass
+ * it through to the rest of the input pipeline as-is.
+ * </ul>
+ *
+ * When {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE } is enabled, this
+ * controller will receive all motion events received by the framework for the specified display
+ * when not touch-exploring or delegating. If the service classifies this interaction as touch
+ * exploration or delegating the framework will stop sending motion events to the service for the
+ * duration of this interaction. If the service classifies this interaction as a dragging
+ * interaction the framework will send motion events to the service to allow the service to
+ * determine if the interaction still qualifies as dragging or if it has become a delegating
+ * interaction. If {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE } is disabled
+ * this controller will not receive any motion events because touch interactions are being passed
+ * through to the input pipeline unaltered.
+ * Note that {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE }
+ * requires setting {@link android.R.attr#canRequestTouchExplorationMode} as well.
+ */
+public final class TouchInteractionController {
+ /** The state where the user is not touching the screen. */
+ public static final int STATE_CLEAR = 0;
+ /**
+ * The state where the user is touching the screen and the service is receiving motion events.
+ */
+ public static final int STATE_TOUCH_INTERACTING = 1;
+ /**
+ * The state where the user is explicitly exploring the screen. The service is not receiving
+ * motion events.
+ */
+ public static final int STATE_TOUCH_EXPLORING = 2;
+ /**
+ * The state where the user is dragging with two fingers. The service is not receiving motion
+ * events. The selected finger is being dispatched to the rest of the input pipeline to execute
+ * the drag.
+ */
+ public static final int STATE_DRAGGING = 3;
+ /**
+ * The user is performing a gesture which is being passed through to the input pipeline as-is.
+ * The service is not receiving motion events.
+ */
+ public static final int STATE_DELEGATING = 4;
+
+ @IntDef({
+ STATE_CLEAR,
+ STATE_TOUCH_INTERACTING,
+ STATE_TOUCH_EXPLORING,
+ STATE_DRAGGING,
+ STATE_DELEGATING
+ })
+ private @interface State {}
+
+ // The maximum number of pointers that can be touching the screen at once. (See MAX_POINTER_ID
+ // in frameworks/native/include/input/Input.h)
+ private static final int MAX_POINTER_COUNT = 32;
+
+ private final AccessibilityService mService;
+ private final Object mLock;
+ private final int mDisplayId;
+ private boolean mServiceDetectsGestures;
+ /** Map of listeners to executors. Lazily created when adding the first listener. */
+ private ArrayMap<Listener, Executor> mListeners;
+
+ // The current state of the display.
+ private int mState = STATE_CLEAR;
+
+ TouchInteractionController(
+ @NonNull AccessibilityService service, @NonNull Object lock, int displayId) {
+ mDisplayId = displayId;
+ mLock = lock;
+ mService = service;
+ }
+
+ /**
+ * Adds the specified change listener to the list of motion event listeners. The callback will
+ * run using on the specified {@link Executor}', or on the service's main thread if the
+ * Executor is {@code null}.
+ * @param listener the listener to add, must be non-null
+ * @param executor the executor for this callback, or {@code null} to execute on the service's
+ * main thread
+ */
+ public void addListener(@Nullable Executor executor, @NonNull Listener listener) {
+ synchronized (mLock) {
+ if (mListeners == null) {
+ mListeners = new ArrayMap<>();
+ }
+ mListeners.put(listener, executor);
+ if (mListeners.size() == 1) {
+ setServiceDetectsGestures(true);
+ }
+ }
+ }
+
+ /**
+ * Removes the specified listener from the list of motion event listeners.
+ *
+ * @param listener the listener to remove, must be non-null
+ * @return {@code true} if the listener was removed, {@code false} otherwise
+ */
+ public boolean removeListener(@NonNull Listener listener) {
+ if (mListeners == null) {
+ return false;
+ }
+ synchronized (mLock) {
+ boolean result = mListeners.remove(listener) != null;
+ if (result && mListeners.size() == 0) {
+ setServiceDetectsGestures(false);
+ }
+ return result;
+ }
+ }
+
+ /**
+ * Removes all listeners and returns control of touch interactions to the framework.
+ */
+ public void removeAllListeners() {
+ if (mListeners != null) {
+ synchronized (mLock) {
+ mListeners.clear();
+ setServiceDetectsGestures(false);
+ }
+ }
+ }
+
+ /**
+ * Dispatches motion events to any registered listeners. This should be called on the service's
+ * main thread.
+ */
+ void onMotionEvent(MotionEvent event) {
+ final ArrayMap<Listener, Executor> entries;
+ synchronized (mLock) {
+ // Listeners may remove themselves. Perform a shallow copy to avoid concurrent
+ // modification.
+ entries = new ArrayMap<>(mListeners);
+ }
+ for (int i = 0, count = entries.size(); i < count; i++) {
+ final Listener listener = entries.keyAt(i);
+ final Executor executor = entries.valueAt(i);
+ if (executor != null) {
+ executor.execute(() -> listener.onMotionEvent(event));
+ } else {
+ // We're already on the main thread, just run the listener.
+ listener.onMotionEvent(event);
+ }
+ }
+ }
+
+ /**
+ * Dispatches motion events to any registered listeners. This should be called on the service's
+ * main thread.
+ */
+ void onStateChanged(@State int state) {
+ mState = state;
+ final ArrayMap<Listener, Executor> entries;
+ synchronized (mLock) {
+ // Listeners may remove themselves. Perform a shallow copy to avoid concurrent
+ // modification.
+ entries = new ArrayMap<>(mListeners);
+ }
+ for (int i = 0, count = entries.size(); i < count; i++) {
+ final Listener listener = entries.keyAt(i);
+ final Executor executor = entries.valueAt(i);
+ if (executor != null) {
+ executor.execute(() -> listener.onStateChanged(state));
+ } else {
+ // We're already on the main thread, just run the listener.
+ listener.onStateChanged(state);
+ }
+ }
+ }
+
+ /**
+ * When {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled, this
+ * controller will receive all motion events received by the framework for the specified display
+ * when not touch-exploring, delegating, or dragging. This allows the service to detect its own
+ * gestures, and use its own logic to judge when the framework should start touch-exploring,
+ * delegating, or dragging. If {@link
+ * AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE } is disabled this flag has no
+ * effect.
+ *
+ * @see AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE
+ */
+ private void setServiceDetectsGestures(boolean mode) {
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance()
+ .getConnection(mService.getConnectionId());
+ if (connection != null) {
+ try {
+ connection.setServiceDetectsGesturesEnabled(mDisplayId, mode);
+ mServiceDetectsGestures = mode;
+ } catch (RemoteException re) {
+ throw new RuntimeException(re);
+ }
+ }
+ }
+
+ /**
+ * If {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled and at
+ * least one listener has been added for this display this function tells the framework to
+ * initiate touch exploration. Touch exploration will continue for the duration of this
+ * interaction.
+ */
+ public void requestTouchExploration() {
+ checkState();
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance()
+ .getConnection(mService.getConnectionId());
+ if (connection != null) {
+ try {
+ connection.requestTouchExploration(mDisplayId);
+ } catch (RemoteException re) {
+ throw new RuntimeException(re);
+ }
+ }
+ }
+
+ /**
+ * If {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} and {@link If
+ * {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled and at least
+ * one listener has been added, this function tells the framework to initiate a dragging
+ * interaction using the specified pointer. The pointer's movements will be passed through to
+ * the rest of the input pipeline. Dragging is often used to perform two-finger scrolling.
+ *
+ * @param pointerId the pointer to be passed through to the rest of the input pipeline. If the
+ * pointer id is valid but not actually present on the screen it will be ignored.
+ * @throws IllegalArgumentException if the pointer id is outside of the allowed range.
+ */
+ public void requestDragging(int pointerId) {
+ checkState();
+ if (pointerId < 0 || pointerId > MAX_POINTER_COUNT) {
+ throw new IllegalArgumentException("Invalid pointer id: " + pointerId);
+ }
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance()
+ .getConnection(mService.getConnectionId());
+ if (connection != null) {
+ try {
+ connection.requestDragging(mDisplayId, pointerId);
+ } catch (RemoteException re) {
+ throw new RuntimeException(re);
+ }
+ }
+ }
+
+ /**
+ * If {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} and {@link If
+ * {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled and at least
+ * one listener has been added, this function tells the framework to initiate a delegating
+ * interaction. Motion events will be passed through as-is to the rest of the input pipeline for
+ * the duration of this interaction.
+ */
+ public void requestDelegating() {
+ checkState();
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance()
+ .getConnection(mService.getConnectionId());
+ if (connection != null) {
+ try {
+ connection.requestDelegating(mDisplayId);
+ } catch (RemoteException re) {
+ throw new RuntimeException(re);
+ }
+ }
+ }
+
+ /**
+ * If {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} and {@link If
+ * {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled and at least
+ * one listener has been added, this function tells the framework to perform a click.
+ * The framework will first try to perform
+ * {@link AccessibilityNodeInfo.AccessibilityAction#ACTION_CLICK} on the item with
+ * accessibility focus. If that fails, the framework will simulate a click using motion events
+ * on the last location to have accessibility focus.
+ */
+ public void performClick() {
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance()
+ .getConnection(mService.getConnectionId());
+ if (connection != null) {
+ try {
+ connection.onDoubleTap(mDisplayId);
+ } catch (RemoteException re) {
+ throw new RuntimeException(re);
+ }
+ }
+ }
+
+ /**
+ * If {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} and {@link If
+ * {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled and at least
+ * one listener has been added, this function tells the framework to perform a long click.
+ * The framework will simulate a long click using motion events on the last location with
+ * accessibility focus and will delegate any movements to the rest of the input pipeline. This
+ * allows a user to double-tap and hold to trigger a drag and then execute that drag by moving
+ * their finger.
+ */
+ public void performLongClickAndStartDrag() {
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance()
+ .getConnection(mService.getConnectionId());
+ if (connection != null) {
+ try {
+ connection.onDoubleTapAndHold(mDisplayId);
+ } catch (RemoteException re) {
+ throw new RuntimeException(re);
+ }
+ }
+ }
+
+ private void checkState() {
+ if (!mServiceDetectsGestures || mListeners.size() == 0) {
+ throw new IllegalStateException(
+ "State transitions are not allowed without first adding a listener.");
+ }
+ if (mState != STATE_TOUCH_INTERACTING) {
+ throw new IllegalStateException(
+ "State transitions are not allowed in " + stateToString(mState));
+ }
+ }
+
+ /** @return the maximum number of pointers that this display will accept. */
+ public int getMaxPointerCount() {
+ return MAX_POINTER_COUNT;
+ }
+
+ /** @return the display id associated with this controller. */
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
+ /**
+ * @return the current state of this controller.
+ * @see TouchInteractionController#STATE_CLEAR
+ * @see TouchInteractionController#STATE_DELEGATING
+ * @see TouchInteractionController#STATE_DRAGGING
+ * @see TouchInteractionController#STATE_TOUCH_EXPLORING
+ */
+ public int getState() {
+ synchronized (mLock) {
+ return mState;
+ }
+ }
+
+ /** Returns a string representation of the specified state. */
+ @NonNull
+ public static String stateToString(int state) {
+ switch (state) {
+ case STATE_CLEAR:
+ return "STATE_CLEAR";
+ case STATE_TOUCH_INTERACTING:
+ return "STATE_TOUCH_INTERACTING";
+ case STATE_TOUCH_EXPLORING:
+ return "STATE_TOUCH_EXPLORING";
+ case STATE_DRAGGING:
+ return "STATE_DRAGGING";
+ case STATE_DELEGATING:
+ return "STATE_DELEGATING";
+ default:
+ return "Unknown state: " + state;
+ }
+ }
+
+ /** Listeners allow services to receive motion events and state change updates. */
+ public interface Listener {
+ /**
+ * Called when the framework has sent a motion event to the service.
+ *
+ * @param event the event being passed to the service.
+ */
+ void onMotionEvent(@NonNull MotionEvent event);
+
+ /**
+ * Called when the state of motion event dispatch for this display has changed.
+ *
+ * @param state the new state of motion event dispatch.
+ * @see TouchInteractionController#STATE_CLEAR
+ * @see TouchInteractionController#STATE_DELEGATING
+ * @see TouchInteractionController#STATE_DRAGGING
+ * @see TouchInteractionController#STATE_TOUCH_EXPLORING
+ */
+ void onStateChanged(@State int state);
+ }
+}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 365493de..d77227b 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4082,6 +4082,56 @@
}
/**
+ * Uses the value defined by the platform.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final int STOP_BG_USERS_ON_SWITCH_DEFAULT = -1;
+
+ /**
+ * Overrides value defined by the platform and stop background users on switch.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final int STOP_BG_USERS_ON_SWITCH_TRUE = 1;
+
+ /**
+ * Overrides value defined by the platform and don't stop background users on switch.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final int STOP_BG_USERS_ON_SWITCH_FALSE = 0;
+
+ /** @hide */
+ @IntDef(prefix = { "STOP_BG_USERS_ON_SWITCH_" }, value = {
+ STOP_BG_USERS_ON_SWITCH_DEFAULT,
+ STOP_BG_USERS_ON_SWITCH_TRUE,
+ STOP_BG_USERS_ON_SWITCH_FALSE
+ })
+ public @interface StopBgUsersOnSwitch {}
+
+ /**
+ * Sets whether background users should be stopped when the current user is switched.
+ *
+ * <p>Should only be used on tests.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})
+ public void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value) {
+ try {
+ getService().setStopBackgroundUsersOnSwitch(value);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Starts a profile.
* To be used with non-managed profiles, managed profiles should use
* {@link UserManager#requestQuietModeEnabled}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 0881db0..7dbcd5f 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -16,6 +16,8 @@
package android.app;
+import static android.app.ActivityManager.StopBgUsersOnSwitch;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -686,6 +688,11 @@
@Nullable VoiceInteractionManagerProvider provider);
/**
+ * Sets whether background users should be stopped when the current user is switched.
+ */
+ public abstract void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value);
+
+ /**
* Provides the interface to communicate between voice interaction manager service and
* ActivityManagerService.
*/
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 6011cc2..1943f9d 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -79,7 +79,7 @@
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
-import android.content.pm.pkg.PackageUserState;
+import android.content.pm.pkg.FrameworkPackageUserState;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
@@ -2130,7 +2130,7 @@
return null;
}
return PackageInfoWithoutStateUtils.generate(result.getResult(), null, flags, 0, 0, null,
- PackageUserState.DEFAULT, UserHandle.getCallingUserId());
+ FrameworkPackageUserState.DEFAULT, UserHandle.getCallingUserId());
}
@Override
diff --git a/core/java/android/app/DreamManager.java b/core/java/android/app/DreamManager.java
index f236813..34ae08fd 100644
--- a/core/java/android/app/DreamManager.java
+++ b/core/java/android/app/DreamManager.java
@@ -17,6 +17,7 @@
package android.app;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -91,10 +92,30 @@
@TestApi
@UserHandleAware
@RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)
- public void setActiveDream(@NonNull ComponentName dreamComponent) {
+ public void setActiveDream(@Nullable ComponentName dreamComponent) {
ComponentName[] dreams = {dreamComponent};
+
try {
- mService.setDreamComponentsForUser(mContext.getUserId(), dreams);
+ mService.setDreamComponentsForUser(mContext.getUserId(),
+ dreamComponent != null ? dreams : null);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets the active dream on the device to be "dreamComponent".
+ *
+ * <p>This is only used for testing the dream service APIs.
+ *
+ * @hide
+ */
+ @TestApi
+ @UserHandleAware
+ @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)
+ public void setDreamOverlay(@Nullable ComponentName dreamOverlayComponent) {
+ try {
+ mService.registerDreamOverlayService(dreamOverlayComponent);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 658c130..de79a64 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -349,6 +349,7 @@
@UnsupportedAppUsage
boolean switchUser(int userid);
@UnsupportedAppUsage
+ void setStopBackgroundUsersOnSwitch(int value);
boolean removeTask(int taskId);
@UnsupportedAppUsage
void registerProcessObserver(in IProcessObserver observer);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 1312454..fdcf99d 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -118,6 +118,7 @@
ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int userId);
boolean isPackagePaused(String pkg);
void deleteNotificationHistoryItem(String pkg, int uid, long postedTime);
+ boolean isPermissionFixed(String pkg, int userId);
void silenceNotificationSound();
diff --git a/core/java/android/app/LocaleManager.java b/core/java/android/app/LocaleManager.java
new file mode 100644
index 0000000..2aa7c8e
--- /dev/null
+++ b/core/java/android/app/LocaleManager.java
@@ -0,0 +1,152 @@
+/*
+ * 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.app;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.annotation.UserHandleAware;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.LocaleList;
+import android.os.RemoteException;
+
+/**
+ * This class gives access to system locale services. These services allow applications to control
+ * granular locale settings (such as per-app locales).
+ *
+ * <p> Third party applications should treat this as a write-side surface, and continue reading
+ * locales via their in-process {@link LocaleList}s.
+ */
+@SystemService(Context.LOCALE_SERVICE)
+public class LocaleManager {
+ private static final String TAG = "LocaleManager";
+
+ /** Context required for getting the user for which API calls are made. */
+ private Context mContext;
+ private ILocaleManager mService;
+
+ /** @hide Instantiated by ContextImpl */
+ public LocaleManager(Context context, ILocaleManager service) {
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Sets the UI locales for the calling app.
+ *
+ * <p>Pass a {@link LocaleList#getEmptyLocaleList()} to reset to the system locale.
+ *
+ * @param locales the desired locales for the calling app.
+ */
+ @UserHandleAware
+ public void setApplicationLocales(@NonNull LocaleList locales) {
+ setApplicationLocales(mContext.getPackageName(), locales);
+ }
+
+ /**
+ * Sets the UI locales for a specified app (described by package name).
+ *
+ * <p>Pass a {@link LocaleList#getEmptyLocaleList()} to reset to the system locale.
+ *
+ * @param appPackageName the package name of the app for which to set the locales.
+ * @param locales the desired locales for the specified app.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.CHANGE_CONFIGURATION)
+ @UserHandleAware
+ public void setApplicationLocales(@NonNull String appPackageName, @NonNull LocaleList locales) {
+ try {
+ mService.setApplicationLocales(appPackageName, mContext.getUser().getIdentifier(),
+ locales);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the UI locales for the calling app.
+ *
+ * <p>Returns a {@link LocaleList#getEmptyLocaleList()} if no app-specific locales are set.
+ */
+ @UserHandleAware
+ @NonNull
+ public LocaleList getApplicationLocales() {
+ return getApplicationLocales(mContext.getPackageName());
+ }
+
+ /**
+ * Returns the current UI locales for a specific app (described by package name).
+ *
+ * <p>Returns a {@link LocaleList#getEmptyLocaleList()} if no app-specific locales are set.
+ *
+ * <b>Note:</b> Non-system apps should read Locale information via their in-process
+ * LocaleLists.
+ *
+ * @param appPackageName the package name of the app for which to retrieve the locales.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_APP_SPECIFIC_LOCALES)
+ @UserHandleAware
+ @NonNull
+ public LocaleList getApplicationLocales(@NonNull String appPackageName) {
+ try {
+ return mService.getApplicationLocales(appPackageName, mContext.getUser()
+ .getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets the current system locales to the provided value.
+ *
+ * @hide
+ */
+ @TestApi
+ public void setSystemLocales(@NonNull LocaleList locales) {
+ try {
+ Configuration conf = ActivityManager.getService().getConfiguration();
+ conf.setLocales(locales);
+ ActivityManager.getService().updatePersistentConfiguration(conf);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the current system locales for the device.
+ *
+ * @hide
+ */
+ @TestApi
+ @Nullable
+ public LocaleList getSystemLocales() {
+ try {
+ return ActivityManager.getService().getConfiguration().getLocales();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index cf54ecf..6bfde9a 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -7915,7 +7915,7 @@
* Adds a message for display by this notification. Convenience call for a simple
* {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
* @param text A {@link CharSequence} to be displayed as the message content
- * @param timestamp Time at which the message arrived
+ * @param timestamp Time in milliseconds at which the message arrived
* @param sender A {@link CharSequence} to be used for displaying the name of the
* sender. Should be <code>null</code> for messages by the current user, in which case
* the platform will insert {@link #getUserDisplayName()}.
@@ -7937,7 +7937,7 @@
* Adds a message for display by this notification. Convenience call for a simple
* {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
* @param text A {@link CharSequence} to be displayed as the message content
- * @param timestamp Time at which the message arrived
+ * @param timestamp Time in milliseconds at which the message arrived
* @param sender The {@link Person} who sent the message.
* Should be <code>null</code> for messages by the current user, in which case
* the platform will insert the user set in {@code MessagingStyle(Person)}.
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 6553b61..91ab19b 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -440,15 +440,13 @@
/**
* Allows users to block notifications sent through this channel, if this channel belongs to
- * a package that is signed with the system signature.
+ * a package that otherwise would have notifications "fixed" as enabled.
*
- * If the channel does not belong to a package that is signed with the system signature, this
+ * If the channel does not belong to a package that has a fixed notification permission, this
* method does nothing, since such channels are blockable by default and cannot be set to be
* unblockable.
* @param blockable if {@code true}, allows users to block notifications on this channel.
- * @hide
*/
- @SystemApi
public void setBlockable(boolean blockable) {
mBlockableSystem = blockable;
}
@@ -842,9 +840,9 @@
}
/**
- * @hide
+ * Returns whether this channel is always blockable, even if the app is 'fixed' as
+ * non-blockable.
*/
- @TestApi
public boolean isBlockable() {
return mBlockableSystem;
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 9be4adc..717e289 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -2579,6 +2579,10 @@
* for more information.
* </p>
* <p>
+ * Callers of this method must have notification listener access, permission to read contacts,
+ * or have system permissions.
+ * </p>
+ * <p>
* NOTE: This method calls into Contacts, which may take some time, and should not be called
* on the main thread.
* </p>
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index 4da51c1..1909f3c 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -40,6 +40,9 @@
# AppOps
per-file *AppOp* = file:/core/java/android/permission/OWNERS
+# LocaleManager
+per-file *Locale* = file:/services/core/java/com/android/server/locales/OWNERS
+
# Multiuser
per-file *User* = file:/MULTIUSER_OWNERS
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 6ba185f..fc40179 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1393,6 +1393,14 @@
throws ServiceNotFoundException {
return new SystemLightsManager(ctx);
}});
+ registerService(Context.LOCALE_SERVICE, LocaleManager.class,
+ new CachedServiceFetcher<LocaleManager>() {
+ @Override
+ public LocaleManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ return new LocaleManager(ctx, ILocaleManager.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.LOCALE_SERVICE)));
+ }});
registerService(Context.INCREMENTAL_SERVICE, IncrementalManager.class,
new CachedServiceFetcher<IncrementalManager>() {
@Override
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index bd9b6e9..ddde272 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -368,7 +368,8 @@
&& Objects.equals(taskDescription, that.taskDescription)
&& isFocused == that.isFocused
&& isVisible == that.isVisible
- && isSleeping == that.isSleeping;
+ && isSleeping == that.isSleeping
+ && Objects.equals(mTopActivityLocusId, that.mTopActivityLocusId);
}
/**
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 65f71d0..828b171 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -49,6 +49,7 @@
import android.view.Display;
import android.view.InputEvent;
import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.View;
@@ -1542,6 +1543,15 @@
return false;
}
+ public void onMotionEvent(MotionEvent event) {
+ /* do nothing */
+ }
+
+ @Override
+ public void onTouchStateChanged(int displayId, int state) {
+ /* do nothing */
+ }
+
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
final OnAccessibilityEventListener listener;
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index b3fad21..737360d 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -587,12 +587,12 @@
Rect dimensions = null;
synchronized (this) {
+ ParcelFileDescriptor pfd = null;
try {
Bundle params = new Bundle();
+ pfd = mService.getWallpaperWithFeature(context.getOpPackageName(),
+ context.getAttributionTag(), this, FLAG_SYSTEM, params, userId);
// Let's peek user wallpaper first.
- ParcelFileDescriptor pfd = mService.getWallpaperWithFeature(
- context.getOpPackageName(), context.getAttributionTag(), this,
- FLAG_SYSTEM, params, userId);
if (pfd != null) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
@@ -601,6 +601,13 @@
}
} catch (RemoteException ex) {
Log.w(TAG, "peek wallpaper dimensions failed", ex);
+ } finally {
+ if (pfd != null) {
+ try {
+ pfd.close();
+ } catch (IOException ignored) {
+ }
+ }
}
}
// If user wallpaper is unavailable, may be the default one instead.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 84bb9c6..40880d4 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3452,6 +3452,9 @@
* set on the primary {@link DevicePolicyManager} must be cleared first by calling
* {@link #setRequiredPasswordComplexity} with {@link #PASSWORD_COMPLEXITY_NONE) first.
*
+ * <p><string>Note:</strong> this method is ignored on
+ * {PackageManager#FEATURE_AUTOMOTIVE automotive builds}.
+ *
* @deprecated Prefer using {@link #setRequiredPasswordComplexity(int)}, to require a password
* that satisfies a complexity level defined by the platform, rather than specifying custom
* password requirement.
@@ -3542,12 +3545,14 @@
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
* not, a security exception will be thrown.
* <p>
- *
* Apps targeting {@link android.os.Build.VERSION_CODES#R} and below can call this method on the
* {@link DevicePolicyManager} instance returned by
* {@link #getParentProfileInstance(ComponentName)} in order to set restrictions on the parent
* profile.
*
+ * <p><string>Note:</strong> this method is ignored on
+ * {PackageManager#FEATURE_AUTOMOTIVE automotive builds}.
+ *
* @deprecated see {@link #setPasswordQuality(ComponentName, int)} for details.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
@@ -3625,12 +3630,14 @@
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
* not, a security exception will be thrown.
* <p>
- *
* Apps targeting {@link android.os.Build.VERSION_CODES#R} and below can call this method on the
* {@link DevicePolicyManager} instance returned by
* {@link #getParentProfileInstance(ComponentName)} in order to set restrictions on the parent
* profile.
*
+ * <p><string>Note:</strong> this method is ignored on
+ * {PackageManager#FEATURE_AUTOMOTIVE automotive builds}.
+ *
* @deprecated see {@link #setPasswordQuality(ComponentName, int)} for details.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
@@ -3715,12 +3722,14 @@
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
* not, a security exception will be thrown.
* <p>
- *
* Apps targeting {@link android.os.Build.VERSION_CODES#R} and below can call this method on the
* {@link DevicePolicyManager} instance returned by
* {@link #getParentProfileInstance(ComponentName)} in order to set restrictions on the parent
* profile.
*
+ * <p><string>Note:</strong> this method is ignored on
+ * {PackageManager#FEATURE_AUTOMOTIVE automotive builds}.
+ *
* @deprecated see {@link #setPasswordQuality(ComponentName, int)} for details.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
@@ -3805,12 +3814,14 @@
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
* not, a security exception will be thrown.
* <p>
- *
* Apps targeting {@link android.os.Build.VERSION_CODES#R} and below can call this method on the
* {@link DevicePolicyManager} instance returned by
* {@link #getParentProfileInstance(ComponentName)} in order to set restrictions on the parent
* profile.
*
+ * <p><string>Note:</strong> this method is ignored on
+ * {PackageManager#FEATURE_AUTOMOTIVE automotive builds}.
+ *
* @deprecated see {@link #setPasswordQuality(ComponentName, int)} for details.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
@@ -3894,12 +3905,14 @@
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
* not, a security exception will be thrown.
* <p>
- *
* Apps targeting {@link android.os.Build.VERSION_CODES#R} and below can call this method on the
* {@link DevicePolicyManager} instance returned by
* {@link #getParentProfileInstance(ComponentName)} in order to set restrictions on the parent
* profile.
*
+ * <p><string>Note:</strong> this method is ignored on
+ * {PackageManager#FEATURE_AUTOMOTIVE automotive builds}.
+ *
* @deprecated see {@link #setPasswordQuality(ComponentName, int)} for details.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
@@ -3983,12 +3996,14 @@
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
* not, a security exception will be thrown.
* <p>
- *
* Apps targeting {@link android.os.Build.VERSION_CODES#R} and below can call this method on the
* {@link DevicePolicyManager} instance returned by
* {@link #getParentProfileInstance(ComponentName)} in order to set restrictions on the parent
* profile.
*
+ * <p><string>Note:</strong> this method is ignored on
+ * {PackageManager#FEATURE_AUTOMOTIVE automotive builds}.
+ *
* @deprecated see {@link #setPasswordQuality(ComponentName, int)} for details.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
@@ -4071,12 +4086,14 @@
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
* not, a security exception will be thrown.
* <p>
- *
* Apps targeting {@link android.os.Build.VERSION_CODES#R} and below can call this method on the
* {@link DevicePolicyManager} instance returned by
* {@link #getParentProfileInstance(ComponentName)} in order to set restrictions on the parent
* profile.
*
+ * <p><string>Note:</strong> this method is ignored on
+ * {PackageManager#FEATURE_AUTOMOTIVE automotive builds}.
+ *
* @deprecated see {@link #setPasswordQuality(ComponentName, int)} for details.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
@@ -9556,7 +9573,14 @@
/**
* Called by a profile owner of secondary user that is affiliated with the device to stop the
- * calling user and switch back to primary.
+ * calling user and switch back to primary user.
+ *
+ * <p>Notice that on devices running with
+ * {@link UserManager#isHeadlessSystemUserMode() headless system user mode}, there is no primary
+ * user, so it switches back to the user that was in the foreground before the first call to
+ * {@link #switchUser(ComponentName, UserHandle)} (or fails with
+ * {@link UserManager#USER_OPERATION_ERROR_UNKNOWN} if that method was not called prior to this
+ * call).
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @return one of the following result codes:
@@ -13327,8 +13351,8 @@
}
/**
- * Returns the default package names set by the OEM that are allowed to request user consent for
- * cross-profile communication without being explicitly enabled by the admin, via {@link
+ * Returns the default package names set by the OEM that are allowed to communicate
+ * cross-profile without being explicitly enabled by the admin, via {@link
* com.android.internal.R.array#cross_profile_apps} and {@link com.android.internal.R.array
* #vendor_cross_profile_apps}.
*
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index a0d2977..8e8c035 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -200,10 +200,10 @@
public abstract List<String> getAllCrossProfilePackages();
/**
- * Returns the default package names set by the OEM that are allowed to request user consent for
- * cross-profile communication without being explicitly enabled by the admin, via
- * {@link com.android.internal.R.array#cross_profile_apps} and
- * {@link com.android.internal.R.array#vendor_cross_profile_apps}.
+ * Returns the default package names set by the OEM that are allowed to communicate
+ * cross-profile without being explicitly enabled by the admin, via {@link
+ * com.android.internal.R.array#cross_profile_apps} and {@link
+ * com.android.internal.R.array#vendor_cross_profile_apps}.
*
* @hide
*/
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 71fae3d..0f5cd4e 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -15,6 +15,7 @@
*/
package android.app.usage;
+import android.annotation.CurrentTimeMillisLong;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -584,6 +585,7 @@
* <p/>
* See {@link System#currentTimeMillis()}.
*/
+ @CurrentTimeMillisLong
public long getTimeStamp() {
return mTimeStamp;
}
@@ -801,6 +803,9 @@
* @return true if an event was available, false if there are no more events.
*/
public boolean getNextEvent(Event eventOut) {
+ if (eventOut == null) {
+ throw new IllegalArgumentException("Given eventOut must not be null");
+ }
if (mIndex >= mEventCount) {
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 65cdc83..4e7c01a 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
@@ -29,6 +31,8 @@
import android.os.RemoteException;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@@ -140,27 +144,6 @@
public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
/**
- * A GATT writeCharacteristic request is started successfully.
- *
- * @hide
- */
- public static final int GATT_WRITE_REQUEST_SUCCESS = 0;
-
- /**
- * A GATT writeCharacteristic request failed to start.
- *
- * @hide
- */
- public static final int GATT_WRITE_REQUEST_FAIL = 1;
-
- /**
- * A GATT writeCharacteristic request is issued to a busy remote device.
- *
- * @hide
- */
- public static final int GATT_WRITE_REQUEST_BUSY = 2;
-
- /**
* No authentication required.
*
* @hide
@@ -430,6 +413,9 @@
if (callback != null) {
if (status == 0) characteristic.setValue(value);
callback.onCharacteristicRead(BluetoothGatt.this, characteristic,
+ value, status);
+ // Keep calling deprecated callback to maintain app compatibility
+ callback.onCharacteristicRead(BluetoothGatt.this, characteristic,
status);
}
}
@@ -443,7 +429,8 @@
*/
@Override
@SuppressLint("AndroidFrameworkRequiresPermission")
- public void onCharacteristicWrite(String address, int status, int handle) {
+ public void onCharacteristicWrite(String address, int status, int handle,
+ byte[] value) {
if (VDBG) {
Log.d(TAG, "onCharacteristicWrite() - Device=" + address
+ " handle=" + handle + " Status=" + status);
@@ -467,12 +454,13 @@
try {
final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
- int requestStatus = GATT_WRITE_REQUEST_FAIL;
+ int requestStatus = BluetoothStatusCodes.ERROR_UNKNOWN;
for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
- requestStatus = mService.writeCharacteristic(mClientIf, address, handle,
- characteristic.getWriteType(), authReq,
- characteristic.getValue(), mAttributionSource);
- if (requestStatus != GATT_WRITE_REQUEST_BUSY) {
+ requestStatus = mService.writeCharacteristic(mClientIf, address,
+ handle, characteristic.getWriteType(), authReq,
+ value, mAttributionSource);
+ if (requestStatus
+ != BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY) {
break;
}
try {
@@ -488,7 +476,6 @@
}
mAuthRetryState = AUTH_RETRY_STATE_IDLE;
-
runOrQueueCallback(new Runnable() {
@Override
public void run() {
@@ -525,6 +512,9 @@
if (callback != null) {
characteristic.setValue(value);
callback.onCharacteristicChanged(BluetoothGatt.this,
+ characteristic, value);
+ // Keep calling deprecated callback to maintain app compatibility
+ callback.onCharacteristicChanged(BluetoothGatt.this,
characteristic);
}
}
@@ -578,6 +568,9 @@
final BluetoothGattCallback callback = mCallback;
if (callback != null) {
if (status == 0) descriptor.setValue(value);
+ callback.onDescriptorRead(BluetoothGatt.this, descriptor, status,
+ value);
+ // Keep calling deprecated callback to maintain app compatibility
callback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
}
}
@@ -590,7 +583,8 @@
*/
@Override
@SuppressLint("AndroidFrameworkRequiresPermission")
- public void onDescriptorWrite(String address, int status, int handle) {
+ public void onDescriptorWrite(String address, int status, int handle,
+ byte[] value) {
if (VDBG) {
Log.d(TAG,
"onDescriptorWrite() - Device=" + address + " handle=" + handle);
@@ -614,7 +608,7 @@
final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
mService.writeDescriptor(mClientIf, address, handle,
- authReq, descriptor.getValue(), mAttributionSource);
+ authReq, value, mAttributionSource);
mAuthRetryState++;
return;
} catch (RemoteException e) {
@@ -1194,8 +1188,8 @@
* Reads the requested characteristic from the associated remote device.
*
* <p>This is an asynchronous operation. The result of the read operation
- * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
- * callback.
+ * is reported by the {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt,
+ * BluetoothGattCharacteristic, byte[], int)} callback.
*
* @param characteristic Characteristic to read from the remote device
* @return true, if the read operation was initiated successfully
@@ -1240,8 +1234,8 @@
* Reads the characteristic using its UUID from the associated remote device.
*
* <p>This is an asynchronous operation. The result of the read operation
- * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
- * callback.
+ * is reported by the {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt,
+ * BluetoothGattCharacteristic, byte[], int)} callback.
*
* @param uuid UUID of characteristic to read from the remote device
* @return true, if the read operation was initiated successfully
@@ -1284,40 +1278,94 @@
*
* @param characteristic Characteristic to write on the remote device
* @return true, if the write operation was initiated successfully
+ * @throws IllegalArgumentException if characteristic or its value are null
+ *
+ * @deprecated Use {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[],
+ * int)} as this is not memory safe.
*/
+ @Deprecated
@RequiresLegacyBluetoothPermission
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
+ try {
+ return writeCharacteristic(characteristic, characteristic.getValue(),
+ characteristic.getWriteType()) == BluetoothStatusCodes.SUCCESS;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ BluetoothStatusCodes.SUCCESS,
+ BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
+ BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION,
+ BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED,
+ BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND,
+ BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED,
+ BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY,
+ BluetoothStatusCodes.ERROR_UNKNOWN
+ })
+ public @interface WriteOperationReturnValues{}
+
+ /**
+ * Writes a given characteristic and its values to the associated remote device.
+ *
+ * <p>Once the write operation has been completed, the
+ * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
+ * reporting the result of the operation.
+ *
+ * @param characteristic Characteristic to write on the remote device
+ * @return whether the characteristic was successfully written to
+ * @throws IllegalArgumentException if characteristic or value are null
+ */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @WriteOperationReturnValues
+ public int writeCharacteristic(@NonNull BluetoothGattCharacteristic characteristic,
+ @NonNull byte[] value, int writeType) {
+ if (characteristic == null) {
+ throw new IllegalArgumentException("characteristic must not be null");
+ }
+ if (value == null) {
+ throw new IllegalArgumentException("value must not be null");
+ }
+ if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
&& (characteristic.getProperties()
& BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) {
- return false;
+ return BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED;
+ }
+ if (mService == null || mClientIf == 0) {
+ return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
}
- if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
- if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
-
BluetoothGattService service = characteristic.getService();
- if (service == null) return false;
+ if (service == null) {
+ throw new IllegalArgumentException("Characteristic must have a non-null service");
+ }
BluetoothDevice device = service.getDevice();
- if (device == null) return false;
+ if (device == null) {
+ throw new IllegalArgumentException("Service must have a non-null device");
+ }
synchronized (mDeviceBusyLock) {
if (mDeviceBusy) {
- return false;
+ return BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY;
}
mDeviceBusy = true;
}
- int requestStatus = GATT_WRITE_REQUEST_FAIL;
+ int requestStatus = BluetoothStatusCodes.ERROR_UNKNOWN;
try {
for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
requestStatus = mService.writeCharacteristic(mClientIf, device.getAddress(),
- characteristic.getInstanceId(), characteristic.getWriteType(),
- AUTHENTICATION_NONE, characteristic.getValue(), mAttributionSource);
- if (requestStatus != GATT_WRITE_REQUEST_BUSY) {
+ characteristic.getInstanceId(), writeType, AUTHENTICATION_NONE, value,
+ mAttributionSource);
+ if (requestStatus != BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY) {
break;
}
try {
@@ -1330,10 +1378,10 @@
synchronized (mDeviceBusyLock) {
mDeviceBusy = false;
}
- return false;
+ throw e.rethrowFromSystemServer();
}
- return requestStatus == GATT_WRITE_REQUEST_SUCCESS;
+ return requestStatus;
}
/**
@@ -1384,45 +1432,86 @@
/**
* Write the value of a given descriptor to the associated remote device.
*
- * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
- * triggered to report the result of the write operation.
+ * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is triggered to report the
+ * result of the write operation.
*
* @param descriptor Descriptor to write to the associated remote device
* @return true, if the write operation was initiated successfully
+ * @throws IllegalArgumentException if descriptor or its value are null
+ *
+ * @deprecated Use {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, byte[])} as
+ * this is not memory safe.
*/
+ @Deprecated
@RequiresLegacyBluetoothPermission
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
+ try {
+ return writeDescriptor(descriptor, descriptor.getValue())
+ == BluetoothStatusCodes.SUCCESS;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ /**
+ * Write the value of a given descriptor to the associated remote device.
+ *
+ * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is triggered to report the
+ * result of the write operation.
+ *
+ * @param descriptor Descriptor to write to the associated remote device
+ * @return true, if the write operation was initiated successfully
+ * @throws IllegalArgumentException if descriptor or value are null
+ */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @WriteOperationReturnValues
+ public int writeDescriptor(@NonNull BluetoothGattDescriptor descriptor,
+ @NonNull byte[] value) {
+ if (descriptor == null) {
+ throw new IllegalArgumentException("descriptor must not be null");
+ }
+ if (value == null) {
+ throw new IllegalArgumentException("value must not be null");
+ }
if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
- if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
+ if (mService == null || mClientIf == 0) {
+ return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
+ }
BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
- if (characteristic == null) return false;
+ if (characteristic == null) {
+ throw new IllegalArgumentException("Descriptor must have a non-null characteristic");
+ }
BluetoothGattService service = characteristic.getService();
- if (service == null) return false;
+ if (service == null) {
+ throw new IllegalArgumentException("Characteristic must have a non-null service");
+ }
BluetoothDevice device = service.getDevice();
- if (device == null) return false;
+ if (device == null) {
+ throw new IllegalArgumentException("Service must have a non-null device");
+ }
synchronized (mDeviceBusyLock) {
- if (mDeviceBusy) return false;
+ if (mDeviceBusy) return BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY;
mDeviceBusy = true;
}
try {
- mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
- AUTHENTICATION_NONE, descriptor.getValue(), mAttributionSource);
+ return mService.writeDescriptor(mClientIf, device.getAddress(),
+ descriptor.getInstanceId(), AUTHENTICATION_NONE, value, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
synchronized (mDeviceBusyLock) {
mDeviceBusy = false;
}
- return false;
+ e.rethrowFromSystemServer();
}
-
- return true;
+ return BluetoothStatusCodes.ERROR_UNKNOWN;
}
/**
@@ -1431,9 +1520,9 @@
* <p>Once a reliable write transaction has been initiated, all calls
* to {@link #writeCharacteristic} are sent to the remote device for
* verification and queued up for atomic execution. The application will
- * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
- * in response to every {@link #writeCharacteristic} call and is responsible
- * for verifying if the value has been transmitted accurately.
+ * receive a {@link BluetoothGattCallback#onCharacteristicWrite} callback in response to every
+ * {@link #writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} call and is
+ * responsible for verifying if the value has been transmitted accurately.
*
* <p>After all characteristics have been queued up and verified,
* {@link #executeReliableWrite} will execute all writes. If a characteristic
@@ -1530,9 +1619,9 @@
* Enable or disable notifications/indications for a given characteristic.
*
* <p>Once notifications are enabled for a characteristic, a
- * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
- * triggered if the remote device indicates that the given characteristic
- * has changed.
+ * {@link BluetoothGattCallback#onCharacteristicChanged(BluetoothGatt,
+ * BluetoothGattCharacteristic, byte[])} callback will be triggered if the remote device
+ * indicates that the given characteristic has changed.
*
* @param characteristic The characteristic for which to enable notifications
* @param enable Set to true to enable notifications/indications
diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java
index 1c40cff..d0a5a1e 100644
--- a/core/java/android/bluetooth/BluetoothGattCallback.java
+++ b/core/java/android/bluetooth/BluetoothGattCallback.java
@@ -80,16 +80,34 @@
/**
* Callback reporting the result of a characteristic read operation.
*
- * @param gatt GATT client invoked {@link BluetoothGatt#readCharacteristic}
+ * @param gatt GATT client invoked
+ * {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)}
* @param characteristic Characteristic that was read from the associated remote device.
- * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
- * successfully.
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
+ * successfully.
+ * @deprecated Use {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt,
+ * BluetoothGattCharacteristic, byte[], int)} as it is memory safe
*/
+ @Deprecated
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic,
int status) {
}
/**
+ * Callback reporting the result of a characteristic read operation.
+ *
+ * @param gatt GATT client invoked
+ * {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)}
+ * @param characteristic Characteristic that was read from the associated remote device.
+ * @param value the value of the characteristic
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
+ * successfully.
+ */
+ public void onCharacteristicRead(@NonNull BluetoothGatt gatt, @NonNull
+ BluetoothGattCharacteristic characteristic, @NonNull byte[] value, int status) {
+ }
+
+ /**
* Callback indicating the result of a characteristic write operation.
*
* <p>If this callback is invoked while a reliable write transaction is
@@ -98,10 +116,13 @@
* value to the desired value to be written. If the values don't match,
* the application must abort the reliable write transaction.
*
- * @param gatt GATT client invoked {@link BluetoothGatt#writeCharacteristic}
+ * @param gatt GATT client that invoked
+ * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic,
+ * byte[], int)}
* @param characteristic Characteristic that was written to the associated remote device.
- * @param status The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if the
- * operation succeeds.
+ * @param status The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if
+ * the
+ * operation succeeds.
*/
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
@@ -110,33 +131,68 @@
/**
* Callback triggered as a result of a remote characteristic notification.
*
- * @param gatt GATT client the characteristic is associated with
+ * @param gatt GATT client the characteristic is associated with
* @param characteristic Characteristic that has been updated as a result of a remote
- * notification event.
+ * notification event.
+ * @deprecated Use {@link BluetoothGattCallback#onCharacteristicChanged(BluetoothGatt,
+ * BluetoothGattCharacteristic, byte[])} as it is memory safe by providing the characteristic
+ * value at the time of notification.
*/
+ @Deprecated
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
}
/**
+ * Callback triggered as a result of a remote characteristic notification. Note that the value
+ * within the characteristic object may have changed since receiving the remote characteristic
+ * notification, so check the parameter value for the value at the time of notification.
+ *
+ * @param gatt GATT client the characteristic is associated with
+ * @param characteristic Characteristic that has been updated as a result of a remote
+ * notification event.
+ * @param value notified characteristic value
+ */
+ public void onCharacteristicChanged(@NonNull BluetoothGatt gatt,
+ @NonNull BluetoothGattCharacteristic characteristic, @NonNull byte[] value) {
+ }
+
+ /**
* Callback reporting the result of a descriptor read operation.
*
- * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor}
+ * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor}
* @param descriptor Descriptor that was read from the associated remote device.
- * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
- * successfully
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
+ * successfully
+ * @deprecated Use {@link BluetoothGattCallback#onDescriptorRead(BluetoothGatt,
+ * BluetoothGattDescriptor, int, byte[])} as it is memory safe by providing the descriptor
+ * value at the time it was read.
*/
+ @Deprecated
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
int status) {
}
/**
+ * Callback reporting the result of a descriptor read operation.
+ *
+ * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor}
+ * @param descriptor Descriptor that was read from the associated remote device.
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
+ * successfully
+ * @param value the descriptor value at the time of the read operation
+ */
+ public void onDescriptorRead(@NonNull BluetoothGatt gatt,
+ @NonNull BluetoothGattDescriptor descriptor, int status, @NonNull byte[] value) {
+ }
+
+ /**
* Callback indicating the result of a descriptor write operation.
*
- * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor}
+ * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor}
* @param descriptor Descriptor that was writte to the associated remote device.
- * @param status The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if the
- * operation succeeds.
+ * @param status The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if the
+ * operation succeeds.
*/
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
int status) {
diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
index 8a7d4ba..c5e986e 100644
--- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java
+++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
@@ -451,8 +451,8 @@
* Set the write type for this characteristic
*
* <p>Setting the write type of a characteristic determines how the
- * {@link BluetoothGatt#writeCharacteristic} function write this
- * characteristic.
+ * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} function
+ * write this characteristic.
*
* @param writeType The write type to for this characteristic. Can be one of: {@link
* #WRITE_TYPE_DEFAULT}, {@link #WRITE_TYPE_NO_RESPONSE} or {@link #WRITE_TYPE_SIGNED}.
@@ -504,7 +504,10 @@
* operation or if a characteristic update notification has been received.
*
* @return Cached value of the characteristic
+ *
+ * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} instead
*/
+ @Deprecated
public byte[] getValue() {
return mValue;
}
@@ -521,7 +524,11 @@
* @param formatType The format type used to interpret the characteristic value.
* @param offset Offset at which the integer value can be found.
* @return Cached value of the characteristic or null of offset exceeds value size.
+ *
+ * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get
+ * the characteristic value
*/
+ @Deprecated
public Integer getIntValue(int formatType, int offset) {
if ((offset + getTypeLen(formatType)) > mValue.length) return null;
@@ -558,7 +565,11 @@
* @param offset Offset at which the float value can be found.
* @return Cached value of the characteristic at a given offset or null if the requested offset
* exceeds the value size.
+ *
+ * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get
+ * the characteristic value
*/
+ @Deprecated
public Float getFloatValue(int formatType, int offset) {
if ((offset + getTypeLen(formatType)) > mValue.length) return null;
@@ -580,7 +591,11 @@
*
* @param offset Offset at which the string value can be found.
* @return Cached value of the characteristic
+ *
+ * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get
+ * the characteristic value
*/
+ @Deprecated
public String getStringValue(int offset) {
if (mValue == null || offset > mValue.length) return null;
byte[] strBytes = new byte[mValue.length - offset];
@@ -599,7 +614,11 @@
* @param value New value for this characteristic
* @return true if the locally stored value has been set, false if the requested value could not
* be stored locally.
+ *
+ * @deprecated Pass the characteristic value directly into
+ * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)}
*/
+ @Deprecated
public boolean setValue(byte[] value) {
mValue = value;
return true;
@@ -613,7 +632,11 @@
* @param formatType Integer format type used to transform the value parameter
* @param offset Offset at which the value should be placed
* @return true if the locally stored value has been set
+ *
+ * @deprecated Pass the characteristic value directly into
+ * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)}
*/
+ @Deprecated
public boolean setValue(int value, int formatType, int offset) {
int len = offset + getTypeLen(formatType);
if (mValue == null) mValue = new byte[len];
@@ -660,7 +683,11 @@
* @param formatType Float format type used to transform the value parameter
* @param offset Offset at which the value should be placed
* @return true if the locally stored value has been set
+ *
+ * @deprecated Pass the characteristic value directly into
+ * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)}
*/
+ @Deprecated
public boolean setValue(int mantissa, int exponent, int formatType, int offset) {
int len = offset + getTypeLen(formatType);
if (mValue == null) mValue = new byte[len];
@@ -697,7 +724,11 @@
*
* @param value New value for this characteristic
* @return true if the locally stored value has been set
+ *
+ * @deprecated Pass the characteristic value directly into
+ * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)}
*/
+ @Deprecated
public boolean setValue(String value) {
mValue = value.getBytes();
return true;
diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.java b/core/java/android/bluetooth/BluetoothGattDescriptor.java
index ed5ea08..a35d5b9 100644
--- a/core/java/android/bluetooth/BluetoothGattDescriptor.java
+++ b/core/java/android/bluetooth/BluetoothGattDescriptor.java
@@ -260,7 +260,10 @@
* operation.
*
* @return Cached value of the descriptor
+ *
+ * @deprecated Use {@link BluetoothGatt#readDescriptor(BluetoothGattDescriptor)} instead
*/
+ @Deprecated
public byte[] getValue() {
return mValue;
}
@@ -276,7 +279,11 @@
* @param value New value for this descriptor
* @return true if the locally stored value has been set, false if the requested value could not
* be stored locally.
+ *
+ * @deprecated Pass the descriptor value directly into
+ * {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, byte[])}
*/
+ @Deprecated
public boolean setValue(byte[] value) {
mValue = value;
return true;
diff --git a/core/java/android/bluetooth/BluetoothStatusCodes.java b/core/java/android/bluetooth/BluetoothStatusCodes.java
index 63e84ed..ca01784 100644
--- a/core/java/android/bluetooth/BluetoothStatusCodes.java
+++ b/core/java/android/bluetooth/BluetoothStatusCodes.java
@@ -79,9 +79,31 @@
public static final int ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION = 7;
/**
+ * Error code indicating that the caller does not have the
+ * {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission
+ */
+ public static final int ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION = 8;
+
+ /**
+ * Error code indicating that the profile service is not bound. You can bind a profile service
+ * by calling {@link BluetoothAdapter#getProfileProxy}
+ */
+ public static final int ERROR_PROFILE_SERVICE_NOT_BOUND = 9;
+
+ /**
* Error code indicating that the feature is not supported.
*/
- public static final int ERROR_FEATURE_NOT_SUPPORTED = 8;
+ public static final int ERROR_FEATURE_NOT_SUPPORTED = 10;
+
+ /**
+ * A GATT writeCharacteristic request is not permitted on the remote device.
+ */
+ public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 101;
+
+ /**
+ * A GATT writeCharacteristic request is issued to a busy remote device.
+ */
+ public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 102;
/**
* If another application has already requested {@link OobData} then another fetch will be
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index a8fe602..6719a69 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -109,7 +109,7 @@
}
private final ICompanionDeviceManager mService;
- private final Context mContext;
+ private Context mContext;
/** @hide */
public CompanionDeviceManager(
@@ -572,6 +572,7 @@
mCallback = null;
mHandler = null;
mRequest = null;
+ mContext = null;
}
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index fc94fb2..665f626 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5825,7 +5825,6 @@
* {@link android.app.LocaleManager}.
*
* @see #getSystemService(String)
- * @hide
*/
public static final String LOCALE_SERVICE = "locale";
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index cbb5c2b..e7ca76e 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -23,6 +23,7 @@
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
+import android.compat.annotation.EnabledSince;
import android.compat.annotation.Overridable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
@@ -1042,6 +1043,14 @@
public static final float OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE = 16 / 9f;
/**
+ * Compares activity window layout min width/height with require space for multi window to
+ * determine if it can be put into multi window mode.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+ private static final long CHECK_MIN_WIDTH_HEIGHT_FOR_MULTI_WINDOW = 197654537L;
+
+ /**
* Convert Java change bits to native.
*
* @hide
@@ -1481,6 +1490,17 @@
}
}
+ /**
+ * Whether we should compare activity window layout min width/height with require space for
+ * multi window to determine if it can be put into multi window mode.
+ * @hide
+ */
+ public boolean shouldCheckMinWidthHeightForMultiWindow() {
+ return CompatChanges.isChangeEnabled(CHECK_MIN_WIDTH_HEIGHT_FOR_MULTI_WINDOW,
+ applicationInfo.packageName,
+ UserHandle.getUserHandleForUid(applicationInfo.uid));
+ }
+
public void dump(Printer pw, String prefix) {
dump(pw, prefix, DUMP_FLAG_ALL);
}
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 0462a4b..eefa63f 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -346,20 +346,44 @@
*/
public int installLocation = INSTALL_LOCATION_INTERNAL_ONLY;
- /** @hide */
+ /**
+ * Whether or not the package is a stub and should be replaced by a full version of the app.
+ *
+ * @hide
+ */
public boolean isStub;
- /** @hide */
+ /**
+ * Whether the app is included when the device is booted into a minimal state. Set through the
+ * non-namespaced "coreApp" attribute of the manifest tag.
+ *
+ * @hide
+ */
@UnsupportedAppUsage
public boolean coreApp;
- /** @hide */
+ /**
+ * Signals that this app is required for all users on the device.
+ *
+ * When a restricted user profile is created, the user is prompted with a list of apps to
+ * install on that user. Settings uses this field to determine obligatory apps which cannot be
+ * deselected.
+ *
+ * This restriction is not handled by the framework itself.
+ * @hide
+ */
public boolean requiredForAllUsers;
- /** @hide */
+ /**
+ * The restricted account authenticator type that is used by this application.
+ * @hide
+ */
public String restrictedAccountType;
- /** @hide */
+ /**
+ * The required account type without which this application will not function.
+ * @hide
+ */
public String requiredAccountType;
/**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 07151ec..b1777a0 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -49,7 +49,6 @@
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.dex.ArtManager;
-import android.content.pm.pkg.PackageUserState;
import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -4479,6 +4478,20 @@
@TestApi
public static final String SYSTEM_SHARED_LIBRARY_SHARED = "android.ext.shared";
+ /** @hide */
+ @IntDef({
+ NOTIFY_PACKAGE_USE_ACTIVITY,
+ NOTIFY_PACKAGE_USE_SERVICE,
+ NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE,
+ NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER,
+ NOTIFY_PACKAGE_USE_CONTENT_PROVIDER,
+ NOTIFY_PACKAGE_USE_BACKUP,
+ NOTIFY_PACKAGE_USE_CROSS_PACKAGE,
+ NOTIFY_PACKAGE_USE_INSTRUMENTATION,
+ })
+ public @interface NotifyReason {
+ }
+
/**
* Used when starting a process for an Activity.
*
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index cbf54e6..e2c91a4b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -56,9 +56,8 @@
import android.content.IntentFilter;
import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.pkg.PackageUserState;
+import android.content.pm.pkg.FrameworkPackageUserState;
import android.content.pm.pkg.PackageUserStateUtils;
-import android.content.pm.split.SplitAssetLoader;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.res.ApkAssets;
@@ -645,7 +644,7 @@
* explicitly wanted all uninstalled and hidden packages as well.
* @param appInfo The applicationInfo of the app being checked.
*/
- private static boolean checkUseInstalledOrHidden(int flags, PackageUserState state,
+ private static boolean checkUseInstalledOrHidden(int flags, FrameworkPackageUserState state,
ApplicationInfo appInfo) {
// Returns false if the package is hidden system app until installed.
if ((flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) == 0
@@ -662,7 +661,7 @@
|| (flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) != 0));
}
- public static boolean isAvailable(PackageUserState state) {
+ public static boolean isAvailable(FrameworkPackageUserState state) {
return checkUseInstalledOrHidden(0, state, null);
}
@@ -675,7 +674,7 @@
@UnsupportedAppUsage
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int[] gids, int flags, long firstInstallTime, long lastUpdateTime,
- Set<String> grantedPermissions, PackageUserState state) {
+ Set<String> grantedPermissions, FrameworkPackageUserState state) {
return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
grantedPermissions, state, UserHandle.getCallingUserId());
@@ -684,7 +683,7 @@
@UnsupportedAppUsage
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int[] gids, int flags, long firstInstallTime, long lastUpdateTime,
- Set<String> grantedPermissions, PackageUserState state, int userId) {
+ Set<String> grantedPermissions, FrameworkPackageUserState state, int userId) {
return generatePackageInfo(p, null, gids, flags, firstInstallTime, lastUpdateTime,
grantedPermissions, state, userId);
@@ -701,12 +700,12 @@
public static PackageInfo generatePackageInfo(
PackageParser.Package pkg, ApexInfo apexInfo, int flags) {
return generatePackageInfo(pkg, apexInfo, EmptyArray.INT, flags, 0, 0,
- Collections.emptySet(), PackageUserState.DEFAULT, UserHandle.getCallingUserId());
+ Collections.emptySet(), FrameworkPackageUserState.DEFAULT, UserHandle.getCallingUserId());
}
private static PackageInfo generatePackageInfo(PackageParser.Package p, ApexInfo apexInfo,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
- Set<String> grantedPermissions, PackageUserState state, int userId) {
+ Set<String> grantedPermissions, FrameworkPackageUserState state, int userId) {
if (!checkUseInstalledOrHidden(flags, state, p.applicationInfo) || !p.isMatch(flags)) {
return null;
}
@@ -7883,7 +7882,7 @@
}
private static boolean copyNeeded(int flags, Package p,
- PackageUserState state, Bundle metaData, int userId) {
+ FrameworkPackageUserState state, Bundle metaData, int userId) {
if (userId != UserHandle.USER_SYSTEM) {
// We always need to copy for other users, since we need
// to fix up the uid.
@@ -7929,12 +7928,12 @@
@UnsupportedAppUsage
public static ApplicationInfo generateApplicationInfo(Package p, int flags,
- PackageUserState state) {
+ FrameworkPackageUserState state) {
return generateApplicationInfo(p, flags, state, UserHandle.getCallingUserId());
}
private static void updateApplicationInfo(ApplicationInfo ai, int flags,
- PackageUserState state) {
+ FrameworkPackageUserState state) {
// CompatibilityMode is global state.
if (!sCompatibilityModeEnabled) {
ai.disableCompatibilityMode();
@@ -7989,7 +7988,7 @@
@UnsupportedAppUsage
public static ApplicationInfo generateApplicationInfo(Package p, int flags,
- PackageUserState state, int userId) {
+ FrameworkPackageUserState state, int userId) {
if (p == null) return null;
if (!checkUseInstalledOrHidden(flags, state, p.applicationInfo) || !p.isMatch(flags)) {
return null;
@@ -8029,7 +8028,7 @@
}
public static ApplicationInfo generateApplicationInfo(ApplicationInfo ai, int flags,
- PackageUserState state, int userId) {
+ FrameworkPackageUserState state, int userId) {
if (ai == null) return null;
if (!checkUseInstalledOrHidden(flags, state, ai)) {
return null;
@@ -8188,7 +8187,7 @@
@UnsupportedAppUsage
public static final ActivityInfo generateActivityInfo(Activity a, int flags,
- PackageUserState state, int userId) {
+ FrameworkPackageUserState state, int userId) {
if (a == null) return null;
if (!checkUseInstalledOrHidden(flags, state, a.owner.applicationInfo)) {
return null;
@@ -8205,7 +8204,7 @@
}
public static final ActivityInfo generateActivityInfo(ActivityInfo ai, int flags,
- PackageUserState state, int userId) {
+ FrameworkPackageUserState state, int userId) {
if (ai == null) return null;
if (!checkUseInstalledOrHidden(flags, state, ai.applicationInfo)) {
return null;
@@ -8280,7 +8279,7 @@
@UnsupportedAppUsage
public static final ServiceInfo generateServiceInfo(Service s, int flags,
- PackageUserState state, int userId) {
+ FrameworkPackageUserState state, int userId) {
if (s == null) return null;
if (!checkUseInstalledOrHidden(flags, state, s.owner.applicationInfo)) {
return null;
@@ -8378,7 +8377,7 @@
@UnsupportedAppUsage
public static final ProviderInfo generateProviderInfo(Provider p, int flags,
- PackageUserState state, int userId) {
+ FrameworkPackageUserState state, int userId) {
if (p == null) return null;
if (!checkUseInstalledOrHidden(flags, state, p.owner.applicationInfo)) {
return null;
diff --git a/core/java/android/content/pm/SELinuxUtil.java b/core/java/android/content/pm/SELinuxUtil.java
index 90dcd85..898dddf 100644
--- a/core/java/android/content/pm/SELinuxUtil.java
+++ b/core/java/android/content/pm/SELinuxUtil.java
@@ -16,7 +16,7 @@
package android.content.pm;
-import android.content.pm.pkg.PackageUserState;
+import android.content.pm.pkg.FrameworkPackageUserState;
/**
* Utility methods that need to be used in application space.
@@ -31,7 +31,7 @@
public static final String COMPLETE_STR = ":complete";
/** @hide */
- public static String getSeinfoUser(PackageUserState userState) {
+ public static String getSeinfoUser(FrameworkPackageUserState userState) {
if (userState.isInstantApp()) {
return INSTANT_APP_STR + COMPLETE_STR;
}
diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java
index 259cbf9..5ffb958 100644
--- a/core/java/android/content/pm/parsing/ApkLite.java
+++ b/core/java/android/content/pm/parsing/ApkLite.java
@@ -128,6 +128,11 @@
*/
private final int mRollbackDataPolicy;
+ /**
+ * Indicates if this app contains a {@link android.app.admin.DeviceAdminReceiver}.
+ */
+ private final boolean mHasDeviceAdminReceiver;
+
public ApkLite(String path, String packageName, String splitName, boolean isFeatureSplit,
String configForSplit, String usesSplitName, boolean isSplitRequired, int versionCode,
int versionCodeMajor, int revisionCode, int installLocation,
@@ -137,7 +142,8 @@
String targetPackageName, boolean overlayIsStatic, int overlayPriority,
String requiredSystemPropertyName, String requiredSystemPropertyValue,
int minSdkVersion, int targetSdkVersion, int rollbackDataPolicy,
- Set<String> requiredSplitTypes, Set<String> splitTypes) {
+ Set<String> requiredSplitTypes, Set<String> splitTypes,
+ boolean hasDeviceAdminReceiver) {
mPath = path;
mPackageName = packageName;
mSplitName = splitName;
@@ -169,6 +175,7 @@
mMinSdkVersion = minSdkVersion;
mTargetSdkVersion = targetSdkVersion;
mRollbackDataPolicy = rollbackDataPolicy;
+ mHasDeviceAdminReceiver = hasDeviceAdminReceiver;
}
/**
@@ -461,11 +468,16 @@
return mRollbackDataPolicy;
}
+ @DataClass.Generated.Member
+ public boolean isHasDeviceAdminReceiver() {
+ return mHasDeviceAdminReceiver;
+ }
+
@DataClass.Generated(
- time = 1631763761543L,
+ time = 1635266936769L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ApkLite.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 82637aa..1639ee9 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -28,6 +28,7 @@
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import android.annotation.NonNull;
+import android.app.admin.DeviceAdminReceiver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.SigningDetails;
@@ -399,6 +400,8 @@
String requiredSystemPropertyName = null;
String requiredSystemPropertyValue = null;
+ boolean hasDeviceAdminReceiver = false;
+
// Only search the tree when the tag is the direct child of <manifest> tag
int type;
final int searchDepth = parser.getDepth() + 1;
@@ -432,6 +435,10 @@
"useEmbeddedDex", false);
rollbackDataPolicy = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE,
"rollbackDataPolicy", 0);
+ String permission = parser.getAttributeValue(ANDROID_RES_NAMESPACE,
+ "permission");
+ boolean hasBindDeviceAdminPermission =
+ android.Manifest.permission.BIND_DEVICE_ADMIN.equals(permission);
final int innerDepth = parser.getDepth();
int innerType;
@@ -449,6 +456,9 @@
if (ParsingPackageUtils.TAG_PROFILEABLE.equals(parser.getName())) {
profilableByShell = parser.getAttributeBooleanValue(ANDROID_RES_NAMESPACE,
"shell", profilableByShell);
+ } else if (ParsingPackageUtils.TAG_RECEIVER.equals(parser.getName())) {
+ hasDeviceAdminReceiver |= isDeviceAdminReceiver(
+ parser, hasBindDeviceAdminPermission);
}
}
} else if (ParsingPackageUtils.TAG_OVERLAY.equals(parser.getName())) {
@@ -541,7 +551,42 @@
useEmbeddedDex, extractNativeLibs, isolatedSplits, targetPackage,
overlayIsStatic, overlayPriority, requiredSystemPropertyName,
requiredSystemPropertyValue, minSdkVersion, targetSdkVersion,
- rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second));
+ rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second,
+ hasDeviceAdminReceiver));
+ }
+
+ private static boolean isDeviceAdminReceiver(
+ XmlResourceParser parser, boolean applicationHasBindDeviceAdminPermission)
+ throws XmlPullParserException, IOException {
+ String permission = parser.getAttributeValue(ANDROID_RES_NAMESPACE,
+ "permission");
+ if (!applicationHasBindDeviceAdminPermission
+ && !android.Manifest.permission.BIND_DEVICE_ADMIN.equals(permission)) {
+ return false;
+ }
+
+ boolean hasDeviceAdminReceiver = false;
+ final int depth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > depth)) {
+ if (type == XmlPullParser.END_TAG
+ || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ if (parser.getDepth() != depth + 1) {
+ // Search only under <receiver>.
+ continue;
+ }
+ if (!hasDeviceAdminReceiver && "meta-data".equals(parser.getName())) {
+ String name = parser.getAttributeValue(ANDROID_RES_NAMESPACE,
+ "name");
+ if (DeviceAdminReceiver.DEVICE_ADMIN_META_DATA.equals(name)) {
+ hasDeviceAdminReceiver = true;
+ }
+ }
+ }
+ return hasDeviceAdminReceiver;
}
public static ParseResult<Pair<String, String>> parsePackageSplitNames(ParseInput input,
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index cb26165..ef124c7 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -52,7 +52,7 @@
import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedService;
import android.content.pm.parsing.component.ParsedUsesPermission;
-import android.content.pm.pkg.PackageUserState;
+import android.content.pm.pkg.FrameworkPackageUserState;
import android.content.pm.pkg.PackageUserStateUtils;
import android.os.Environment;
import android.os.UserHandle;
@@ -77,7 +77,7 @@
@Nullable
public static PackageInfo generate(ParsingPackageRead pkg, int[] gids,
@PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
- Set<String> grantedPermissions, PackageUserState state, int userId) {
+ Set<String> grantedPermissions, FrameworkPackageUserState state, int userId) {
return generateWithComponents(pkg, gids, flags, firstInstallTime, lastUpdateTime, grantedPermissions,
state, userId, null);
}
@@ -85,13 +85,13 @@
@Nullable
public static PackageInfo generate(ParsingPackageRead pkg, ApexInfo apexInfo, int flags) {
return generateWithComponents(pkg, EmptyArray.INT, flags, 0, 0, Collections.emptySet(),
- PackageUserState.DEFAULT, UserHandle.getCallingUserId(), apexInfo);
+ FrameworkPackageUserState.DEFAULT, UserHandle.getCallingUserId(), apexInfo);
}
@Nullable
private static PackageInfo generateWithComponents(ParsingPackageRead pkg, int[] gids,
@PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
- Set<String> grantedPermissions, PackageUserState state, int userId,
+ Set<String> grantedPermissions, FrameworkPackageUserState state, int userId,
@Nullable ApexInfo apexInfo) {
ApplicationInfo applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
if (applicationInfo == null) {
@@ -191,7 +191,7 @@
@Nullable
public static PackageInfo generateWithoutComponents(ParsingPackageRead pkg, int[] gids,
@PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
- Set<String> grantedPermissions, PackageUserState state, int userId,
+ Set<String> grantedPermissions, FrameworkPackageUserState state, int userId,
@Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo) {
if (!checkUseInstalled(pkg, state, flags)) {
return null;
@@ -206,12 +206,12 @@
* server.
* <p>
* Prefer {@link #generateWithoutComponents(ParsingPackageRead, int[], int, long, long, Set,
- * PackageUserState, int, ApexInfo, ApplicationInfo)}.
+ * FrameworkPackageUserState, int, ApexInfo, ApplicationInfo)}.
*/
@NonNull
public static PackageInfo generateWithoutComponentsUnchecked(ParsingPackageRead pkg, int[] gids,
@PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
- Set<String> grantedPermissions, PackageUserState state, int userId,
+ Set<String> grantedPermissions, FrameworkPackageUserState state, int userId,
@Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo) {
PackageInfo pi = new PackageInfo();
pi.packageName = pkg.getPackageName();
@@ -365,7 +365,7 @@
@Nullable
public static ApplicationInfo generateApplicationInfo(ParsingPackageRead pkg,
- @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId) {
+ @PackageManager.ApplicationInfoFlags int flags, FrameworkPackageUserState state, int userId) {
if (pkg == null) {
return null;
}
@@ -382,7 +382,7 @@
* This bypasses critical checks that are necessary for usage with data passed outside of system
* server.
* <p>
- * Prefer {@link #generateApplicationInfo(ParsingPackageRead, int, PackageUserState, int)}.
+ * Prefer {@link #generateApplicationInfo(ParsingPackageRead, int, FrameworkPackageUserState, int)}.
*
* @param assignUserFields whether to fill the returned {@link ApplicationInfo} with user
* specific fields. This can be skipped when building from a system
@@ -392,7 +392,7 @@
*/
@NonNull
public static ApplicationInfo generateApplicationInfoUnchecked(@NonNull ParsingPackageRead pkg,
- @PackageManager.ApplicationInfoFlags int flags, @NonNull PackageUserState state,
+ @PackageManager.ApplicationInfoFlags int flags, @NonNull FrameworkPackageUserState state,
int userId, boolean assignUserFields) {
// Make shallow copy so we can store the metadata/libraries safely
ApplicationInfo ai = ((ParsingPackageHidden) pkg).toAppInfoWithoutState();
@@ -407,7 +407,7 @@
}
private static void updateApplicationInfo(ApplicationInfo ai, int flags,
- PackageUserState state) {
+ FrameworkPackageUserState state) {
if ((flags & PackageManager.GET_META_DATA) == 0) {
ai.metaData = null;
}
@@ -452,7 +452,7 @@
@Nullable
public static ApplicationInfo generateDelegateApplicationInfo(@Nullable ApplicationInfo ai,
- @PackageManager.ApplicationInfoFlags int flags, @NonNull PackageUserState state,
+ @PackageManager.ApplicationInfoFlags int flags, @NonNull FrameworkPackageUserState state,
int userId) {
if (ai == null || !checkUseInstalledOrHidden(flags, state, ai)) {
return null;
@@ -469,7 +469,7 @@
@Nullable
public static ActivityInfo generateDelegateActivityInfo(@Nullable ActivityInfo a,
- @PackageManager.ComponentInfoFlags int flags, @NonNull PackageUserState state,
+ @PackageManager.ComponentInfoFlags int flags, @NonNull FrameworkPackageUserState state,
int userId) {
if (a == null || !checkUseInstalledOrHidden(flags, state, a.applicationInfo)) {
return null;
@@ -484,7 +484,7 @@
@Nullable
public static ActivityInfo generateActivityInfo(ParsingPackageRead pkg, ParsedActivity a,
- @PackageManager.ComponentInfoFlags int flags, PackageUserState state,
+ @PackageManager.ComponentInfoFlags int flags, FrameworkPackageUserState state,
@Nullable ApplicationInfo applicationInfo, int userId) {
if (a == null) return null;
if (!checkUseInstalled(pkg, state, flags)) {
@@ -505,7 +505,7 @@
* server.
* <p>
* Prefer {@link #generateActivityInfo(ParsingPackageRead, ParsedActivity, int,
- * PackageUserState, ApplicationInfo, int)}.
+ * FrameworkPackageUserState, ApplicationInfo, int)}.
*/
@NonNull
public static ActivityInfo generateActivityInfoUnchecked(@NonNull ParsedActivity a,
@@ -550,13 +550,13 @@
@Nullable
public static ActivityInfo generateActivityInfo(ParsingPackageRead pkg, ParsedActivity a,
- @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId) {
+ @PackageManager.ComponentInfoFlags int flags, FrameworkPackageUserState state, int userId) {
return generateActivityInfo(pkg, a, flags, state, null, userId);
}
@Nullable
public static ServiceInfo generateServiceInfo(ParsingPackageRead pkg, ParsedService s,
- @PackageManager.ComponentInfoFlags int flags, PackageUserState state,
+ @PackageManager.ComponentInfoFlags int flags, FrameworkPackageUserState state,
@Nullable ApplicationInfo applicationInfo, int userId) {
if (s == null) return null;
if (!checkUseInstalled(pkg, state, flags)) {
@@ -576,7 +576,7 @@
* This bypasses critical checks that are necessary for usage with data passed outside of system
* server.
* <p>
- * Prefer {@link #generateServiceInfo(ParsingPackageRead, ParsedService, int, PackageUserState,
+ * Prefer {@link #generateServiceInfo(ParsingPackageRead, ParsedService, int, FrameworkPackageUserState,
* ApplicationInfo, int)}.
*/
@NonNull
@@ -600,13 +600,13 @@
@Nullable
public static ServiceInfo generateServiceInfo(ParsingPackageRead pkg, ParsedService s,
- @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId) {
+ @PackageManager.ComponentInfoFlags int flags, FrameworkPackageUserState state, int userId) {
return generateServiceInfo(pkg, s, flags, state, null, userId);
}
@Nullable
public static ProviderInfo generateProviderInfo(ParsingPackageRead pkg, ParsedProvider p,
- @PackageManager.ComponentInfoFlags int flags, PackageUserState state,
+ @PackageManager.ComponentInfoFlags int flags, FrameworkPackageUserState state,
@Nullable ApplicationInfo applicationInfo, int userId) {
if (p == null) return null;
if (!checkUseInstalled(pkg, state, flags)) {
@@ -627,7 +627,7 @@
* server.
* <p>
* Prefer {@link #generateProviderInfo(ParsingPackageRead, ParsedProvider, int,
- * PackageUserState, ApplicationInfo, int)}.
+ * FrameworkPackageUserState, ApplicationInfo, int)}.
*/
@NonNull
public static ProviderInfo generateProviderInfoUnchecked(@NonNull ParsedProvider p,
@@ -661,13 +661,13 @@
@Nullable
public static ProviderInfo generateProviderInfo(ParsingPackageRead pkg, ParsedProvider p,
- @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId) {
+ @PackageManager.ComponentInfoFlags int flags, FrameworkPackageUserState state, int userId) {
return generateProviderInfo(pkg, p, flags, state, null, userId);
}
/**
* @param assignUserFields see {@link #generateApplicationInfoUnchecked(ParsingPackageRead, int,
- * PackageUserState, int, boolean)}
+ * FrameworkPackageUserState, int, boolean)}
*/
@Nullable
public static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i,
@@ -753,7 +753,7 @@
return new Attribution(pa.getTag(), pa.getLabel());
}
- private static boolean checkUseInstalledOrHidden(int flags, @NonNull PackageUserState state,
+ private static boolean checkUseInstalledOrHidden(int flags, @NonNull FrameworkPackageUserState state,
@Nullable ApplicationInfo appInfo) {
// Returns false if the package is hidden system app until installed.
if ((flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) == 0
@@ -882,7 +882,7 @@
return privateFlagsExt;
}
- private static boolean checkUseInstalled(ParsingPackageRead pkg, PackageUserState state,
+ private static boolean checkUseInstalled(ParsingPackageRead pkg, FrameworkPackageUserState state,
@PackageManager.PackageInfoFlags int flags) {
// If available for the target user
return PackageUserStateUtils.isAvailable(state, flags);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index d19186e..0a10aaa 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -52,7 +52,6 @@
import android.content.pm.parsing.component.ComponentMutateUtils;
import android.content.pm.parsing.component.ComponentParseUtils;
import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedActivityImpl;
import android.content.pm.parsing.component.ParsedActivityUtils;
import android.content.pm.parsing.component.ParsedAttribution;
import android.content.pm.parsing.component.ParsedAttributionUtils;
@@ -191,6 +190,7 @@
public static final String TAG_USES_SDK = "uses-sdk";
public static final String TAG_USES_SPLIT = "uses-split";
public static final String TAG_PROFILEABLE = "profileable";
+ public static final String TAG_RECEIVER = "receiver";
public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes";
diff --git a/core/java/android/content/pm/parsing/PkgWithoutStatePackageInfo.java b/core/java/android/content/pm/parsing/PkgWithoutStatePackageInfo.java
index b10e6fe5..7d758a8 100644
--- a/core/java/android/content/pm/parsing/PkgWithoutStatePackageInfo.java
+++ b/core/java/android/content/pm/parsing/PkgWithoutStatePackageInfo.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ConfigurationInfo;
@@ -33,6 +34,7 @@
import android.content.pm.parsing.component.ParsedPermission;
import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedService;
+import android.content.pm.pkg.FrameworkPackageUserState;
import com.android.internal.R;
@@ -74,6 +76,14 @@
public interface PkgWithoutStatePackageInfo {
/**
+ * Set of Activities parsed from the manifest.
+ *
+ * This contains minimal system state and does not
+ * provide the same information as {@link ActivityInfo}. Effective state can be queried through
+ * {@link android.content.pm.PackageManager#getActivityInfo(ComponentName, int)} or by
+ * combining state from from com.android.server.pm.pkg.PackageState and
+ * {@link FrameworkPackageUserState}.
+ *
* @see ActivityInfo
* @see PackageInfo#activities
*/
@@ -137,6 +147,14 @@
List<ParsedPermission> getPermissions();
/**
+ * Set of {@link android.content.ContentProvider ContentProviders} parsed from the manifest.
+ *
+ * This contains minimal system state and does not
+ * provide the same information as {@link ProviderInfo}. Effective state can be queried through
+ * {@link android.content.pm.PackageManager#getProviderInfo(ComponentName, int)} or by
+ * combining state from from com.android.server.pm.pkg.PackageState and
+ * {@link FrameworkPackageUserState}.
+ *
* @see ProviderInfo
* @see PackageInfo#providers
*/
@@ -144,9 +162,18 @@
List<ParsedProvider> getProviders();
/**
+ * Set of {@link android.content.BroadcastReceiver BroadcastReceivers} parsed from the manifest.
+ *
+ * This contains minimal system state and does not
+ * provide the same information as {@link ActivityInfo}. Effective state can be queried through
+ * {@link android.content.pm.PackageManager#getReceiverInfo(ComponentName, int)} or by
+ * combining state from from com.android.server.pm.pkg.PackageState and
+ * {@link FrameworkPackageUserState}.
+ *
* Since they share several attributes, receivers are parsed as {@link ParsedActivity}, even
- * though they represent different functionality. TODO(b/135203078): Reconsider this and maybe
- * make ParsedReceiver so it's not so confusing
+ * though they represent different functionality.
+ *
+ * TODO(b/135203078): Reconsider this and maybe make ParsedReceiver so it's not so confusing
*
* @see ActivityInfo
* @see PackageInfo#receivers
@@ -173,8 +200,6 @@
List<String> getRequestedPermissions();
/**
- * The required account type without which this application will not function.
- *
* @see PackageInfo#requiredAccountType
* @see R.styleable#AndroidManifestApplication_requiredAccountType
*/
@@ -182,7 +207,7 @@
String getRequiredAccountType();
/**
- * The restricted account authenticator type that is used by this application
+ * The restricted account authenticator type that is used by this application.
*
* @see PackageInfo#restrictedAccountType
* @see R.styleable#AndroidManifestApplication_restrictedAccountType
@@ -191,6 +216,14 @@
String getRestrictedAccountType();
/**
+ * Set of {@link android.app.Service Services} parsed from the manifest.
+ *
+ * This contains minimal system state and does not
+ * provide the same information as {@link ServiceInfo}. Effective state can be queried through
+ * {@link android.content.pm.PackageManager#getServiceInfo(ComponentName, int)} or by
+ * combining state from from com.android.server.pm.pkg.PackageState and
+ * {@link FrameworkPackageUserState}.
+ *
* @see ServiceInfo
* @see PackageInfo#services
*/
diff --git a/core/java/android/content/pm/parsing/component/ComponentParseUtils.java b/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
index 012a542..1ac9739 100644
--- a/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
+++ b/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
@@ -28,7 +28,7 @@
import android.content.pm.parsing.ParsingUtils;
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseResult;
-import android.content.pm.pkg.PackageUserState;
+import android.content.pm.pkg.FrameworkPackageUserState;
import android.content.pm.pkg.PackageUserStateUtils;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -169,13 +169,13 @@
return component.getIcon();
}
- public static boolean isMatch(PackageUserState state, boolean isSystem,
+ public static boolean isMatch(FrameworkPackageUserState state, boolean isSystem,
boolean isPackageEnabled, ParsedMainComponent component, int flags) {
return PackageUserStateUtils.isMatch(state, isSystem, isPackageEnabled,
component.isEnabled(), component.isDirectBootAware(), component.getName(), flags);
}
- public static boolean isEnabled(PackageUserState state, boolean isPackageEnabled,
+ public static boolean isEnabled(FrameworkPackageUserState state, boolean isPackageEnabled,
ParsedMainComponent parsedComponent, int flags) {
return PackageUserStateUtils.isEnabled(state, isPackageEnabled, parsedComponent.isEnabled(),
parsedComponent.getName(), flags);
diff --git a/core/java/android/content/pm/pkg/FrameworkPackageUserState.java b/core/java/android/content/pm/pkg/FrameworkPackageUserState.java
new file mode 100644
index 0000000..0daf6cf
--- /dev/null
+++ b/core/java/android/content/pm/pkg/FrameworkPackageUserState.java
@@ -0,0 +1,87 @@
+/*
+ * 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.content.pm.pkg;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageManager;
+import android.content.pm.overlay.OverlayPaths;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A framework copy of the services PackageUserState, which acts as compatibility layer for existing
+ * usages of PackageUserState in the framework. One day this can hopefully be removed.
+ *
+ * See the services variant for method documentation.
+ *
+ * @hide
+ * @deprecated Unless you know exactly what you're doing, you probably want the services variant.
+ */
+public interface FrameworkPackageUserState {
+
+ FrameworkPackageUserState DEFAULT = new FrameworkPackageUserStateDefault();
+
+ @Nullable
+ OverlayPaths getAllOverlayPaths();
+
+ long getCeDataInode();
+
+ @NonNull
+ Set<String> getDisabledComponents();
+
+ @PackageManager.DistractionRestriction
+ int getDistractionFlags();
+
+ @NonNull
+ Set<String> getEnabledComponents();
+
+ @PackageManager.EnabledState
+ int getEnabledState();
+
+ @Nullable
+ String getHarmfulAppWarning();
+
+ @PackageManager.InstallReason
+ int getInstallReason();
+
+ @Nullable
+ String getLastDisableAppCaller();
+
+ @Nullable
+ OverlayPaths getOverlayPaths();
+
+ @NonNull
+ Map<String, OverlayPaths> getSharedLibraryOverlayPaths();
+
+ @PackageManager.UninstallReason
+ int getUninstallReason();
+
+ boolean isComponentEnabled(@NonNull String componentName);
+ boolean isComponentDisabled(@NonNull String componentName);
+ boolean isHidden();
+ boolean isInstalled();
+ boolean isInstantApp();
+ boolean isNotLaunched();
+ boolean isStopped();
+ boolean isSuspended();
+ boolean isVirtualPreload();
+
+ @Nullable
+ String getSplashScreenTheme();
+}
diff --git a/core/java/android/content/pm/pkg/PackageUserStateDefault.java b/core/java/android/content/pm/pkg/FrameworkPackageUserStateDefault.java
similarity index 96%
rename from core/java/android/content/pm/pkg/PackageUserStateDefault.java
rename to core/java/android/content/pm/pkg/FrameworkPackageUserStateDefault.java
index 6bee8c8..27255da 100644
--- a/core/java/android/content/pm/pkg/PackageUserStateDefault.java
+++ b/core/java/android/content/pm/pkg/FrameworkPackageUserStateDefault.java
@@ -25,7 +25,8 @@
import java.util.Map;
import java.util.Set;
-class PackageUserStateDefault implements PackageUserState {
+/** @hide */
+class FrameworkPackageUserStateDefault implements FrameworkPackageUserState {
@Override
public int getEnabledState() {
@@ -139,6 +140,7 @@
return false;
}
+ @Nullable
@Override
public OverlayPaths getAllOverlayPaths() {
return null;
diff --git a/core/java/android/content/pm/pkg/PackageUserState.java b/core/java/android/content/pm/pkg/PackageUserState.java
deleted file mode 100644
index e0d1eea..0000000
--- a/core/java/android/content/pm/pkg/PackageUserState.java
+++ /dev/null
@@ -1,262 +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 android.content.pm.pkg;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.content.pm.PackageManager;
-import android.content.pm.SuspendDialogInfo;
-import android.content.pm.overlay.OverlayPaths;
-import android.os.BaseBundle;
-import android.os.PersistableBundle;
-import android.util.Slog;
-import android.util.TypedXmlPullParser;
-import android.util.TypedXmlSerializer;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.IOException;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * The API surface for a {@link PackageUserStateImpl}. Methods are expected to return
- * immutable objects. This may mean copying data on each invocation until related classes are
- * refactored to be immutable.
- * <p>
- * TODO: Replace implementation usage with the interface. Currently the name overlap is intentional.
- * <p>
- *
- * @hide
- */
-// TODO(b/173807334): Expose API
-//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
-public interface PackageUserState {
-
- PackageUserState DEFAULT = new PackageUserStateDefault();
-
- /**
- * {@link #getOverlayPaths()} but also include shared library overlay paths.
- */
- @Nullable
- OverlayPaths getAllOverlayPaths();
-
- /**
- * Credential encrypted /data partition inode.
- */
- long getCeDataInode();
-
- @NonNull
- Set<String> getDisabledComponents();
-
- @PackageManager.DistractionRestriction
- int getDistractionFlags();
-
- @NonNull
- Set<String> getEnabledComponents();
-
- int getEnabledState();
-
- @Nullable
- String getHarmfulAppWarning();
-
- @PackageManager.InstallReason
- int getInstallReason();
-
- @Nullable
- String getLastDisableAppCaller();
-
- @Nullable
- OverlayPaths getOverlayPaths();
-
- @NonNull
- Map<String, OverlayPaths> getSharedLibraryOverlayPaths();
-
- @PackageManager.UninstallReason
- int getUninstallReason();
-
- boolean isComponentEnabled(@NonNull String componentName);
-
- boolean isComponentDisabled(@NonNull String componentName);
-
- boolean isHidden();
-
- boolean isInstalled();
-
- boolean isInstantApp();
-
- boolean isNotLaunched();
-
- boolean isStopped();
-
- boolean isSuspended();
-
- boolean isVirtualPreload();
-
- @Nullable
- String getSplashScreenTheme();
-
- /**
- * Container to describe suspension parameters.
- */
- final class SuspendParams {
-
- private static final String LOG_TAG = "PackageUserState";
- private static final String TAG_DIALOG_INFO = "dialog-info";
- private static final String TAG_APP_EXTRAS = "app-extras";
- private static final String TAG_LAUNCHER_EXTRAS = "launcher-extras";
-
- public SuspendDialogInfo dialogInfo;
- public PersistableBundle appExtras;
- public PersistableBundle launcherExtras;
-
- private SuspendParams() {
- }
-
- /**
- * Returns a {@link SuspendParams} object with the given fields. Returns {@code null} if all
- * the fields are {@code null}.
- *
- * @param dialogInfo
- * @param appExtras
- * @param launcherExtras
- * @return A {@link SuspendParams} object or {@code null}.
- */
- public static SuspendParams getInstanceOrNull(SuspendDialogInfo dialogInfo,
- PersistableBundle appExtras, PersistableBundle launcherExtras) {
- if (dialogInfo == null && appExtras == null && launcherExtras == null) {
- return null;
- }
- final SuspendParams instance = new SuspendParams();
- instance.dialogInfo = dialogInfo;
- instance.appExtras = appExtras;
- instance.launcherExtras = launcherExtras;
- return instance;
- }
-
- @Override
- public boolean equals(@Nullable Object obj) {
- if (this == obj) {
- return true;
- }
- if (!(obj instanceof SuspendParams)) {
- return false;
- }
- final SuspendParams other = (SuspendParams) obj;
- if (!Objects.equals(dialogInfo, other.dialogInfo)) {
- return false;
- }
- if (!BaseBundle.kindofEquals(appExtras, other.appExtras)) {
- return false;
- }
- if (!BaseBundle.kindofEquals(launcherExtras, other.launcherExtras)) {
- return false;
- }
- return true;
- }
-
- @Override
- public int hashCode() {
- int hashCode = Objects.hashCode(dialogInfo);
- hashCode = 31 * hashCode + ((appExtras != null) ? appExtras.size() : 0);
- hashCode = 31 * hashCode + ((launcherExtras != null) ? launcherExtras.size() : 0);
- return hashCode;
- }
-
- /**
- * Serializes this object into an xml format
- * @param out the {@link XmlSerializer} object
- * @throws IOException
- */
- public void saveToXml(TypedXmlSerializer out) throws IOException {
- if (dialogInfo != null) {
- out.startTag(null, TAG_DIALOG_INFO);
- dialogInfo.saveToXml(out);
- out.endTag(null, TAG_DIALOG_INFO);
- }
- if (appExtras != null) {
- out.startTag(null, TAG_APP_EXTRAS);
- try {
- appExtras.saveToXml(out);
- } catch (XmlPullParserException e) {
- Slog.e(LOG_TAG, "Exception while trying to write appExtras."
- + " Will be lost on reboot", e);
- }
- out.endTag(null, TAG_APP_EXTRAS);
- }
- if (launcherExtras != null) {
- out.startTag(null, TAG_LAUNCHER_EXTRAS);
- try {
- launcherExtras.saveToXml(out);
- } catch (XmlPullParserException e) {
- Slog.e(LOG_TAG, "Exception while trying to write launcherExtras."
- + " Will be lost on reboot", e);
- }
- out.endTag(null, TAG_LAUNCHER_EXTRAS);
- }
- }
-
- /**
- * Parses this object from the xml format. Returns {@code null} if no object related
- * information could be read.
- * @param in the reader
- * @return
- */
- public static SuspendParams restoreFromXml(TypedXmlPullParser in) throws IOException {
- SuspendDialogInfo readDialogInfo = null;
- PersistableBundle readAppExtras = null;
- PersistableBundle readLauncherExtras = null;
-
- final int currentDepth = in.getDepth();
- int type;
- try {
- while ((type = in.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || in.getDepth() > currentDepth)) {
- if (type == XmlPullParser.END_TAG
- || type == XmlPullParser.TEXT) {
- continue;
- }
- switch (in.getName()) {
- case TAG_DIALOG_INFO:
- readDialogInfo = SuspendDialogInfo.restoreFromXml(in);
- break;
- case TAG_APP_EXTRAS:
- readAppExtras = PersistableBundle.restoreFromXml(in);
- break;
- case TAG_LAUNCHER_EXTRAS:
- readLauncherExtras = PersistableBundle.restoreFromXml(in);
- break;
- default:
- Slog.w(LOG_TAG, "Unknown tag " + in.getName()
- + " in SuspendParams. Ignoring");
- break;
- }
- }
- } catch (XmlPullParserException e) {
- Slog.e(LOG_TAG, "Exception while trying to parse SuspendParams,"
- + " some fields may default", e);
- }
- return getInstanceOrNull(readDialogInfo, readAppExtras, readLauncherExtras);
- }
- }
-}
diff --git a/core/java/android/content/pm/pkg/PackageUserStateImpl.java b/core/java/android/content/pm/pkg/PackageUserStateImpl.java
deleted file mode 100644
index 4d1b236..0000000
--- a/core/java/android/content/pm/pkg/PackageUserStateImpl.java
+++ /dev/null
@@ -1,429 +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 android.content.pm.pkg;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.pm.PackageManager;
-import android.content.pm.overlay.OverlayPaths;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.DataClass;
-
-import java.util.Map;
-import java.util.Set;
-
-/** @hide */
-@DataClass(genConstructor = false, genBuilder = false, genEqualsHashCode = true)
-@DataClass.Suppress({"mOverlayPathsLock", "mOverlayPaths", "mSharedLibraryOverlayPathsLock",
- "mSharedLibraryOverlayPaths", "setOverlayPaths"})
-public class PackageUserStateImpl implements PackageUserState {
-
- @Nullable
- protected ArraySet<String> mDisabledComponents;
- @Nullable
- protected ArraySet<String> mEnabledComponents;
-
- private long mCeDataInode;
- private boolean mInstalled = true;
- private boolean mStopped;
- private boolean mNotLaunched;
- private boolean mHidden; // Is the app restricted by owner / admin
- private int mDistractionFlags;
- private boolean mSuspended;
- private boolean mInstantApp;
- private boolean mVirtualPreload;
- private int mEnabledState = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
- @PackageManager.InstallReason
- private int mInstallReason = PackageManager.INSTALL_REASON_UNKNOWN;
- @PackageManager.UninstallReason
- private int mUninstallReason = PackageManager.UNINSTALL_REASON_UNKNOWN;
- @Nullable
- private String mHarmfulAppWarning;
- @Nullable
- private String mLastDisableAppCaller;
-
- @Nullable
- protected OverlayPaths mOverlayPaths;
-
- // Lib name to overlay paths
- @Nullable
- protected ArrayMap<String, OverlayPaths> mSharedLibraryOverlayPaths;
-
- @Nullable
- private String mSplashScreenTheme;
-
- public PackageUserStateImpl() {
- }
-
- @VisibleForTesting
- public PackageUserStateImpl(PackageUserStateImpl other) {
- mDisabledComponents = ArrayUtils.cloneOrNull(other.mDisabledComponents);
- mEnabledComponents = ArrayUtils.cloneOrNull(other.mEnabledComponents);
- mOverlayPaths = other.mOverlayPaths;
- if (other.mSharedLibraryOverlayPaths != null) {
- mSharedLibraryOverlayPaths = new ArrayMap<>(other.mSharedLibraryOverlayPaths);
- }
- this.mDisabledComponents = other.mDisabledComponents;
- this.mEnabledComponents = other.mEnabledComponents;
- this.mCeDataInode = other.mCeDataInode;
- this.mInstalled = other.mInstalled;
- this.mStopped = other.mStopped;
- this.mNotLaunched = other.mNotLaunched;
- this.mHidden = other.mHidden;
- this.mDistractionFlags = other.mDistractionFlags;
- this.mSuspended = other.mSuspended;
- this.mInstantApp = other.mInstantApp;
- this.mVirtualPreload = other.mVirtualPreload;
- this.mEnabledState = other.mEnabledState;
- this.mInstallReason = other.mInstallReason;
- this.mUninstallReason = other.mUninstallReason;
- this.mHarmfulAppWarning = other.mHarmfulAppWarning;
- this.mLastDisableAppCaller = other.mLastDisableAppCaller;
- this.mOverlayPaths = other.mOverlayPaths;
- this.mSharedLibraryOverlayPaths = other.mSharedLibraryOverlayPaths;
- this.mSplashScreenTheme = other.mSplashScreenTheme;
- }
-
- @Override
- public boolean isComponentEnabled(String componentName) {
- // TODO: Not locked
- return ArrayUtils.contains(mEnabledComponents, componentName);
- }
-
- @Override
- public boolean isComponentDisabled(String componentName) {
- // TODO: Not locked
- return ArrayUtils.contains(mDisabledComponents, componentName);
- }
-
- @Override
- public OverlayPaths getAllOverlayPaths() {
- if (mOverlayPaths == null && mSharedLibraryOverlayPaths == null) {
- return null;
- }
- final OverlayPaths.Builder newPaths = new OverlayPaths.Builder();
- newPaths.addAll(mOverlayPaths);
- if (mSharedLibraryOverlayPaths != null) {
- for (final OverlayPaths libOverlayPaths : mSharedLibraryOverlayPaths.values()) {
- newPaths.addAll(libOverlayPaths);
- }
- }
- return newPaths.build();
- }
-
-
-
- // Code below generated by codegen v1.0.23.
- //
- // DO NOT MODIFY!
- // CHECKSTYLE:OFF Generated code
- //
- // To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/pkg/PackageUserStateImpl.java
- //
- // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
- // Settings > Editor > Code Style > Formatter Control
- //@formatter:off
-
-
- @DataClass.Generated.Member
- public @Nullable ArraySet<String> getDisabledComponents() {
- return mDisabledComponents;
- }
-
- @DataClass.Generated.Member
- public @Nullable ArraySet<String> getEnabledComponents() {
- return mEnabledComponents;
- }
-
- @DataClass.Generated.Member
- public long getCeDataInode() {
- return mCeDataInode;
- }
-
- @DataClass.Generated.Member
- public boolean isInstalled() {
- return mInstalled;
- }
-
- @DataClass.Generated.Member
- public boolean isStopped() {
- return mStopped;
- }
-
- @DataClass.Generated.Member
- public boolean isNotLaunched() {
- return mNotLaunched;
- }
-
- @DataClass.Generated.Member
- public boolean isHidden() {
- return mHidden;
- }
-
- @DataClass.Generated.Member
- public int getDistractionFlags() {
- return mDistractionFlags;
- }
-
- @DataClass.Generated.Member
- public boolean isSuspended() {
- return mSuspended;
- }
-
- @DataClass.Generated.Member
- public boolean isInstantApp() {
- return mInstantApp;
- }
-
- @DataClass.Generated.Member
- public boolean isVirtualPreload() {
- return mVirtualPreload;
- }
-
- @DataClass.Generated.Member
- public int getEnabledState() {
- return mEnabledState;
- }
-
- @DataClass.Generated.Member
- public @PackageManager.InstallReason int getInstallReason() {
- return mInstallReason;
- }
-
- @DataClass.Generated.Member
- public @PackageManager.UninstallReason int getUninstallReason() {
- return mUninstallReason;
- }
-
- @DataClass.Generated.Member
- public @Nullable String getHarmfulAppWarning() {
- return mHarmfulAppWarning;
- }
-
- @DataClass.Generated.Member
- public @Nullable String getLastDisableAppCaller() {
- return mLastDisableAppCaller;
- }
-
- @DataClass.Generated.Member
- public @Nullable OverlayPaths getOverlayPaths() {
- return mOverlayPaths;
- }
-
- @DataClass.Generated.Member
- public @Nullable ArrayMap<String,OverlayPaths> getSharedLibraryOverlayPaths() {
- return mSharedLibraryOverlayPaths;
- }
-
- @DataClass.Generated.Member
- public @Nullable String getSplashScreenTheme() {
- return mSplashScreenTheme;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setDisabledComponents(@NonNull ArraySet<String> value) {
- mDisabledComponents = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setEnabledComponents(@NonNull ArraySet<String> value) {
- mEnabledComponents = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setCeDataInode( long value) {
- mCeDataInode = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setInstalled( boolean value) {
- mInstalled = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setStopped( boolean value) {
- mStopped = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setNotLaunched( boolean value) {
- mNotLaunched = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setHidden( boolean value) {
- mHidden = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setDistractionFlags( int value) {
- mDistractionFlags = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setSuspended( boolean value) {
- mSuspended = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setInstantApp( boolean value) {
- mInstantApp = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setVirtualPreload( boolean value) {
- mVirtualPreload = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setEnabledState( int value) {
- mEnabledState = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setInstallReason(@PackageManager.InstallReason int value) {
- mInstallReason = value;
- com.android.internal.util.AnnotationValidations.validate(
- PackageManager.InstallReason.class, null, mInstallReason);
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setUninstallReason(@PackageManager.UninstallReason int value) {
- mUninstallReason = value;
- com.android.internal.util.AnnotationValidations.validate(
- PackageManager.UninstallReason.class, null, mUninstallReason);
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setHarmfulAppWarning(@NonNull String value) {
- mHarmfulAppWarning = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setLastDisableAppCaller(@NonNull String value) {
- mLastDisableAppCaller = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setSharedLibraryOverlayPaths(@NonNull ArrayMap<String,OverlayPaths> value) {
- mSharedLibraryOverlayPaths = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setSplashScreenTheme(@NonNull String value) {
- mSplashScreenTheme = value;
- return this;
- }
-
- @Override
- @DataClass.Generated.Member
- public boolean equals(@Nullable Object o) {
- // You can override field equality logic by defining either of the methods like:
- // boolean fieldNameEquals(PackageUserStateImpl other) { ... }
- // boolean fieldNameEquals(FieldType otherValue) { ... }
-
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- @SuppressWarnings("unchecked")
- PackageUserStateImpl that = (PackageUserStateImpl) o;
- //noinspection PointlessBooleanExpression
- return true
- && java.util.Objects.equals(mDisabledComponents, that.mDisabledComponents)
- && java.util.Objects.equals(mEnabledComponents, that.mEnabledComponents)
- && mCeDataInode == that.mCeDataInode
- && mInstalled == that.mInstalled
- && mStopped == that.mStopped
- && mNotLaunched == that.mNotLaunched
- && mHidden == that.mHidden
- && mDistractionFlags == that.mDistractionFlags
- && mSuspended == that.mSuspended
- && mInstantApp == that.mInstantApp
- && mVirtualPreload == that.mVirtualPreload
- && mEnabledState == that.mEnabledState
- && mInstallReason == that.mInstallReason
- && mUninstallReason == that.mUninstallReason
- && java.util.Objects.equals(mHarmfulAppWarning, that.mHarmfulAppWarning)
- && java.util.Objects.equals(mLastDisableAppCaller, that.mLastDisableAppCaller)
- && java.util.Objects.equals(mOverlayPaths, that.mOverlayPaths)
- && java.util.Objects.equals(mSharedLibraryOverlayPaths, that.mSharedLibraryOverlayPaths)
- && java.util.Objects.equals(mSplashScreenTheme, that.mSplashScreenTheme);
- }
-
- @Override
- @DataClass.Generated.Member
- public int hashCode() {
- // You can override field hashCode logic by defining methods like:
- // int fieldNameHashCode() { ... }
-
- int _hash = 1;
- _hash = 31 * _hash + java.util.Objects.hashCode(mDisabledComponents);
- _hash = 31 * _hash + java.util.Objects.hashCode(mEnabledComponents);
- _hash = 31 * _hash + Long.hashCode(mCeDataInode);
- _hash = 31 * _hash + Boolean.hashCode(mInstalled);
- _hash = 31 * _hash + Boolean.hashCode(mStopped);
- _hash = 31 * _hash + Boolean.hashCode(mNotLaunched);
- _hash = 31 * _hash + Boolean.hashCode(mHidden);
- _hash = 31 * _hash + mDistractionFlags;
- _hash = 31 * _hash + Boolean.hashCode(mSuspended);
- _hash = 31 * _hash + Boolean.hashCode(mInstantApp);
- _hash = 31 * _hash + Boolean.hashCode(mVirtualPreload);
- _hash = 31 * _hash + mEnabledState;
- _hash = 31 * _hash + mInstallReason;
- _hash = 31 * _hash + mUninstallReason;
- _hash = 31 * _hash + java.util.Objects.hashCode(mHarmfulAppWarning);
- _hash = 31 * _hash + java.util.Objects.hashCode(mLastDisableAppCaller);
- _hash = 31 * _hash + java.util.Objects.hashCode(mOverlayPaths);
- _hash = 31 * _hash + java.util.Objects.hashCode(mSharedLibraryOverlayPaths);
- _hash = 31 * _hash + java.util.Objects.hashCode(mSplashScreenTheme);
- return _hash;
- }
-
- @DataClass.Generated(
- time = 1633391914126L,
- codegenVersion = "1.0.23",
- sourceFile = "frameworks/base/core/java/android/content/pm/pkg/PackageUserStateImpl.java",
- inputSignatures = "protected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mDisabledComponents\nprotected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate long mCeDataInode\nprivate boolean mInstalled\nprivate boolean mStopped\nprivate boolean mNotLaunched\nprivate boolean mHidden\nprivate int mDistractionFlags\nprivate boolean mSuspended\nprivate boolean mInstantApp\nprivate boolean mVirtualPreload\nprivate int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprotected @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\nclass PackageUserStateImpl extends java.lang.Object implements [android.content.pm.pkg.PackageUserState]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
- @Deprecated
- private void __metadata() {}
-
-
- //@formatter:on
- // End of generated code
-
-}
diff --git a/core/java/android/content/pm/pkg/PackageUserStateInternalDefault.java b/core/java/android/content/pm/pkg/PackageUserStateInternalDefault.java
deleted file mode 100644
index b72ed13..0000000
--- a/core/java/android/content/pm/pkg/PackageUserStateInternalDefault.java
+++ /dev/null
@@ -1,53 +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.content.pm.pkg;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Pair;
-
-class PackageUserStateInternalDefault extends PackageUserStateDefault implements
- PackageUserStateInternal {
-
- @Nullable
- @Override
- public ArrayMap<String, SuspendParams> getSuspendParams() {
- return null;
- }
-
- @Nullable
- @Override
- public ArraySet<String> getDisabledComponentsNoCopy() {
- return null;
- }
-
- @Nullable
- @Override
- public ArraySet<String> getEnabledComponentsNoCopy() {
- return null;
- }
-
- @Nullable
- @Override
- public Pair<String, Integer> getOverrideLabelIconForComponent(
- @NonNull ComponentName componentName) {
- return null;
- }
-}
diff --git a/core/java/android/content/pm/pkg/PackageUserStateUtils.java b/core/java/android/content/pm/pkg/PackageUserStateUtils.java
index e46a10c..9a800b0 100644
--- a/core/java/android/content/pm/pkg/PackageUserStateUtils.java
+++ b/core/java/android/content/pm/pkg/PackageUserStateUtils.java
@@ -22,7 +22,6 @@
import android.annotation.NonNull;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
-import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.ParsingPackageRead;
import android.content.pm.parsing.component.ParsedMainComponent;
import android.os.Debug;
@@ -35,14 +34,14 @@
private static final boolean DEBUG = false;
private static final String TAG = "PackageUserStateUtils";
- public static boolean isMatch(@NonNull PackageUserState state, ComponentInfo componentInfo,
+ public static boolean isMatch(@NonNull FrameworkPackageUserState state, ComponentInfo componentInfo,
int flags) {
return isMatch(state, componentInfo.applicationInfo.isSystemApp(),
componentInfo.applicationInfo.enabled, componentInfo.enabled,
componentInfo.directBootAware, componentInfo.name, flags);
}
- public static boolean isMatch(@NonNull PackageUserState state, boolean isSystem,
+ public static boolean isMatch(@NonNull FrameworkPackageUserState state, boolean isSystem,
boolean isPackageEnabled, ParsedMainComponent component, int flags) {
return isMatch(state, isSystem, isPackageEnabled, component.isEnabled(),
component.isDirectBootAware(), component.getName(), flags);
@@ -57,7 +56,7 @@
* PackageManager#MATCH_DIRECT_BOOT_UNAWARE} are specified in {@code flags}.
* </p>
*/
- public static boolean isMatch(@NonNull PackageUserState state, boolean isSystem,
+ public static boolean isMatch(@NonNull FrameworkPackageUserState state, boolean isSystem,
boolean isPackageEnabled, boolean isComponentEnabled,
boolean isComponentDirectBootAware, String componentName, int flags) {
final boolean matchUninstalled = (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0;
@@ -82,7 +81,7 @@
return reportIfDebug(matchesUnaware || matchesAware, flags);
}
- public static boolean isAvailable(@NonNull PackageUserState state, int flags) {
+ public static boolean isAvailable(@NonNull FrameworkPackageUserState state, int flags) {
// True if it is installed for this user and it is not hidden. If it is hidden,
// still return true if the caller requested MATCH_UNINSTALLED_PACKAGES
final boolean matchAnyUser = (flags & PackageManager.MATCH_ANY_USER) != 0;
@@ -101,13 +100,13 @@
return result;
}
- public static boolean isEnabled(@NonNull PackageUserState state, ComponentInfo componentInfo,
+ public static boolean isEnabled(@NonNull FrameworkPackageUserState state, ComponentInfo componentInfo,
int flags) {
return isEnabled(state, componentInfo.applicationInfo.enabled, componentInfo.enabled,
componentInfo.name, flags);
}
- public static boolean isEnabled(@NonNull PackageUserState state, boolean isPackageEnabled,
+ public static boolean isEnabled(@NonNull FrameworkPackageUserState state, boolean isPackageEnabled,
ParsedMainComponent parsedComponent, int flags) {
return isEnabled(state, isPackageEnabled, parsedComponent.isEnabled(),
parsedComponent.getName(), flags);
@@ -116,7 +115,7 @@
/**
* Test if the given component is considered enabled.
*/
- public static boolean isEnabled(@NonNull PackageUserState state, boolean isPackageEnabled,
+ public static boolean isEnabled(@NonNull FrameworkPackageUserState state, boolean isPackageEnabled,
boolean isComponentEnabled, String componentName, int flags) {
if ((flags & MATCH_DISABLED_COMPONENTS) != 0) {
return true;
@@ -153,7 +152,7 @@
return isComponentEnabled;
}
- public static boolean isPackageEnabled(@NonNull PackageUserState state,
+ public static boolean isPackageEnabled(@NonNull FrameworkPackageUserState state,
@NonNull ParsingPackageRead pkg) {
switch (state.getEnabledState()) {
case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
diff --git a/core/java/android/hardware/biometrics/BiometricOverlayConstants.java b/core/java/android/hardware/biometrics/BiometricOverlayConstants.java
index 603b06d..065ae64a 100644
--- a/core/java/android/hardware/biometrics/BiometricOverlayConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricOverlayConstants.java
@@ -38,13 +38,16 @@
int REASON_AUTH_KEYGUARD = 4;
/** Non-specific usage (from FingerprintManager). */
int REASON_AUTH_OTHER = 5;
+ /** Usage from Settings. */
+ int REASON_AUTH_SETTINGS = 6;
@IntDef({REASON_UNKNOWN,
REASON_ENROLL_FIND_SENSOR,
REASON_ENROLL_ENROLLING,
REASON_AUTH_BP,
REASON_AUTH_KEYGUARD,
- REASON_AUTH_OTHER})
+ REASON_AUTH_OTHER,
+ REASON_AUTH_SETTINGS})
@Retention(RetentionPolicy.SOURCE)
@interface ShowReason {}
}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index afea4e5..96a18dc 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1325,7 +1325,7 @@
* flashlight brightness level via
* {android.hardware.camera2.CameraManager#setTorchStrengthLevel}.
* If this value is equal to 1, flashlight brightness control is not supported.
- * This value will be -1 if the flash unit is not available.</p>
+ * The value for this key will be null for devices with no flash unit.</p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
*/
@PublicKey
@@ -1337,14 +1337,15 @@
* <p>Default flashlight brightness level to be set via
* {android.hardware.camera2.CameraManager#setTorchStrengthLevel}.</p>
* <p>If flash unit is available this will be greater than or equal to 1 and less
- * or equal to <code>{@link CameraCharacteristics#FLASH_INFO_STRENGTH_MAXIMUM_LEVEL android.flash.info.strengthMaximumLevel}</code>.
- * If flash unit is not available this will be set to -1.</p>
+ * or equal to <code>{@link CameraCharacteristics#FLASH_INFO_STRENGTH_MAXIMUM_LEVEL android.flash.info.strengthMaximumLevel}</code>.</p>
* <p>Setting flashlight brightness above the default level
* (i.e.<code>{@link CameraCharacteristics#FLASH_INFO_STRENGTH_DEFAULT_LEVEL android.flash.info.strengthDefaultLevel}</code>) may make the device more
* likely to reach thermal throttling conditions and slow down, or drain the
* battery quicker than normal. To minimize such issues, it is recommended to
* start the flashlight at this default brightness until a user explicitly requests
- * a brighter level.</p>
+ * a brighter level.
+ * Note that the value for this key will be null for devices with no flash unit.
+ * The default level should always be > 0.</p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
*
* @see CameraCharacteristics#FLASH_INFO_STRENGTH_DEFAULT_LEVEL
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 4b35294..639abe9 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -2657,6 +2657,18 @@
*/
public static final int CONTROL_VIDEO_STABILIZATION_MODE_ON = 1;
+ /**
+ * <p>Preview stabilization, where the preview in addition to all other non-RAW streams are
+ * stabilized with the same quality of stabilization, is enabled. This mode aims to give
+ * clients a 'what you see is what you get' effect. In this mode, the FoV reduction will
+ * be a maximum of 20 % both horizontally and vertically
+ * (10% from left, right, top, bottom) for the given zoom ratio / crop region.
+ * The resultant FoV will also be the same across all processed streams
+ * (that have the same aspect ratio).</p>
+ * @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE
+ */
+ public static final int CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION = 2;
+
//
// Enumeration values for CaptureRequest#CONTROL_EXTENDED_SCENE_MODE
//
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 08276ac..86ae3a3 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -2081,10 +2081,20 @@
* ({@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode}), turning both modes on may
* produce undesirable interaction, so it is recommended not to enable
* both at the same time.</p>
+ * <p>If video stabilization is set to "PREVIEW_STABILIZATION",
+ * {@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode} is overridden. The camera sub-system may choose
+ * to turn on hardware based image stabilization in addition to software based stabilization
+ * if it deems that appropriate.
+ * This key may be a part of the available session keys, which camera clients may
+ * query via
+ * {@link android.hardware.camera2.CameraCharacteristics#getAvailableSessionKeys }.
+ * If this is the case, changing this key over the life-time of a capture session may
+ * cause delays / glitches.</p>
* <p><b>Possible values:</b></p>
* <ul>
* <li>{@link #CONTROL_VIDEO_STABILIZATION_MODE_OFF OFF}</li>
* <li>{@link #CONTROL_VIDEO_STABILIZATION_MODE_ON ON}</li>
+ * <li>{@link #CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION PREVIEW_STABILIZATION}</li>
* </ul>
*
* <p>This key is available on all devices.</p>
@@ -2094,6 +2104,7 @@
* @see CaptureRequest#SCALER_CROP_REGION
* @see #CONTROL_VIDEO_STABILIZATION_MODE_OFF
* @see #CONTROL_VIDEO_STABILIZATION_MODE_ON
+ * @see #CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION
*/
@PublicKey
@NonNull
@@ -2785,6 +2796,11 @@
* <p>If a camera device supports both OIS and digital image stabilization
* ({@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode}), turning both modes on may produce undesirable
* interaction, so it is recommended not to enable both at the same time.</p>
+ * <p>If {@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode} is set to "PREVIEW_STABILIZATION",
+ * {@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode} is overridden. The camera sub-system may choose
+ * to turn on hardware based image stabilization in addition to software based stabilization
+ * if it deems that appropriate. This key's value in the capture result will reflect which
+ * OIS mode was chosen.</p>
* <p>Not all devices will support OIS; see
* {@link CameraCharacteristics#LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION android.lens.info.availableOpticalStabilization} for
* available controls.</p>
@@ -2804,6 +2820,7 @@
* @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
* @see CameraCharacteristics#LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION
+ * @see CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE
* @see #LENS_OPTICAL_STABILIZATION_MODE_OFF
* @see #LENS_OPTICAL_STABILIZATION_MODE_ON
*/
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 296bfbe..a0fb179 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2335,10 +2335,20 @@
* ({@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode}), turning both modes on may
* produce undesirable interaction, so it is recommended not to enable
* both at the same time.</p>
+ * <p>If video stabilization is set to "PREVIEW_STABILIZATION",
+ * {@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode} is overridden. The camera sub-system may choose
+ * to turn on hardware based image stabilization in addition to software based stabilization
+ * if it deems that appropriate.
+ * This key may be a part of the available session keys, which camera clients may
+ * query via
+ * {@link android.hardware.camera2.CameraCharacteristics#getAvailableSessionKeys }.
+ * If this is the case, changing this key over the life-time of a capture session may
+ * cause delays / glitches.</p>
* <p><b>Possible values:</b></p>
* <ul>
* <li>{@link #CONTROL_VIDEO_STABILIZATION_MODE_OFF OFF}</li>
* <li>{@link #CONTROL_VIDEO_STABILIZATION_MODE_ON ON}</li>
+ * <li>{@link #CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION PREVIEW_STABILIZATION}</li>
* </ul>
*
* <p>This key is available on all devices.</p>
@@ -2348,6 +2358,7 @@
* @see CaptureRequest#SCALER_CROP_REGION
* @see #CONTROL_VIDEO_STABILIZATION_MODE_OFF
* @see #CONTROL_VIDEO_STABILIZATION_MODE_ON
+ * @see #CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION
*/
@PublicKey
@NonNull
@@ -3072,6 +3083,11 @@
* <p>If a camera device supports both OIS and digital image stabilization
* ({@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode}), turning both modes on may produce undesirable
* interaction, so it is recommended not to enable both at the same time.</p>
+ * <p>If {@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode} is set to "PREVIEW_STABILIZATION",
+ * {@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode} is overridden. The camera sub-system may choose
+ * to turn on hardware based image stabilization in addition to software based stabilization
+ * if it deems that appropriate. This key's value in the capture result will reflect which
+ * OIS mode was chosen.</p>
* <p>Not all devices will support OIS; see
* {@link CameraCharacteristics#LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION android.lens.info.availableOpticalStabilization} for
* available controls.</p>
@@ -3091,6 +3107,7 @@
* @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
* @see CameraCharacteristics#LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION
+ * @see CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE
* @see #LENS_OPTICAL_STABILIZATION_MODE_OFF
* @see #LENS_OPTICAL_STABILIZATION_MODE_ON
*/
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 74506da..ee24084 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -777,8 +777,8 @@
}
/**
- * Examine the given template and normalize if it refers to a "merged"
- * mobile subscriber. We pick the "lowest" merged subscriber as the primary
+ * Examine the given template and normalize it.
+ * We pick the "lowest" merged subscriber as the primary
* for key purposes, and expand the template to match all other merged
* subscribers.
* <p>
@@ -793,8 +793,8 @@
}
/**
- * Examine the given template and normalize if it refers to a "merged"
- * mobile subscriber. We pick the "lowest" merged subscriber as the primary
+ * Examine the given template and normalize it.
+ * We pick the "lowest" merged subscriber as the primary
* for key purposes, and expand the template to match all other merged
* subscribers.
*
@@ -806,7 +806,12 @@
* A, but also matches B.
*/
public static NetworkTemplate normalize(NetworkTemplate template, List<String[]> mergedList) {
- if (!template.isMatchRuleMobile()) return template;
+ // Now there are several types of network which uses SubscriberId to store network
+ // information. For instances:
+ // The TYPE_WIFI with subscriberId means that it is a merged carrier wifi network.
+ // The TYPE_CARRIER means that the network associate to specific carrier network.
+
+ if (template.mSubscriberId == null) return template;
for (String[] merged : mergedList) {
if (ArrayUtils.contains(merged, template.mSubscriberId)) {
diff --git a/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java b/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
index 09f5283..be57372 100644
--- a/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
+++ b/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
@@ -79,6 +79,7 @@
IKE_OPTIONS.add(IkeSessionParams.IKE_OPTION_EAP_ONLY_AUTH);
IKE_OPTIONS.add(IkeSessionParams.IKE_OPTION_MOBIKE);
IKE_OPTIONS.add(IkeSessionParams.IKE_OPTION_FORCE_PORT_4500);
+ IKE_OPTIONS.add(IkeSessionParams.IKE_OPTION_INITIAL_CONTACT);
}
/** Serializes an IkeSessionParams to a PersistableBundle. */
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 2a344f6..ad3de25 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -103,7 +103,7 @@
* are unparcelled, mParcelledData willbe set to null.
*/
@UnsupportedAppUsage
- Parcel mParcelledData = null;
+ volatile Parcel mParcelledData = null;
/**
* Whether {@link #mParcelledData} was generated by native code or not.
@@ -182,13 +182,56 @@
* @param b a Bundle to be copied.
*/
BaseBundle(BaseBundle b) {
- copyInternal(b, false);
+ this(b, /* deep */ false);
}
/**
- * Special constructor that does not initialize the bundle.
+ * Constructs a {@link BaseBundle} containing a copy of {@code from}.
+ *
+ * @param from The bundle to be copied.
+ * @param deep Whether is a deep or shallow copy.
+ *
+ * @hide
*/
- BaseBundle(boolean doInit) {
+ BaseBundle(BaseBundle from, boolean deep) {
+ synchronized (from) {
+ mClassLoader = from.mClassLoader;
+
+ if (from.mMap != null) {
+ if (!deep) {
+ mMap = new ArrayMap<>(from.mMap);
+ } else {
+ final ArrayMap<String, Object> fromMap = from.mMap;
+ final int n = fromMap.size();
+ mMap = new ArrayMap<>(n);
+ for (int i = 0; i < n; i++) {
+ mMap.append(fromMap.keyAt(i), deepCopyValue(fromMap.valueAt(i)));
+ }
+ }
+ } else {
+ mMap = null;
+ }
+
+ final Parcel parcelledData;
+ if (from.mParcelledData != null) {
+ if (from.isEmptyParcel()) {
+ parcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
+ mParcelledByNative = false;
+ } else {
+ parcelledData = Parcel.obtain();
+ parcelledData.appendFrom(from.mParcelledData, 0,
+ from.mParcelledData.dataSize());
+ parcelledData.setDataPosition(0);
+ mParcelledByNative = from.mParcelledByNative;
+ }
+ } else {
+ parcelledData = null;
+ mParcelledByNative = false;
+ }
+
+ // Keep as last statement to ensure visibility of other fields
+ mParcelledData = parcelledData;
+ }
}
/**
@@ -323,8 +366,8 @@
} else {
mMap.erase();
}
- mParcelledData = null;
mParcelledByNative = false;
+ mParcelledData = null;
return;
}
@@ -358,8 +401,8 @@
if (recycleParcel) {
recycleParcel(parcelledData);
}
- mParcelledData = null;
mParcelledByNative = false;
+ mParcelledData = null;
}
if (DEBUG) {
Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
@@ -501,44 +544,7 @@
mMap.clear();
}
- void copyInternal(BaseBundle from, boolean deep) {
- synchronized (from) {
- if (from.mParcelledData != null) {
- if (from.isEmptyParcel()) {
- mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
- mParcelledByNative = false;
- } else {
- mParcelledData = Parcel.obtain();
- mParcelledData.appendFrom(from.mParcelledData, 0,
- from.mParcelledData.dataSize());
- mParcelledData.setDataPosition(0);
- mParcelledByNative = from.mParcelledByNative;
- }
- } else {
- mParcelledData = null;
- mParcelledByNative = false;
- }
-
- if (from.mMap != null) {
- if (!deep) {
- mMap = new ArrayMap<>(from.mMap);
- } else {
- final ArrayMap<String, Object> fromMap = from.mMap;
- final int N = fromMap.size();
- mMap = new ArrayMap<>(N);
- for (int i = 0; i < N; i++) {
- mMap.append(fromMap.keyAt(i), deepCopyValue(fromMap.valueAt(i)));
- }
- }
- } else {
- mMap = null;
- }
-
- mClassLoader = from.mClassLoader;
- }
- }
-
- Object deepCopyValue(Object value) {
+ private Object deepCopyValue(Object value) {
if (value == null) {
return null;
}
@@ -570,7 +576,7 @@
return value;
}
- ArrayList deepcopyArrayList(ArrayList from) {
+ private ArrayList deepcopyArrayList(ArrayList from) {
final int N = from.size();
ArrayList out = new ArrayList(N);
for (int i=0; i<N; i++) {
@@ -1717,9 +1723,9 @@
if (length < 0) {
throw new RuntimeException("Bad length in parcel: " + length);
} else if (length == 0) {
+ mParcelledByNative = false;
// Empty Bundle or end of data.
mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
- mParcelledByNative = false;
return;
} else if (length % 4 != 0) {
throw new IllegalStateException("Bundle length is not aligned by 4: " + length);
@@ -1757,8 +1763,8 @@
+ ": " + length + " bundle bytes starting at " + offset);
p.setDataPosition(0);
- mParcelledData = p;
mParcelledByNative = isNativeBundle;
+ mParcelledData = p;
}
/** {@hide} */
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index be36027..689c806 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -845,6 +845,12 @@
* Returns cpu active time of an uid.
*/
public abstract long getCpuActiveTime();
+
+ /**
+ * Returns cpu active time of a UID while in the specified process state.
+ */
+ public abstract long getCpuActiveTime(int procState);
+
/**
* Returns cpu times of an uid on each cluster
*/
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index b677b69..b069fb3 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -53,7 +53,7 @@
*
* <p>Most developers will not implement this class directly, instead using the
* <a href="{@docRoot}guide/components/aidl.html">aidl</a> tool to describe the desired
- * interface, having it generate the appropriate Binder subclass. You can,
+ * interface, having it generate the appropriate Binder subclass. You can,
* however, derive directly from Binder to implement your own custom RPC
* protocol or simply instantiate a raw Binder object directly to use as a
* token that can be shared across processes.
@@ -63,17 +63,17 @@
* To use this correctly, you must be doing so within the context of a top-level
* application component (a {@link android.app.Service}, {@link android.app.Activity},
* or {@link android.content.ContentProvider}) that lets the system know your process
- * should remain running.</p>
+ * should remain running.
*
* <p>You must keep in mind the situations in which your process
* could go away, and thus require that you later re-create a new Binder and re-attach
- * it when the process starts again. For example, if you are using this within an
+ * it when the process starts again. For example, if you are using this within an
* {@link android.app.Activity}, your activity's process may be killed any time the
* activity is not started; if the activity is later re-created you will need to
* create a new Binder and hand it back to the correct place again; you need to be
* aware that your process may be started for another reason (for example to receive
* a broadcast) that will not involve re-creating the activity and thus run its code
- * to create a new Binder.</p>
+ * to create a new Binder.
*
* @see IBinder
*/
@@ -94,14 +94,15 @@
/**
* Value to represents that a calling work source is not set.
*
- * This constatnt needs to be kept in sync with IPCThreadState::kUnsetWorkSource.
+ * <p>This constant needs to be kept in sync with IPCThreadState::kUnsetWorkSource.
*
* @hide
*/
public static final int UNSET_WORKSOURCE = -1;
/**
- * Control whether dump() calls are allowed.
+ * Control whether {@link #dump(FileDescriptor, PrintWriter, String[]) dump()}
+ * calls are allowed.
*/
private static volatile String sDumpDisabled = null;
@@ -188,7 +189,7 @@
sObserver = observer;
}
- /** {@hide} */
+ /** @hide */
static volatile boolean sWarnOnBlocking = false;
/**
@@ -207,8 +208,8 @@
/**
* Allow blocking calls on the given interface, overriding the requested
* value of {@link #setWarnOnBlocking(boolean)}.
- * <p>
- * This should only be rarely called when you are <em>absolutely sure</em>
+ *
+ * <p>This should only be rarely called when you are <em>absolutely sure</em>
* the remote interface is a built-in system component that can never be
* upgraded. In particular, this <em>must never</em> be called for
* interfaces hosted by package that could be upgraded or replaced,
@@ -258,7 +259,9 @@
ThreadLocal.withInitial(() -> sWarnOnBlocking);
/**
- * Allow blocking calls for the current thread. See {@link #allowBlocking}.
+ * Allow blocking calls for the current thread.
+ *
+ * @see {@link #allowBlocking}.
*
* @hide
*/
@@ -267,7 +270,9 @@
}
/**
- * Reset the current thread to the default blocking behavior. See {@link #defaultBlocking}.
+ * Reset the current thread to the default blocking behavior.
+ *
+ * @see {@link #defaultBlocking}.
*
* @hide
*/
@@ -286,10 +291,10 @@
/**
* Return the ID of the process that sent you the current transaction
- * that is being processed. This pid can be used with higher-level
+ * that is being processed. This PID can be used with higher-level
* system services to determine its identity and check permissions.
* If the current thread is not currently executing an incoming transaction,
- * then its own pid is returned.
+ * then its own PID is returned.
*
* Warning: oneway transactions do not receive PID.
*/
@@ -297,11 +302,11 @@
public static final native int getCallingPid();
/**
- * Return the Linux uid assigned to the process that sent you the
- * current transaction that is being processed. This uid can be used with
+ * Return the Linux UID assigned to the process that sent you the
+ * current transaction that is being processed. This UID can be used with
* higher-level system services to determine its identity and check
- * permissions. If the current thread is not currently executing an
- * incoming transaction, then its own uid is returned.
+ * permissions. If the current thread is not currently executing an
+ * incoming transaction, then its own UID is returned.
*/
@CriticalNative
public static final native int getCallingUid();
@@ -316,11 +321,11 @@
public static final native boolean isDirectlyHandlingTransaction();
/**
- * Return the Linux uid assigned to the process that sent the transaction
+ * Return the Linux UID assigned to the process that sent the transaction
* currently being processed.
*
* @throws IllegalStateException if the current thread is not currently
- * executing an incoming transaction.
+ * executing an incoming transaction.
*/
public static final int getCallingUidOrThrow() {
if (!isDirectlyHandlingTransaction()) {
@@ -332,18 +337,20 @@
/**
* Return the UserHandle assigned to the process that sent you the
- * current transaction that is being processed. This is the user
- * of the caller. It is distinct from {@link #getCallingUid()} in that a
+ * current transaction that is being processed. This is the user
+ * of the caller. It is distinct from {@link #getCallingUid()} in that a
* particular user will have multiple distinct apps running under it each
- * with their own uid. If the current thread is not currently executing an
+ * with their own UID. If the current thread is not currently executing an
* incoming transaction, then its own UserHandle is returned.
+ *
+ * @see UserHandle
*/
public static final @NonNull UserHandle getCallingUserHandle() {
return UserHandle.of(UserHandle.getUserId(getCallingUid()));
}
/**
- * Reset the identity of the incoming IPC on the current thread. This can
+ * Reset the identity of the incoming IPC on the current thread. This can
* be useful if, while handling an incoming call, you will be calling
* on interfaces of other objects that may be local to your process and
* need to do permission checks on the calls coming into them (so they
@@ -376,10 +383,10 @@
/**
* Convenience method for running the provided action enclosed in
- * {@link #clearCallingIdentity}/{@link #restoreCallingIdentity}
+ * {@link #clearCallingIdentity}/{@link #restoreCallingIdentity}.
*
- * Any exception thrown by the given action will be caught and rethrown after the call to
- * {@link #restoreCallingIdentity}
+ * <p>Any exception thrown by the given action will be caught and
+ * rethrown after the call to {@link #restoreCallingIdentity}.
*
* @hide
*/
@@ -400,10 +407,10 @@
/**
* Convenience method for running the provided action enclosed in
- * {@link #clearCallingIdentity}/{@link #restoreCallingIdentity} returning the result
+ * {@link #clearCallingIdentity}/{@link #restoreCallingIdentity} returning the result.
*
- * Any exception thrown by the given action will be caught and rethrown after the call to
- * {@link #restoreCallingIdentity}
+ * <p>Any exception thrown by the given action will be caught and rethrown after
+ * the call to {@link #restoreCallingIdentity}.
*
* @hide
*/
@@ -428,12 +435,13 @@
*
* <p>The StrictMode settings are kept in two places: a Java-level
* threadlocal for libcore/Dalvik, and a native threadlocal (set
- * here) for propagation via Binder calls. This is a little
+ * here) for propagation via Binder calls. This is a little
* unfortunate, but necessary to break otherwise more unfortunate
* dependencies either of Dalvik on Android, or Android
* native-only code on Dalvik.
*
* @see StrictMode
+ *
* @hide
*/
@CriticalNative
@@ -443,6 +451,7 @@
* Gets the current native thread-local StrictMode policy mask.
*
* @see #setThreadStrictModePolicy
+ *
* @hide
*/
@CriticalNative
@@ -459,7 +468,7 @@
* reasons, we only support one UID. This UID represents the original user responsible for the
* binder calls.
*
- * <p>{@link Binder#restoreCallingWorkSource(long)} must always be called after setting the
+ * <p>{@link #restoreCallingWorkSource(long)} must always be called after setting the
* worksource.
*
* <p>A typical use case would be
@@ -477,16 +486,16 @@
*
* @param workSource The original UID responsible for the binder call.
* @return token to restore original work source.
- **/
+ */
@CriticalNative
public static final native long setCallingWorkSourceUid(int workSource);
/**
* Returns the work source set by the caller.
*
- * Unlike {@link Binder#getCallingUid()}, this result of this method cannot be trusted. The
+ * <p>Unlike {@link #getCallingUid()}, this result of this method cannot be trusted. The
* caller can set the value to whatever they want. Only use this value if you trust the calling
- * uid.
+ * UID.
*
* @return The original UID responsible for the binder transaction.
*/
@@ -499,7 +508,7 @@
* <p>The work source will be propagated for future outgoing binder transactions
* executed on this thread.
*
- * <p>{@link Binder#restoreCallingWorkSource(long)} must always be called after clearing the
+ * <p>{@link #restoreCallingWorkSource(long)} must always be called after clearing the
* worksource.
*
* <p>A typical use case would be
@@ -513,13 +522,13 @@
* </pre>
*
* @return token to restore original work source.
- **/
+ */
@CriticalNative
public static final native long clearCallingWorkSource();
/**
* Restores the work source on this thread using a token returned by
- * {@link #setCallingWorkSourceUid(int) or {@link clearCallingWorkSource()}.
+ * {@link #setCallingWorkSourceUid(int)} or {@link #clearCallingWorkSource()}.
*
* <p>A typical use case would be
* <pre>
@@ -530,7 +539,7 @@
* Binder.restoreCallingWorkSource(token);
* }
* </pre>
- **/
+ */
@CriticalNative
public static final native void restoreCallingWorkSource(long token);
@@ -553,7 +562,7 @@
* Use a VINTF-stability binder w/o VINTF requirements. Should be called
* on a binder before it is sent out of process.
*
- * This must be called before the object is sent to another process.
+ * <p>This must be called before the object is sent to another process.
*
* @hide
*/
@@ -561,7 +570,7 @@
/**
* Flush any Binder commands pending in the current thread to the kernel
- * driver. This can be
+ * driver. This can be
* useful to call before performing an operation that may block for a long
* time, to ensure that any pending object references have been released
* in order to prevent the process from holding on to objects longer than
@@ -570,7 +579,7 @@
public static final native void flushPendingCommands();
/**
- * Add the calling thread to the IPC thread pool. This function does
+ * Add the calling thread to the IPC thread pool. This function does
* not return until the current process is exiting.
*/
public static final void joinThreadPool() {
@@ -579,6 +588,7 @@
/**
* Returns true if the specified interface is a proxy.
+ *
* @hide
*/
public static final boolean isProxy(IInterface iface) {
@@ -588,6 +598,7 @@
/**
* Call blocks until the number of executing binder threads is less
* than the maximum number of binder threads allowed for this process.
+ *
* @hide
*/
public static final native void blockUntilThreadAvailable();
@@ -595,7 +606,7 @@
/**
* Default constructor just initializes the object.
*
- * If you're creating a Binder token (a Binder object without an attached interface),
+ * <p>If you're creating a Binder token (a Binder object without an attached interface),
* you should use {@link #Binder(String)} instead.
*/
public Binder() {
@@ -605,7 +616,7 @@
/**
* Constructor for creating a raw Binder object (token) along with a descriptor.
*
- * The descriptor of binder objects usually specifies the interface they are implementing.
+ * <p>The descriptor of binder objects usually specifies the interface they are implementing.
* In case of binder tokens, no interface is implemented, and the descriptor can be used
* as a sort of tag to help identify the binder token. This will help identify remote
* references to these objects more easily when debugging.
@@ -614,7 +625,7 @@
* Instead of creating multiple tokens with the same descriptor, consider adding a suffix to
* help identify them.
*/
- public Binder(@Nullable String descriptor) {
+ public Binder(@Nullable String descriptor) {
mObject = getNativeBBinderHolder();
NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mObject);
@@ -631,9 +642,9 @@
/**
* Convenience method for associating a specific interface with the Binder.
- * After calling, queryLocalInterface() will be implemented for you
- * to return the given owner IInterface when the corresponding
- * descriptor is requested.
+ * After calling, {@link #queryLocalInterface(String) queryLocalInterface()}
+ * will be implemented for you to return the given owner IInterface when
+ * the corresponding descriptor is requested.
*/
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
mOwner = owner;
@@ -666,8 +677,8 @@
}
/**
- * Use information supplied to attachInterface() to return the
- * associated IInterface if it matches the requested
+ * Use information supplied to {@link #attachInterface attachInterface()}
+ * to return the associated {@link IInterface} if it matches the requested
* descriptor.
*/
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
@@ -678,14 +689,15 @@
}
/**
- * Control disabling of dump calls in this process. This is used by the system
+ * Control disabling of dump calls in this process. This is used by the system
* process watchdog to disable incoming dump calls while it has detecting the system
- * is hung and is reporting that back to the activity controller. This is to
+ * is hung and is reporting that back to the activity controller. This is to
* prevent the controller from getting hung up on bug reports at this point.
- * @hide
*
* @param msg The message to show instead of the dump; if null, dumps are
* re-enabled.
+ *
+ * @hide
*/
public static void setDumpDisabled(String msg) {
sDumpDisabled = msg;
@@ -694,7 +706,8 @@
/**
* Listener to be notified about each proxy-side binder call.
*
- * See {@link setProxyTransactListener}.
+ * @see {@link #setProxyTransactListener}.
+ *
* @hide
*/
@SystemApi
@@ -702,7 +715,8 @@
/**
* Called before onTransact.
*
- * @return an object that will be passed back to #onTransactEnded (or null).
+ * @return an object that will be passed back to {@link #onTransactEnded} (or null).,
+ *
* @hide
*/
@Nullable
@@ -713,15 +727,15 @@
/**
* Called before onTransact.
*
- * @return an object that will be passed back to #onTransactEnded (or null).
+ * @return an object that will be passed back to {@link #onTransactEnded} (or null).
*/
@Nullable
Object onTransactStarted(@NonNull IBinder binder, int transactionCode);
/**
- * Called after onTranact (even when an exception is thrown).
+ * Called after onTransact (even when an exception is thrown).
*
- * @param session The object return by #onTransactStarted.
+ * @param session The object return by {@link #onTransactStarted}.
*/
void onTransactEnded(@Nullable Object session);
}
@@ -732,16 +746,17 @@
* <li>By default, this listener will propagate the worksource if the outgoing call happens on
* the same thread as the incoming binder call.
* <li>Custom attribution can be done by calling {@link ThreadLocalWorkSource#setUid(int)}.
+ *
* @hide
*/
public static class PropagateWorkSourceTransactListener implements ProxyTransactListener {
@Override
public Object onTransactStarted(IBinder binder, int transactionCode) {
- // Note that {@link Binder#getCallingUid()} is already set to the UID of the current
- // process when this method is called.
- //
- // We use ThreadLocalWorkSource instead. It also allows feature owners to set
- // {@link ThreadLocalWorkSource#set(int) manually to attribute resources to a UID.
+ // Note that {@link #getCallingUid()} is already set to the UID of the current
+ // process when this method is called.
+ //
+ // We use {@link ThreadLocalWorkSource} instead. It also allows feature owners to set
+ // {@link ThreadLocalWorkSource#set(int)} manually to attribute resources to a UID.
int uid = ThreadLocalWorkSource.getUid();
if (uid != ThreadLocalWorkSource.UID_NONE) {
return Binder.setCallingWorkSourceUid(uid);
@@ -770,6 +785,7 @@
* <li>The listener is called on the critical path of the binder transaction so be careful about
* performance.
* <li>Never execute another binder transaction inside the listener.
+ *
* @hide
*/
@SystemApi
@@ -778,7 +794,7 @@
}
/**
- * Default implementation is a stub that returns false. You will want
+ * Default implementation is a stub that returns false. You will want
* to override this to do the appropriate unmarshalling of transactions.
*
* <p>If you want to call this, call transact().
@@ -786,15 +802,14 @@
* <p>Implementations that are returning a result should generally use
* {@link Parcel#writeNoException() Parcel.writeNoException} and
* {@link Parcel#writeException(Exception) Parcel.writeException} to propagate
- * exceptions back to the caller.</p>
+ * exceptions back to the caller.
*
- * @param code The action to perform. This should
- * be a number between {@link #FIRST_CALL_TRANSACTION} and
- * {@link #LAST_CALL_TRANSACTION}.
+ * @param code The action to perform. This should be a number between
+ * {@link #FIRST_CALL_TRANSACTION} and {@link #LAST_CALL_TRANSACTION}.
* @param data Marshalled data being received from the caller.
* @param reply If the caller is expecting a result back, it should be marshalled
* in to here.
- * @param flags Additional operation flags. Either 0 for a normal
+ * @param flags Additional operation flags. Either 0 for a normal
* RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
*
* @return Return true on a successful call; returning false is generally used to
@@ -856,10 +871,12 @@
* Resolves a transaction code to a human readable name.
*
* <p>Default implementation is a stub that returns null.
+ *
* <p>AIDL generated code will return the original method name.
*
* @param transactionCode The code to resolve.
* @return A human readable name.
+ *
* @hide
*/
public @Nullable String getTransactionName(int transactionCode) {
@@ -925,7 +942,7 @@
* Print the object's state into the given stream.
*
* @param fd The raw file descriptor that the dump is being sent to.
- * @param fout The file to which you should dump your state. This will be
+ * @param fout The file to which you should dump your state. This will be
* closed for you after you return.
* @param args additional arguments to the dump request.
*/
@@ -941,6 +958,7 @@
* @param callback Callback through which to interact with the invoking shell.
* @param resultReceiver Called when the command has finished executing, with the result code.
* @throws RemoteException
+ *
* @hide
*/
public void shellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
@@ -958,7 +976,8 @@
*
* <p class="caution">Note: no permission checking is done before calling this method; you must
* apply any security checks as appropriate for the command being executed.
- * Consider using {@link ShellCommand} to help in the implementation.</p>
+ * Consider using {@link ShellCommand} to help in the implementation.
+ *
* @hide
*/
public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
@@ -1013,7 +1032,7 @@
* System services can implement this method to implement ADB shell commands.
*
* <p>A system binder service can implement it to handle shell commands on ADB. For example,
- * the Job Scheduler service implements it to handle <code>adb shell cmd jobscheduler</code>.
+ * the Job Scheduler service implements it to handle {@code adb shell cmd jobscheduler}.
*
* <p>Commands are only executable by ADB shell; i.e. only {@link Process#SHELL_UID} and
* {@link Process#ROOT_UID} can call them.
@@ -1022,8 +1041,8 @@
* @param out standard output
* @param err standard error
* @param args arguments passed to the command. Can be empty. The first argument is typically
- * a subcommand, such as {@code run} for {@code adb shell cmd jobscheduler run}.
- * @return the status code returned from the <code>cmd</code> command.
+ * a subcommand, such as {@code run} for {@code adb shell cmd jobscheduler run}.
+ * @return the status code returned from the {@code cmd} command.
*
* @hide
*/
@@ -1051,7 +1070,7 @@
public final native void setExtension(@Nullable IBinder extension);
/**
- * Default implementation rewinds the parcels and calls onTransact. On
+ * Default implementation rewinds the parcels and calls onTransact. On
* the remote side, transact calls into the binder to do the IPC.
*/
public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply,
@@ -1083,7 +1102,7 @@
static void checkParcel(IBinder obj, int code, Parcel parcel, String msg) {
if (CHECK_PARCEL_SIZE && parcel.dataSize() >= 800*1024) {
- // Trying to send > 800k, this is way too much
+ // Trying to send > 800k, this is way too much.
StringBuilder sb = new StringBuilder();
sb.append(msg);
sb.append(": on ");
@@ -1107,7 +1126,7 @@
private static native long getNativeBBinderHolder();
/**
- * By default, we use the calling uid since we can always trust it.
+ * By default, we use the calling UID since we can always trust it.
*/
private static volatile BinderInternal.WorkSourceProvider sWorkSourceProvider =
(x) -> Binder.getCallingUid();
@@ -1122,6 +1141,7 @@
* <li>The callback is called on the critical path of the binder transaction so be careful about
* performance.
* <li>Never execute another binder transaction inside the callback.
+ *
* @hide
*/
public static void setWorkSourceProvider(BinderInternal.WorkSourceProvider workSourceProvider) {
@@ -1131,12 +1151,12 @@
sWorkSourceProvider = workSourceProvider;
}
- // Entry point from android_util_Binder.cpp's onTransact
+ // Entry point from android_util_Binder.cpp's onTransact.
@UnsupportedAppUsage
private boolean execTransact(int code, long dataObj, long replyObj,
int flags) {
// At that point, the parcel request headers haven't been parsed so we do not know what
- // WorkSource the caller has set. Use calling uid as the default.
+ // {@link WorkSource} the caller has set. Use calling UID as the default.
final int callingUid = Binder.getCallingUid();
final long origWorkSource = ThreadLocalWorkSource.setUid(callingUid);
try {
@@ -1154,17 +1174,18 @@
observer != null ? observer.callStarted(this, code, UNSET_WORKSOURCE) : null;
Parcel data = Parcel.obtain(dataObj);
Parcel reply = Parcel.obtain(replyObj);
- // theoretically, we should call transact, which will call onTransact,
+ // Theoretically, we should call transact, which will call onTransact,
// but all that does is rewind it, and we just got these from an IPC,
// so we'll just call it directly.
boolean res;
// Log any exceptions as warnings, don't silently suppress them.
- // If the call was FLAG_ONEWAY then these exceptions disappear into the ether.
+ // If the call was {@link IBinder#FLAG_ONEWAY} then these exceptions
+ // disappear into the ether.
final boolean tracingEnabled = Binder.isTracingEnabled();
try {
final BinderCallHeavyHitterWatcher heavyHitterWatcher = sHeavyHitterWatcher;
if (heavyHitterWatcher != null) {
- // Notify the heavy hitter watcher, if it's enabled
+ // Notify the heavy hitter watcher, if it's enabled.
heavyHitterWatcher.onTransaction(callingUid, getClass(), code);
}
if (tracingEnabled) {
@@ -1197,7 +1218,7 @@
Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
}
} else {
- // Clear the parcel before writing the exception
+ // Clear the parcel before writing the exception.
reply.setDataSize(0);
reply.setDataPosition(0);
reply.writeException(e);
@@ -1209,7 +1230,7 @@
}
if (observer != null) {
// The parcel RPC headers have been called during onTransact so we can now access
- // the worksource uid from the parcel.
+ // the worksource UID from the parcel.
final int workSourceUid = sWorkSourceProvider.resolveWorkSourceUid(
data.readCallingWorkSourceUid());
observer.callEnded(callSession, data.dataSize(), reply.dataSize(), workSourceUid);
@@ -1220,9 +1241,9 @@
data.recycle();
// Just in case -- we are done with the IPC, so there should be no more strict
- // mode violations that have gathered for this thread. Either they have been
+ // mode violations that have gathered for this thread. Either they have been
// parceled and are now in transport off to the caller, or we are returning back
- // to the main transaction loop to wait for another incoming transaction. Either
+ // to the main transaction loop to wait for another incoming transaction. Either
// way, strict mode begone!
StrictMode.clearGatheredViolations();
return res;
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 92eb7a5..b2bbfd6 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -102,6 +102,18 @@
}
/**
+ * Constructs a {@link Bundle} containing a copy of {@code from}.
+ *
+ * @param from The bundle to be copied.
+ * @param deep Whether is a deep or shallow copy.
+ *
+ * @hide
+ */
+ Bundle(Bundle from, boolean deep) {
+ super(from, deep);
+ }
+
+ /**
* If {@link #mParcelledData} is not null, copy the HAS FDS bit from it because it's fast.
* Otherwise (if {@link #mParcelledData} is already null), leave {@link #FLAG_HAS_FDS_KNOWN}
* unset, because scanning a map is slower. We'll do it lazily in
@@ -167,13 +179,6 @@
}
/**
- * Constructs a Bundle without initializing it.
- */
- Bundle(boolean doInit) {
- super(doInit);
- }
-
- /**
* Make a Bundle for a single key/value pair.
*
* @hide
@@ -260,9 +265,7 @@
* are referenced as-is and not copied in any way.
*/
public Bundle deepCopy() {
- Bundle b = new Bundle(false);
- b.copyInternal(this, true);
- return b;
+ return new Bundle(this, /* deep */ true);
}
/**
@@ -324,28 +327,10 @@
*/
public boolean hasFileDescriptors() {
if ((mFlags & FLAG_HAS_FDS_KNOWN) == 0) {
- boolean fdFound = false; // keep going until we find one or run out of data
-
- if (mParcelledData != null) {
- if (mParcelledData.hasFileDescriptors()) {
- fdFound = true;
- }
- } else {
- // It's been unparcelled, so we need to walk the map
- for (int i=mMap.size()-1; i>=0; i--) {
- Object obj = mMap.valueAt(i);
- if (Parcel.hasFileDescriptors(obj)) {
- fdFound = true;
- break;
- }
- }
- }
-
- if (fdFound) {
- mFlags |= FLAG_HAS_FDS;
- } else {
- mFlags &= ~FLAG_HAS_FDS;
- }
+ Parcel p = mParcelledData;
+ mFlags = (Parcel.hasFileDescriptors((p != null) ? p : mMap))
+ ? mFlags | FLAG_HAS_FDS
+ : mFlags & ~FLAG_HAS_FDS;
mFlags |= FLAG_HAS_FDS_KNOWN;
}
return (mFlags & FLAG_HAS_FDS) != 0;
diff --git a/core/java/android/os/ISystemConfig.aidl b/core/java/android/os/ISystemConfig.aidl
index d83d94a..15e3ce2 100644
--- a/core/java/android/os/ISystemConfig.aidl
+++ b/core/java/android/os/ISystemConfig.aidl
@@ -16,6 +16,8 @@
package android.os;
+import android.content.ComponentName;
+
/**
* Binder interface to query SystemConfig in the system server.
* {@hide}
@@ -44,5 +46,5 @@
/**
* @see SystemConfigManager#getEnabledComponentOverrides
*/
- List<String> getEnabledComponentOverrides(String packageName);
+ List<ComponentName> getEnabledComponentOverrides(String packageName);
}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 61882ff..d1e6716 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -747,57 +747,70 @@
}
/**
- * Check if the object used in {@link #readValue(ClassLoader)} / {@link #writeValue(Object)}
- * has file descriptors.
+ * Check if the object has file descriptors.
+ *
+ * <p>Objects supported are {@link Parcel} and objects that can be passed to {@link
+ * #writeValue(Object)}}
*
* <p>For most cases, it will use the self-reported {@link Parcelable#describeContents()} method
* for that.
*
- * @throws IllegalArgumentException if you provide any object not supported by above methods.
- * Most notably, if you pass {@link Parcel}, this method will throw, for that check
- * {@link Parcel#hasFileDescriptors()}
+ * @throws IllegalArgumentException if you provide any object not supported by above methods
+ * (including if the unsupported object is inside a nested container).
*
* @hide
*/
public static boolean hasFileDescriptors(Object value) {
- if (value instanceof LazyValue) {
- return ((LazyValue) value).hasFileDescriptors();
- } else if (value instanceof Parcelable) {
- if ((((Parcelable) value).describeContents()
- & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
+ if (value instanceof Parcel) {
+ Parcel parcel = (Parcel) value;
+ if (parcel.hasFileDescriptors()) {
return true;
}
- } else if (value instanceof Parcelable[]) {
- Parcelable[] array = (Parcelable[]) value;
- for (int n = array.length - 1; n >= 0; n--) {
- Parcelable p = array[n];
- if (p != null && ((p.describeContents()
- & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0)) {
+ } else if (value instanceof LazyValue) {
+ LazyValue lazy = (LazyValue) value;
+ if (lazy.hasFileDescriptors()) {
+ return true;
+ }
+ } else if (value instanceof Parcelable) {
+ Parcelable parcelable = (Parcelable) value;
+ if ((parcelable.describeContents() & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
+ return true;
+ }
+ } else if (value instanceof ArrayMap<?, ?>) {
+ ArrayMap<?, ?> map = (ArrayMap<?, ?>) value;
+ for (int i = 0, n = map.size(); i < n; i++) {
+ if (hasFileDescriptors(map.keyAt(i))
+ || hasFileDescriptors(map.valueAt(i))) {
+ return true;
+ }
+ }
+ } else if (value instanceof Map<?, ?>) {
+ Map<?, ?> map = (Map<?, ?>) value;
+ for (Map.Entry<?, ?> entry : map.entrySet()) {
+ if (hasFileDescriptors(entry.getKey())
+ || hasFileDescriptors(entry.getValue())) {
+ return true;
+ }
+ }
+ } else if (value instanceof List<?>) {
+ List<?> list = (List<?>) value;
+ for (int i = 0, n = list.size(); i < n; i++) {
+ if (hasFileDescriptors(list.get(i))) {
return true;
}
}
} else if (value instanceof SparseArray<?>) {
SparseArray<?> array = (SparseArray<?>) value;
- for (int n = array.size() - 1; n >= 0; n--) {
- Object object = array.valueAt(n);
- if (object instanceof Parcelable) {
- Parcelable p = (Parcelable) object;
- if (p != null && (p.describeContents()
- & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
- return true;
- }
+ for (int i = 0, n = array.size(); i < n; i++) {
+ if (hasFileDescriptors(array.valueAt(i))) {
+ return true;
}
}
- } else if (value instanceof ArrayList<?>) {
- ArrayList<?> array = (ArrayList<?>) value;
- for (int n = array.size() - 1; n >= 0; n--) {
- Object object = array.get(n);
- if (object instanceof Parcelable) {
- Parcelable p = (Parcelable) object;
- if (p != null && ((p.describeContents()
- & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0)) {
- return true;
- }
+ } else if (value instanceof Object[]) {
+ Object[] array = (Object[]) value;
+ for (int i = 0, n = array.length; i < n; i++) {
+ if (hasFileDescriptors(array[i])) {
+ return true;
}
}
} else {
@@ -2448,9 +2461,9 @@
writeByteArray(baos.toByteArray());
} catch (IOException ioe) {
- throw new RuntimeException("Parcelable encountered " +
- "IOException writing serializable object (name = " + name +
- ")", ioe);
+ throw new BadParcelableException("Parcelable encountered "
+ + "IOException writing serializable object (name = "
+ + name + ")", ioe);
}
}
@@ -3818,7 +3831,7 @@
break;
case VAL_SERIALIZABLE:
- object = readSerializable(loader);
+ object = readSerializableInternal(loader, clazz);
break;
case VAL_PARCELABLEARRAY:
@@ -4144,12 +4157,37 @@
* wasn't found in the parcel.
*/
@Nullable
- public final Serializable readSerializable() {
- return readSerializable(null);
+ public Serializable readSerializable() {
+ return readSerializableInternal(/* loader */ null, /* clazz */ null);
}
+ /**
+ * Same as {@link #readSerializable()} but accepts {@code loader} parameter
+ * as the primary classLoader for resolving the Serializable class; and {@code clazz} parameter
+ * as the required type.
+ *
+ * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+ * is not an instance of that class or any of its children class or there there was an error
+ * deserializing the object.
+ */
@Nullable
- private final Serializable readSerializable(@Nullable final ClassLoader loader) {
+ public <T extends Serializable> T readSerializable(@Nullable ClassLoader loader,
+ @NonNull Class<T> clazz) {
+ Objects.requireNonNull(clazz);
+ return readSerializableInternal(loader, clazz);
+ }
+
+ /**
+ * @param clazz The type of the serializable expected or {@code null} for performing no checks
+ */
+ @Nullable
+ private <T> T readSerializableInternal(@Nullable final ClassLoader loader,
+ @Nullable Class<T> clazz) {
+ if (clazz != null && !Serializable.class.isAssignableFrom(clazz)) {
+ throw new BadParcelableException("About to unparcel a serializable object "
+ + " but class required " + clazz.getName() + " is not Serializable");
+ }
+
String name = readString();
if (name == null) {
// For some reason we were unable to read the name of the Serializable (either there
@@ -4158,9 +4196,20 @@
return null;
}
- byte[] serializedData = createByteArray();
- ByteArrayInputStream bais = new ByteArrayInputStream(serializedData);
try {
+ if (clazz != null && loader != null) {
+ // If custom classloader is provided, resolve the type of serializable using the
+ // name, then check the type before deserialization. As in this case we can resolve
+ // the class the same way as ObjectInputStream, using the provided classloader.
+ Class<?> cl = Class.forName(name, false, loader);
+ if (!clazz.isAssignableFrom(cl)) {
+ throw new BadParcelableException("Serializable object "
+ + cl.getName() + " is not a subclass of required class "
+ + clazz.getName() + " provided in the parameter");
+ }
+ }
+ byte[] serializedData = createByteArray();
+ ByteArrayInputStream bais = new ByteArrayInputStream(serializedData);
ObjectInputStream ois = new ObjectInputStream(bais) {
@Override
protected Class<?> resolveClass(ObjectStreamClass osClass)
@@ -4168,22 +4217,31 @@
// try the custom classloader if provided
if (loader != null) {
Class<?> c = Class.forName(osClass.getName(), false, loader);
- if (c != null) {
- return c;
- }
+ return Objects.requireNonNull(c);
}
return super.resolveClass(osClass);
}
};
- return (Serializable) ois.readObject();
+ T object = (T) ois.readObject();
+ if (clazz != null && loader == null) {
+ // If custom classloader is not provided, check the type of the serializable using
+ // the deserialized object, as we cannot resolve the class the same way as
+ // ObjectInputStream.
+ if (!clazz.isAssignableFrom(object.getClass())) {
+ throw new BadParcelableException("Serializable object "
+ + object.getClass().getName() + " is not a subclass of required class "
+ + clazz.getName() + " provided in the parameter");
+ }
+ }
+ return object;
} catch (IOException ioe) {
- throw new RuntimeException("Parcelable encountered " +
- "IOException reading a Serializable object (name = " + name +
- ")", ioe);
+ throw new BadParcelableException("Parcelable encountered "
+ + "IOException reading a Serializable object (name = "
+ + name + ")", ioe);
} catch (ClassNotFoundException cnfe) {
- throw new RuntimeException("Parcelable encountered " +
- "ClassNotFoundException reading a Serializable object (name = "
- + name + ")", cnfe);
+ throw new BadParcelableException("Parcelable encountered "
+ + "ClassNotFoundException reading a Serializable object (name = "
+ + name + ")", cnfe);
}
}
diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java
index 7b55e710..f4edcb1 100644
--- a/core/java/android/os/PersistableBundle.java
+++ b/core/java/android/os/PersistableBundle.java
@@ -156,10 +156,15 @@
}
/**
- * Constructs a PersistableBundle without initializing it.
+ * Constructs a {@link PersistableBundle} containing a copy of {@code from}.
+ *
+ * @param from The bundle to be copied.
+ * @param deep Whether is a deep or shallow copy.
+ *
+ * @hide
*/
- PersistableBundle(boolean doInit) {
- super(doInit);
+ PersistableBundle(PersistableBundle from, boolean deep) {
+ super(from, deep);
}
/**
@@ -190,9 +195,7 @@
* are referenced as-is and not copied in any way.
*/
public PersistableBundle deepCopy() {
- PersistableBundle b = new PersistableBundle(false);
- b.copyInternal(this, true);
- return b;
+ return new PersistableBundle(this, /* deep */ true);
}
/**
diff --git a/core/java/android/os/SystemConfigManager.java b/core/java/android/os/SystemConfigManager.java
index a6316df..cde2063 100644
--- a/core/java/android/os/SystemConfigManager.java
+++ b/core/java/android/os/SystemConfigManager.java
@@ -17,10 +17,10 @@
import android.Manifest;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.content.ComponentName;
import android.content.Context;
import android.util.ArraySet;
import android.util.Log;
@@ -138,9 +138,9 @@
* @return The enabled component
* {@hide}
*/
- @SystemApi
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@NonNull
- public List<String> getEnabledComponentOverrides(@NonNull String packageName) {
+ public List<ComponentName> getEnabledComponentOverrides(@NonNull String packageName) {
try {
return mInterface.getEnabledComponentOverrides(packageName);
} catch (RemoteException e) {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 43d6e47..14d7e82 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -288,6 +288,24 @@
public static final String DISALLOW_CHANGE_WIFI_STATE = "no_change_wifi_state";
/**
+ * Specifies if a user is disallowed from using Wi-Fi tethering.
+ *
+ * <p>This restriction can only be set by a device owner,
+ * a profile owner of an organization-owned managed profile on the parent profile.
+ * When it is set by any of these owners, it prevents all users from using
+ * Wi-Fi tethering. Other forms of tethering are not affected.
+ *
+ * <p>The default value is <code>false</code>.
+ *
+ * <p>Key for user restrictions.
+ * <p>Type: Boolean
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_WIFI_TETHERING = "no_wifi_tethering";
+
+ /**
* Specifies if a user is disallowed from changing the device
* language. The default value is <code>false</code>.
*
diff --git a/core/java/android/permission/DEFAULT_PERMISSION_GRANT_POLICY_OWNERS b/core/java/android/permission/DEFAULT_PERMISSION_GRANT_POLICY_OWNERS
new file mode 100644
index 0000000..cb521c8
--- /dev/null
+++ b/core/java/android/permission/DEFAULT_PERMISSION_GRANT_POLICY_OWNERS
@@ -0,0 +1,8 @@
+ewol@google.com
+hackbod@google.com
+jsharkey@google.com
+narayan@google.com
+patb@google.com
+svetoslavganov@google.com
+yamasani@google.com
+zhanghai@google.com
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index d4c9ade..e2f5908 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -180,8 +180,6 @@
mPermissionManager = IPermissionManager.Stub.asInterface(ServiceManager.getServiceOrThrow(
"permissionmgr"));
mLegacyPermissionManager = context.getSystemService(LegacyPermissionManager.class);
- //TODO ntmyren: there should be a way to only enable the watcher when requested
- mUsageHelper = new PermissionUsageHelper(context);
}
/**
@@ -989,6 +987,29 @@
}
/**
+ * Initialize the PermissionUsageHelper, which will register active app op listeners
+ *
+ * @hide
+ */
+ public void initializeUsageHelper() {
+ if (mUsageHelper == null) {
+ mUsageHelper = new PermissionUsageHelper(mContext);
+ }
+ }
+
+ /**
+ * Teardown the PermissionUsageHelper, removing listeners
+ *
+ * @hide
+ */
+ public void tearDownUsageHelper() {
+ if (mUsageHelper != null) {
+ mUsageHelper.tearDown();
+ mUsageHelper = null;
+ }
+ }
+
+ /**
* @return A list of permission groups currently or recently used by all apps by all users in
* the current profile group.
*
@@ -998,7 +1019,7 @@
@NonNull
@RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
public List<PermGroupUsage> getIndicatorAppOpUsageData() {
- return mUsageHelper.getOpUsageData(new AudioManager().isMicrophoneMute());
+ return getIndicatorAppOpUsageData(new AudioManager().isMicrophoneMute());
}
/**
@@ -1013,9 +1034,7 @@
@RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
public List<PermGroupUsage> getIndicatorAppOpUsageData(boolean micMuted) {
// Lazily initialize the usage helper
- if (mUsageHelper == null) {
- mUsageHelper = new PermissionUsageHelper(mContext);
- }
+ initializeUsageHelper();
return mUsageHelper.getOpUsageData(micMuted);
}
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 19f204b..20f6c10 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -176,6 +176,11 @@
return mUserContexts.get(user);
}
+ public void tearDown() {
+ mAppOpsManager.stopWatchingActive(this);
+ mAppOpsManager.stopWatchingStarted(this);
+ }
+
@Override
public void onOpActiveChanged(@NonNull String op, int uid, @NonNull String packageName,
boolean active) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9d4a249..0348107 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3316,6 +3316,66 @@
}
}
+ private static float parseFloatSetting(String settingValue, String settingName)
+ throws SettingNotFoundException {
+ if (settingValue == null) {
+ throw new SettingNotFoundException(settingName);
+ }
+ try {
+ return Float.parseFloat(settingValue);
+ } catch (NumberFormatException e) {
+ throw new SettingNotFoundException(settingName);
+ }
+ }
+
+ private static float parseFloatSettingWithDefault(String settingValue, float defaultValue) {
+ try {
+ return settingValue != null ? Float.parseFloat(settingValue) : defaultValue;
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+
+ private static int parseIntSetting(String settingValue, String settingName)
+ throws SettingNotFoundException {
+ if (settingValue == null) {
+ throw new SettingNotFoundException(settingName);
+ }
+ try {
+ return Integer.parseInt(settingValue);
+ } catch (NumberFormatException e) {
+ throw new SettingNotFoundException(settingName);
+ }
+ }
+
+ private static int parseIntSettingWithDefault(String settingValue, int defaultValue) {
+ try {
+ return settingValue != null ? Integer.parseInt(settingValue) : defaultValue;
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+
+ private static long parseLongSetting(String settingValue, String settingName)
+ throws SettingNotFoundException {
+ if (settingValue == null) {
+ throw new SettingNotFoundException(settingName);
+ }
+ try {
+ return Long.parseLong(settingValue);
+ } catch (NumberFormatException e) {
+ throw new SettingNotFoundException(settingName);
+ }
+ }
+
+ private static long parseLongSettingWithDefault(String settingValue, long defaultValue) {
+ try {
+ return settingValue != null ? Long.parseLong(settingValue) : defaultValue;
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+
/**
* System settings, containing miscellaneous system preferences. This
* table holds simple name/value pairs. There are convenience
@@ -3603,11 +3663,7 @@
@UnsupportedAppUsage
public static int getIntForUser(ContentResolver cr, String name, int def, int userHandle) {
String v = getStringForUser(cr, name, userHandle);
- try {
- return v != null ? Integer.parseInt(v) : def;
- } catch (NumberFormatException e) {
- return def;
- }
+ return parseIntSettingWithDefault(v, def);
}
/**
@@ -3638,11 +3694,7 @@
public static int getIntForUser(ContentResolver cr, String name, int userHandle)
throws SettingNotFoundException {
String v = getStringForUser(cr, name, userHandle);
- try {
- return Integer.parseInt(v);
- } catch (NumberFormatException e) {
- throw new SettingNotFoundException(name);
- }
+ return parseIntSetting(v, name);
}
/**
@@ -3690,14 +3742,8 @@
/** @hide */
public static long getLongForUser(ContentResolver cr, String name, long def,
int userHandle) {
- String valString = getStringForUser(cr, name, userHandle);
- long value;
- try {
- value = valString != null ? Long.parseLong(valString) : def;
- } catch (NumberFormatException e) {
- value = def;
- }
- return value;
+ String v = getStringForUser(cr, name, userHandle);
+ return parseLongSettingWithDefault(v, def);
}
/**
@@ -3725,12 +3771,8 @@
/** @hide */
public static long getLongForUser(ContentResolver cr, String name, int userHandle)
throws SettingNotFoundException {
- String valString = getStringForUser(cr, name, userHandle);
- try {
- return Long.parseLong(valString);
- } catch (NumberFormatException e) {
- throw new SettingNotFoundException(name);
- }
+ String v = getStringForUser(cr, name, userHandle);
+ return parseLongSetting(v, name);
}
/**
@@ -3778,11 +3820,7 @@
public static float getFloatForUser(ContentResolver cr, String name, float def,
int userHandle) {
String v = getStringForUser(cr, name, userHandle);
- try {
- return v != null ? Float.parseFloat(v) : def;
- } catch (NumberFormatException e) {
- return def;
- }
+ return parseFloatSettingWithDefault(v, def);
}
/**
@@ -3812,14 +3850,7 @@
public static float getFloatForUser(ContentResolver cr, String name, int userHandle)
throws SettingNotFoundException {
String v = getStringForUser(cr, name, userHandle);
- if (v == null) {
- throw new SettingNotFoundException(name);
- }
- try {
- return Float.parseFloat(v);
- } catch (NumberFormatException e) {
- throw new SettingNotFoundException(name);
- }
+ return parseFloatSetting(v, name);
}
/**
@@ -6070,11 +6101,7 @@
@UnsupportedAppUsage
public static int getIntForUser(ContentResolver cr, String name, int def, int userHandle) {
String v = getStringForUser(cr, name, userHandle);
- try {
- return v != null ? Integer.parseInt(v) : def;
- } catch (NumberFormatException e) {
- return def;
- }
+ return parseIntSettingWithDefault(v, def);
}
/**
@@ -6104,11 +6131,7 @@
public static int getIntForUser(ContentResolver cr, String name, int userHandle)
throws SettingNotFoundException {
String v = getStringForUser(cr, name, userHandle);
- try {
- return Integer.parseInt(v);
- } catch (NumberFormatException e) {
- throw new SettingNotFoundException(name);
- }
+ return parseIntSetting(v, name);
}
/**
@@ -6157,14 +6180,8 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static long getLongForUser(ContentResolver cr, String name, long def,
int userHandle) {
- String valString = getStringForUser(cr, name, userHandle);
- long value;
- try {
- value = valString != null ? Long.parseLong(valString) : def;
- } catch (NumberFormatException e) {
- value = def;
- }
- return value;
+ String v = getStringForUser(cr, name, userHandle);
+ return parseLongSettingWithDefault(v, def);
}
/**
@@ -6192,12 +6209,8 @@
/** @hide */
public static long getLongForUser(ContentResolver cr, String name, int userHandle)
throws SettingNotFoundException {
- String valString = getStringForUser(cr, name, userHandle);
- try {
- return Long.parseLong(valString);
- } catch (NumberFormatException e) {
- throw new SettingNotFoundException(name);
- }
+ String v = getStringForUser(cr, name, userHandle);
+ return parseLongSetting(v, name);
}
/**
@@ -6246,11 +6259,7 @@
public static float getFloatForUser(ContentResolver cr, String name, float def,
int userHandle) {
String v = getStringForUser(cr, name, userHandle);
- try {
- return v != null ? Float.parseFloat(v) : def;
- } catch (NumberFormatException e) {
- return def;
- }
+ return parseFloatSettingWithDefault(v, def);
}
/**
@@ -6280,14 +6289,7 @@
public static float getFloatForUser(ContentResolver cr, String name, int userHandle)
throws SettingNotFoundException {
String v = getStringForUser(cr, name, userHandle);
- if (v == null) {
- throw new SettingNotFoundException(name);
- }
- try {
- return Float.parseFloat(v);
- } catch (NumberFormatException e) {
- throw new SettingNotFoundException(name);
- }
+ return parseFloatSetting(v, name);
}
/**
@@ -15429,11 +15431,7 @@
*/
public static int getInt(ContentResolver cr, String name, int def) {
String v = getString(cr, name);
- try {
- return v != null ? Integer.parseInt(v) : def;
- } catch (NumberFormatException e) {
- return def;
- }
+ return parseIntSettingWithDefault(v, def);
}
/**
@@ -15457,11 +15455,7 @@
public static int getInt(ContentResolver cr, String name)
throws SettingNotFoundException {
String v = getString(cr, name);
- try {
- return Integer.parseInt(v);
- } catch (NumberFormatException e) {
- throw new SettingNotFoundException(name);
- }
+ return parseIntSetting(v, name);
}
/**
@@ -15496,14 +15490,8 @@
* or not a valid {@code long}.
*/
public static long getLong(ContentResolver cr, String name, long def) {
- String valString = getString(cr, name);
- long value;
- try {
- value = valString != null ? Long.parseLong(valString) : def;
- } catch (NumberFormatException e) {
- value = def;
- }
- return value;
+ String v = getString(cr, name);
+ return parseLongSettingWithDefault(v, def);
}
/**
@@ -15525,12 +15513,8 @@
*/
public static long getLong(ContentResolver cr, String name)
throws SettingNotFoundException {
- String valString = getString(cr, name);
- try {
- return Long.parseLong(valString);
- } catch (NumberFormatException e) {
- throw new SettingNotFoundException(name);
- }
+ String v = getString(cr, name);
+ return parseLongSetting(v, name);
}
/**
@@ -15566,11 +15550,7 @@
*/
public static float getFloat(ContentResolver cr, String name, float def) {
String v = getString(cr, name);
- try {
- return v != null ? Float.parseFloat(v) : def;
- } catch (NumberFormatException e) {
- return def;
- }
+ return parseFloatSettingWithDefault(v, def);
}
/**
@@ -15594,14 +15574,7 @@
public static float getFloat(ContentResolver cr, String name)
throws SettingNotFoundException {
String v = getString(cr, name);
- if (v == null) {
- throw new SettingNotFoundException(name);
- }
- try {
- return Float.parseFloat(v);
- } catch (NumberFormatException e) {
- throw new SettingNotFoundException(name);
- }
+ return parseFloatSetting(v, name);
}
/**
diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java
new file mode 100644
index 0000000..50f9d8a
--- /dev/null
+++ b/core/java/android/service/dreams/DreamOverlayService.java
@@ -0,0 +1,77 @@
+/*
+ * 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.service.dreams;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.WindowManager;
+
+
+/**
+ * Basic implementation of for {@link IDreamOverlay} for testing.
+ * @hide
+ */
+@TestApi
+public abstract class DreamOverlayService extends Service {
+ private static final String TAG = "DreamOverlayService";
+ private static final boolean DEBUG = false;
+
+ private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() {
+ @Override
+ public void startDream(WindowManager.LayoutParams layoutParams,
+ IDreamOverlayCallback callback) {
+ mDreamOverlayCallback = callback;
+ onStartDream(layoutParams);
+ }
+ };
+
+ IDreamOverlayCallback mDreamOverlayCallback;
+
+ public DreamOverlayService() {
+ }
+
+ @Nullable
+ @Override
+ public final IBinder onBind(@NonNull Intent intent) {
+ return mDreamOverlay.asBinder();
+ }
+
+ /**
+ * This method is overridden by implementations to handle when the dream has started and the
+ * window is ready to be interacted with.
+ * @param layoutParams The {@link android.view.WindowManager.LayoutParams} associated with the
+ * dream window.
+ */
+ public abstract void onStartDream(@NonNull WindowManager.LayoutParams layoutParams);
+
+ /**
+ * This method is invoked to request the dream exit.
+ */
+ public final void requestExit() {
+ try {
+ mDreamOverlayCallback.onExitRequested();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not request exit:" + e);
+ }
+ }
+}
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 096595f..3ab6907 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -203,7 +203,6 @@
private boolean mCanDoze;
private boolean mDozing;
private boolean mWindowless;
- private boolean mOverlayServiceBound;
private int mDozeScreenState = Display.STATE_UNKNOWN;
private int mDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
@@ -220,10 +219,34 @@
// A Queue of pending requests to execute on the overlay.
private ArrayDeque<Consumer<IDreamOverlay>> mRequests;
+ private boolean mBound;
+
OverlayConnection() {
mRequests = new ArrayDeque<>();
}
+ public void bind(Context context, @Nullable ComponentName overlayService) {
+ if (overlayService == null) {
+ return;
+ }
+
+ final Intent overlayIntent = new Intent();
+ overlayIntent.setComponent(overlayService);
+
+ context.bindService(overlayIntent,
+ this, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE);
+ mBound = true;
+ }
+
+ public void unbind(Context context) {
+ if (!mBound) {
+ return;
+ }
+
+ context.unbindService(this);
+ mBound = false;
+ }
+
public void request(Consumer<IDreamOverlay> request) {
mRequests.push(request);
evaluate();
@@ -930,14 +953,8 @@
mDreamServiceWrapper = new DreamServiceWrapper();
// Connect to the overlay service if present.
- final ComponentName overlayComponent =
- intent.getParcelableExtra(EXTRA_DREAM_OVERLAY_COMPONENT);
- if (overlayComponent != null && !mWindowless) {
- final Intent overlayIntent = new Intent();
- overlayIntent.setComponent(overlayComponent);
-
- mOverlayServiceBound = getApplicationContext().bindService(overlayIntent,
- mOverlayConnection, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE);
+ if (!mWindowless) {
+ mOverlayConnection.bind(this, intent.getParcelableExtra(EXTRA_DREAM_OVERLAY_COMPONENT));
}
return mDreamServiceWrapper;
@@ -973,10 +990,7 @@
return;
}
- if (!mWindowless && mOverlayServiceBound) {
- unbindService(mOverlayConnection);
- mOverlayServiceBound = false;
- }
+ mOverlayConnection.unbind(this);
try {
// finishSelf will unbind the dream controller from the dream service. This will
@@ -1173,6 +1187,16 @@
@Override
public void onViewAttachedToWindow(View v) {
mDispatchAfterOnAttachedToWindow.run();
+
+ // Request the DreamOverlay be told to dream with dream's window parameters
+ // once the window has been attached.
+ mOverlayConnection.request(overlay -> {
+ try {
+ overlay.startDream(mWindow.getAttributes(), mOverlayCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "could not send window attributes:" + e);
+ }
+ });
}
@Override
@@ -1185,16 +1209,6 @@
}
}
});
-
- // Request the DreamOverlay be told to dream with dream's window parameters once the service
- // has connected.
- mOverlayConnection.request(overlay -> {
- try {
- overlay.startDream(mWindow.getAttributes(), mOverlayCallback);
- } catch (RemoteException e) {
- Log.e(TAG, "could not send window attributes:" + e);
- }
- });
}
private boolean getWindowFlagValue(int flag, boolean defaultValue) {
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index d39b56d..5b9d69c 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -191,20 +191,6 @@
public static final int LISTEN_SIGNAL_STRENGTHS = 0x00000100;
/**
- * Listen for changes of the network signal strengths (cellular) always reported from modem,
- * even in some situations such as the screen of the device is off.
- *
- * @see #onSignalStrengthsChanged
- *
- * @hide
- * @deprecated Use TelephonyManager#setSignalStrengthUpdateRequest
- * instead.
- */
- @Deprecated
- @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
- public static final int LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH = 0x00000200;
-
- /**
* Listen for changes to observed cell info.
*
* Listening to this event requires the {@link Manifest.permission#READ_PHONE_STATE} and
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index bedad73..cb1cff9 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -1041,10 +1041,6 @@
eventList.add(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED);
}
- if ((eventMask & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) {
- eventList.add(TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED);
- }
-
if ((eventMask & PhoneStateListener.LISTEN_CELL_INFO) != 0) {
eventList.add(TelephonyCallback.EVENT_CELL_INFO_CHANGED);
}
diff --git a/core/java/android/util/AtomicFile.java b/core/java/android/util/AtomicFile.java
index e2e9479..be8e2c2 100644
--- a/core/java/android/util/AtomicFile.java
+++ b/core/java/android/util/AtomicFile.java
@@ -330,6 +330,11 @@
}
}
+ @Override
+ public String toString() {
+ return "AtomicFile[" + mBaseName + "]";
+ }
+
private static void rename(File source, File target) {
// We used to delete the target file before rename, but that isn't atomic, and the rename()
// syscall should atomically replace the target file. However in the case where the target
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 2c89a15..7e2792c 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -79,7 +79,7 @@
DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "true");
DEFAULT_FLAGS.put(SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES, "true");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_SECURITY_HUB, "true");
- DEFAULT_FLAGS.put(SETTINGS_SUPPORT_LARGE_SCREEN, "false");
+ DEFAULT_FLAGS.put(SETTINGS_SUPPORT_LARGE_SCREEN, "true");
DEFAULT_FLAGS.put(SETTINGS_APP_LANGUAGE_SELECTION, "false");
}
diff --git a/core/java/android/view/IRecentsAnimationRunner.aidl b/core/java/android/view/IRecentsAnimationRunner.aidl
index 8111755..aca17e4 100644
--- a/core/java/android/view/IRecentsAnimationRunner.aidl
+++ b/core/java/android/view/IRecentsAnimationRunner.aidl
@@ -35,15 +35,17 @@
* wallpaper not drawing in time, or the handler not finishing the animation within a predefined
* amount of time.
*
- * @param taskSnapshot If the snapshot is null, the animation will be cancelled and the leash
- * will be inactive immediately. Otherwise, the contents of the task will be
- * replaced with {@param taskSnapshot}, such that the runner's leash is
- * still active. As soon as the runner doesn't need the leash anymore, it
- * must call {@link IRecentsAnimationController#cleanupScreenshot).
+ * @param taskIds Indicates tasks with cancelling snapshot.
+ * @param taskSnapshots If the snapshots is null, the animation will be cancelled and the leash
+ * will be inactive immediately. Otherwise, the contents of the tasks will
+ * be replaced with {@param taskSnapshots}, such that the runner's leash is
+ * still active. As soon as the runner doesn't need the leash anymore, it
+ * must call {@link IRecentsAnimationController#cleanupScreenshot).
*
* @see {@link RecentsAnimationController#cleanupScreenshot}
*/
- void onAnimationCanceled(in @nullable TaskSnapshot taskSnapshot) = 1;
+ void onAnimationCanceled(in @nullable int[] taskIds,
+ in @nullable TaskSnapshot[] taskSnapshots) = 1;
/**
* Called when the system is ready for the handler to start animating all the visible tasks.
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 0686104..02b2c5d 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -124,7 +124,7 @@
public void setControl(@Nullable InsetsSourceControl control, int[] showTypes,
int[] hideTypes) {
super.setControl(control, showTypes, hideTypes);
- if (control == null && !mIsRequestedVisibleAwaitingControl) {
+ if (control == null && !isRequestedVisibleAwaitingControl()) {
hide();
removeSurface();
}
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index 2884d22..2165f55 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -174,10 +174,10 @@
* Called when the display for the window associated with the input channel has entered or
* exited touch mode.
*
- * @param isInTouchMode {@code true} if the display showing the window associated with the
+ * @param inTouchMode {@code true} if the display showing the window associated with the
* input channel entered touch mode.
*/
- public void onTouchModeChanged(boolean isInTouchMode) {
+ public void onTouchModeChanged(boolean inTouchMode) {
}
/**
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 7d8d653..805727c 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -284,8 +284,8 @@
mShownOnFinish, mCurrentAlpha, mCurrentInsets));
mController.notifyFinished(this, mShownOnFinish);
releaseLeashes();
+ if (DEBUG) Log.d(TAG, "Animation finished abruptly.");
}
- if (DEBUG) Log.d(TAG, "Animation finished abruptly.");
return mFinished;
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 1dd5a1b..71c1b7c 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1266,6 +1266,11 @@
public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
cancelAnimation(runner, false /* invokeCallback */);
if (DEBUG) Log.d(TAG, "notifyFinished. shown: " + shown);
+ if (runner.getAnimationType() == ANIMATION_TYPE_RESIZE) {
+ // The resize animation doesn't show or hide the insets. We shouldn't change the
+ // requested visibility.
+ return;
+ }
if (shown) {
showDirectly(runner.getTypes(), true /* fromIme */);
} else {
diff --git a/core/java/android/view/InsetsResizeAnimationRunner.java b/core/java/android/view/InsetsResizeAnimationRunner.java
index e1352dd..edcfc95 100644
--- a/core/java/android/view/InsetsResizeAnimationRunner.java
+++ b/core/java/android/view/InsetsResizeAnimationRunner.java
@@ -131,6 +131,9 @@
@Override
public boolean applyChangeInsets(InsetsState outState) {
+ if (mCancelled) {
+ return false;
+ }
final float fraction = mAnimation.getInterpolatedFraction();
for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
final InsetsSource fromSource = mFromState.peekSource(type);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 30160c3..af7d86c 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -230,8 +230,6 @@
float frameRate, int compatibility, int changeFrameRateStrategy);
private static native long nativeGetHandle(long nativeObject);
- private static native long nativeAcquireFrameRateFlexibilityToken();
- private static native void nativeReleaseFrameRateFlexibilityToken(long token);
private static native void nativeSetFixedTransformHint(long transactionObj, long nativeObject,
int transformHint);
private static native void nativeSetFocusedWindow(long transactionObj, IBinder toToken,
@@ -3709,27 +3707,6 @@
}
/**
- * Acquire a frame rate flexibility token, which allows surface flinger to freely switch display
- * frame rates. This is used by CTS tests to put the device in a consistent state. See
- * ISurfaceComposer::acquireFrameRateFlexibilityToken(). The caller must have the
- * ACCESS_SURFACE_FLINGER permission, or else the call will fail, returning 0.
- * @hide
- */
- @TestApi
- public static long acquireFrameRateFlexibilityToken() {
- return nativeAcquireFrameRateFlexibilityToken();
- }
-
- /**
- * Release a frame rate flexibility token.
- * @hide
- */
- @TestApi
- public static void releaseFrameRateFlexibilityToken(long token) {
- nativeReleaseFrameRateFlexibilityToken(token);
- }
-
- /**
* This is a refactoring utility function to enable lower levels of code to be refactored
* from using the global transaction (and instead use a passed in Transaction) without
* having to refactor the higher levels at the same time.
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 1c44f443..1ddb043 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -162,8 +162,6 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
boolean mIsCreating = false;
- private volatile boolean mRtHandlingPositionUpdates = false;
- private volatile boolean mRtReleaseSurfaces = false;
private final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener =
this::updateSurface;
@@ -909,13 +907,14 @@
mBlastBufferQueue = null;
}
- if (mRtHandlingPositionUpdates) {
- mRtReleaseSurfaces = true;
- return;
+ ViewRootImpl viewRoot = getViewRootImpl();
+ Transaction transaction = new Transaction();
+ releaseSurfaces(transaction);
+ if (viewRoot != null) {
+ viewRoot.applyTransactionOnDraw(transaction);
+ } else {
+ transaction.apply();
}
-
- releaseSurfaces(mTmpTransaction);
- mTmpTransaction.apply();
}
}
@@ -1468,15 +1467,6 @@
if (mSurfaceControl == null) {
return;
}
- // TODO: This is teensy bit racey in that a brand new SurfaceView moving on
- // its 2nd frame if RenderThread is running slowly could potentially see
- // this as false, enter the branch, get pre-empted, then this comes along
- // and reports a new position, then the UI thread resumes and reports
- // its position. This could therefore be de-sync'd in that interval, but
- // the synchronization would violate the rule that RT must never block
- // on the UI thread which would open up potential deadlocks. The risk of
- // a single-frame desync is therefore preferable for now.
- mRtHandlingPositionUpdates = true;
}
if (mRTLastReportedPosition.left == left
&& mRTLastReportedPosition.top == top
@@ -1552,12 +1542,7 @@
return;
}
mRtTransaction.hide(mSurfaceControl);
- if (mRtReleaseSurfaces) {
- mRtReleaseSurfaces = false;
- releaseSurfaces(mRtTransaction);
- }
applyOrMergeTransaction(mRtTransaction, frameNumber);
- mRtHandlingPositionUpdates = false;
}
}
}
diff --git a/core/java/android/view/TaskTransitionSpec.java b/core/java/android/view/TaskTransitionSpec.java
index e90d6e1..5f498a1 100644
--- a/core/java/android/view/TaskTransitionSpec.java
+++ b/core/java/android/view/TaskTransitionSpec.java
@@ -39,6 +39,7 @@
/**
* TEMPORARY FIELD (b/202383002)
* TODO: Remove once we use surfaceflinger rounded corners on tasks rather than taskbar overlays
+ * or when shell transitions are fully enabled
*
* A set of {@InsetsState.InternalInsetsType}s we want to use as the source to set the bounds
* of the task during the animation. Used to make sure that task animate above the taskbar.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index dabe1e9..f529172 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -20288,7 +20288,7 @@
*/
@CallSuper
protected void onAttachedToWindow() {
- if ((mPrivateFlags & PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
+ if (mParent != null && (mPrivateFlags & PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
mParent.requestTransparentRegion(this);
}
@@ -25070,7 +25070,7 @@
View parent = this;
- while (parent.mParent != null && parent.mParent instanceof View) {
+ while (parent.mParent instanceof View) {
parent = (View) parent.mParent;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 4879206..be15965 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -294,21 +294,6 @@
*/
private static final int SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS = 2500;
- /**
- * If set to {@code true}, the new logic to layout system bars as normal window and to use
- * layout result to get insets will be applied. Otherwise, the old hard-coded window logic will
- * be applied.
- */
- private static final String USE_FLEXIBLE_INSETS = "persist.debug.flexible_insets";
-
- /**
- * A flag to indicate to use the new generalized insets window logic, or the old hard-coded
- * insets window layout logic.
- * {@hide}
- */
- public static final boolean INSETS_LAYOUT_GENERALIZATION =
- SystemProperties.getBoolean(USE_FLEXIBLE_INSETS, true);
-
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
@@ -862,6 +847,13 @@
}
}
+ /** Remove a static config callback. */
+ public static void removeConfigCallback(ConfigChangedCallback callback) {
+ synchronized (sConfigCallbacks) {
+ sConfigCallbacks.remove(callback);
+ }
+ }
+
/** Add activity config callback to be notified about override config changes. */
public void setActivityConfigCallback(ActivityConfigCallback callback) {
mActivityConfigCallback = callback;
@@ -3384,14 +3376,12 @@
private void handleWindowFocusChanged() {
final boolean hasWindowFocus;
- final boolean inTouchMode;
synchronized (this) {
if (!mWindowFocusChanged) {
return;
}
mWindowFocusChanged = false;
hasWindowFocus = mUpcomingWindowFocus;
- inTouchMode = mUpcomingInTouchMode;
}
// TODO (b/131181940): Make sure this doesn't leak Activity with mActivityConfigCallback
// config changes.
@@ -3404,9 +3394,7 @@
if (mAdded) {
profileRendering(hasWindowFocus);
-
if (hasWindowFocus) {
- ensureTouchModeLocally(inTouchMode);
if (mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) {
mFullRedrawNeeded = true;
try {
@@ -3478,6 +3466,14 @@
}
}
+ private void handleWindowTouchModeChanged() {
+ final boolean inTouchMode;
+ synchronized (this) {
+ inTouchMode = mUpcomingInTouchMode;
+ }
+ ensureTouchModeLocally(inTouchMode);
+ }
+
private void fireAccessibilityFocusEventIfHasFocusedNode() {
if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
return;
@@ -5135,6 +5131,7 @@
private static final int MSG_SHOW_INSETS = 34;
private static final int MSG_HIDE_INSETS = 35;
private static final int MSG_REQUEST_SCROLL_CAPTURE = 36;
+ private static final int MSG_WINDOW_TOUCH_MODE_CHANGED = 37;
final class ViewRootHandler extends Handler {
@@ -5201,6 +5198,8 @@
return "MSG_SHOW_INSETS";
case MSG_HIDE_INSETS:
return "MSG_HIDE_INSETS";
+ case MSG_WINDOW_TOUCH_MODE_CHANGED:
+ return "MSG_WINDOW_TOUCH_MODE_CHANGED";
}
return super.getMessageName(message);
}
@@ -5323,9 +5322,12 @@
case MSG_WINDOW_FOCUS_CHANGED: {
handleWindowFocusChanged();
} break;
- case MSG_DIE:
+ case MSG_WINDOW_TOUCH_MODE_CHANGED: {
+ handleWindowTouchModeChanged();
+ } break;
+ case MSG_DIE: {
doDie();
- break;
+ } break;
case MSG_DISPATCH_INPUT_EVENT: {
SomeArgs args = (SomeArgs) msg.obj;
InputEvent event = (InputEvent) args.arg1;
@@ -8696,6 +8698,11 @@
}
@Override
+ public void onTouchModeChanged(boolean inTouchMode) {
+ touchModeChanged(inTouchMode);
+ }
+
+ @Override
public void onPointerCaptureEvent(boolean pointerCaptureEnabled) {
dispatchPointerCaptureChanged(pointerCaptureEnabled);
}
@@ -8953,17 +8960,33 @@
mHandler.sendMessage(msg);
}
- public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
+ /**
+ * Notifies this {@link ViewRootImpl} object that window focus has changed.
+ */
+ public void windowFocusChanged(boolean hasFocus, boolean unusedInTouchMode) {
+ // TODO(b/193718270): Remove inTouchMode parameter from this method and update related code
+ // accordingly.
synchronized (this) {
mWindowFocusChanged = true;
mUpcomingWindowFocus = hasFocus;
- mUpcomingInTouchMode = inTouchMode;
}
Message msg = Message.obtain();
msg.what = MSG_WINDOW_FOCUS_CHANGED;
mHandler.sendMessage(msg);
}
+ /**
+ * Notifies this {@link ViewRootImpl} object that touch mode state has changed.
+ */
+ public void touchModeChanged(boolean inTouchMode) {
+ synchronized (this) {
+ mUpcomingInTouchMode = inTouchMode;
+ }
+ Message msg = Message.obtain();
+ msg.what = MSG_WINDOW_TOUCH_MODE_CHANGED;
+ mHandler.sendMessage(msg);
+ }
+
public void dispatchWindowShown() {
mHandler.sendEmptyMessage(MSG_DISPATCH_WINDOW_SHOWN);
}
@@ -10391,9 +10414,11 @@
@Override
public boolean applyTransactionOnDraw(@NonNull SurfaceControl.Transaction t) {
- registerRtFrameCallback(frame -> {
- mergeWithNextTransaction(t, frame);
- });
+ if (mRemoved) {
+ t.apply();
+ } else {
+ registerRtFrameCallback(frame -> mergeWithNextTransaction(t, frame));
+ }
return true;
}
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index c4540b0..624937f 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -979,4 +979,38 @@
.build();
return mTargetView.performReceiveContent(payload) == null;
}
+
+ /**
+ * Default implementation that constructs {@link TextSnapshot} with information extracted from
+ * {@link BaseInputConnection}.
+ *
+ * @return {@code null} when {@link TextSnapshot} cannot be fully taken.
+ */
+ @Nullable
+ @Override
+ public TextSnapshot takeSnapshot() {
+ final Editable content = getEditable();
+ if (content == null) {
+ return null;
+ }
+ int composingStart = getComposingSpanStart(content);
+ int composingEnd = getComposingSpanEnd(content);
+ if (composingEnd < composingStart) {
+ final int tmp = composingStart;
+ composingStart = composingEnd;
+ composingEnd = tmp;
+ }
+
+ final SurroundingText surroundingText = getSurroundingText(
+ EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH / 2,
+ EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH / 2, GET_TEXT_WITH_STYLES);
+ if (surroundingText == null) {
+ return null;
+ }
+
+ final int cursorCapsMode = getCursorCapsMode(TextUtils.CAP_MODE_CHARACTERS
+ | TextUtils.CAP_MODE_WORDS | TextUtils.CAP_MODE_SENTENCES);
+
+ return new TextSnapshot(surroundingText, composingStart, composingEnd, cursorCapsMode);
+ }
}
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index fdcc208..c3d7836 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -1034,4 +1034,27 @@
default boolean setImeConsumesInput(boolean imeConsumesInput) {
return false;
}
+
+ /**
+ * Called by the system when it needs to take a snapshot of multiple text-related data in an
+ * atomic manner.
+ *
+ * <p><strong>Editor authors</strong>: Supporting this method is strongly encouraged. Atomically
+ * taken {@link TextSnapshot} is going to be really helpful for the system when optimizing IPCs
+ * in a safe and deterministic manner. Return {@code null} if an atomically taken
+ * {@link TextSnapshot} is unavailable. The system continues supporting such a scenario
+ * gracefully.</p>
+ *
+ * <p><strong>IME authors</strong>: Currently IMEs cannot call this method directly and always
+ * receive {@code null} as the result.</p>
+ *
+ * @return {@code null} if {@link TextSnapshot} is unavailable and/or this API is called from
+ * IMEs.
+ */
+ @Nullable
+ default TextSnapshot takeSnapshot() {
+ // Returning null by default because the composing text range cannot be retrieved from
+ // existing APIs.
+ return null;
+ }
}
diff --git a/core/java/android/view/inputmethod/TextSnapshot.java b/core/java/android/view/inputmethod/TextSnapshot.java
new file mode 100644
index 0000000..33ce282
--- /dev/null
+++ b/core/java/android/view/inputmethod/TextSnapshot.java
@@ -0,0 +1,144 @@
+/*
+ * 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.view.inputmethod;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * An immutable data snapshot of text editing state.
+ */
+public final class TextSnapshot {
+ @NonNull
+ private final SurroundingText mSurroundingText;
+ @IntRange(from = -1)
+ private final int mCompositionStart;
+ @IntRange(from = -1)
+ private final int mCompositionEnd;
+ private final int mCursorCapsMode;
+
+ /**
+ * Creates a new instance of {@link TextSnapshot}
+ *
+ * @param surroundingText {@link SurroundingText} of the current edit field.
+ * @param compositionStart The start index of the composing text.
+ * {@code -1} if there is no composing text.
+ * @param compositionEnd The end index of the composing text.
+ * {@code -1} if there is no composing text.
+ * @param cursorCapsMode The capitalization mode of the first character being edited in the
+ * text. See {@link EditorInfo#initialCapsMode}.
+ * @throws NullPointerException if {@code surroundingText} is {@code null}.
+ * @throws IllegalArgumentException if {@code compositionStart} and/or {@code compositionEnd}
+ * is less than {@code -1}.
+ */
+ public TextSnapshot(@NonNull SurroundingText surroundingText,
+ @IntRange(from = -1) int compositionStart, @IntRange(from = -1) int compositionEnd,
+ int cursorCapsMode) {
+ Objects.requireNonNull(surroundingText);
+ mSurroundingText = surroundingText;
+ if (compositionStart < -1) {
+ throw new IllegalArgumentException("compositionStart must be -1 or higher but was "
+ + compositionStart);
+ }
+ if (compositionEnd < -1) {
+ throw new IllegalArgumentException("compositionEnd must be -1 or higher but was "
+ + compositionEnd);
+ }
+ if (compositionStart == -1 && compositionEnd != -1) {
+ throw new IllegalArgumentException("compositionEnd must be -1 if compositionStart is "
+ + "-1 but was " + compositionEnd);
+ }
+ if (compositionStart != -1 && compositionEnd == -1) {
+ throw new IllegalArgumentException("compositionStart must be -1 if compositionEnd is "
+ + "-1 but was " + compositionStart);
+ }
+ if (compositionStart > compositionEnd) {
+ throw new IllegalArgumentException("compositionStart=" + compositionStart + " must be"
+ + " equal to or greater than compositionEnd=" + compositionEnd);
+ }
+ mCompositionStart = compositionStart;
+ mCompositionEnd = compositionEnd;
+ mCursorCapsMode = cursorCapsMode;
+ }
+
+ /**
+ * @return {@link SurroundingText} of the current edit field.
+ */
+ @NonNull
+ public SurroundingText getSurroundingText() {
+ return mSurroundingText;
+ }
+
+ /**
+ * @return The start index of the selection range. {@code -1} if it is not available.
+ */
+ @IntRange(from = -1)
+ public int getSelectionStart() {
+ if (mSurroundingText.getOffset() < 0) {
+ return -1;
+ }
+ return mSurroundingText.getSelectionStart() + mSurroundingText.getOffset();
+ }
+
+ /**
+ * @return The end index of the selection range. {@code -1} if it is not available.
+ */
+ @IntRange(from = -1)
+ public int getSelectionEnd() {
+ if (mSurroundingText.getOffset() < 0) {
+ return -1;
+ }
+ return mSurroundingText.getSelectionEnd() + mSurroundingText.getOffset();
+ }
+
+ /**
+ * @return The end index of the composing text. {@code -1} if there is no composing text.
+ */
+ @IntRange(from = -1)
+ public int getCompositionStart() {
+ return mCompositionStart;
+ }
+
+ /**
+ * @return The end index of the composing text. {@code -1} if there is no composing text.
+ */
+ @IntRange(from = -1)
+ public int getCompositionEnd() {
+ return mCompositionEnd;
+ }
+
+ /**
+ * The capitalization mode of the first character being edited in the text.
+ *
+ * <p>Values may be any combination of the following values:</p>
+ * <ul>
+ * <li>{@link android.text.TextUtils#CAP_MODE_CHARACTERS TextUtils.CAP_MODE_CHARACTERS}</li>
+ * <li>{@link android.text.TextUtils#CAP_MODE_CHARACTERS TextUtils.CAP_MODE_WORDS}</li>
+ * <li>{@link android.text.TextUtils#CAP_MODE_CHARACTERS TextUtils.CAP_MODE_SENTENCES}</li>
+ * </ul>
+ *
+ * <p>You should generally just take a non-zero value to mean "start out in caps mode" though.
+ * </p>
+ * @see EditorInfo#initialCapsMode
+ */
+ public int getCursorCapsMode() {
+ return mCursorCapsMode;
+ }
+}
+
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 2108f8ea..da09489 100755
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -492,6 +492,13 @@
if (!isFocusable()) return false;
// If it is not iconified, then give the focus to the text field
if (!isIconified()) {
+ if (direction == FOCUS_BACKWARD) {
+ final View found = focusSearch(FOCUS_BACKWARD);
+ if (found != null) {
+ return found.requestFocus(FOCUS_BACKWARD, previouslyFocusedRect);
+ }
+ return false;
+ }
boolean result = mSearchSrcTextView.requestFocus(direction, previouslyFocusedRect);
if (result) {
updateViewsVisibility(false);
diff --git a/core/java/android/window/RemoteTransition.java b/core/java/android/window/RemoteTransition.java
index b243b65..4bd15f2 100644
--- a/core/java/android/window/RemoteTransition.java
+++ b/core/java/android/window/RemoteTransition.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.IApplicationThread;
import android.os.IBinder;
import android.os.Parcelable;
@@ -34,6 +35,14 @@
/** The actual remote-transition interface used to run the transition animation. */
private @NonNull IRemoteTransition mRemoteTransition;
+ /** The application thread that will be running the remote transition. */
+ private @Nullable IApplicationThread mAppThread;
+
+ /** Constructs with no app thread (animation runs in shell). */
+ public RemoteTransition(@NonNull IRemoteTransition remoteTransition) {
+ this(remoteTransition, null /* appThread */);
+ }
+
/** Get the IBinder associated with the underlying IRemoteTransition. */
public @Nullable IBinder asBinder() {
return mRemoteTransition.asBinder();
@@ -59,13 +68,17 @@
*
* @param remoteTransition
* The actual remote-transition interface used to run the transition animation.
+ * @param appThread
+ * The application thread that will be running the remote transition.
*/
@DataClass.Generated.Member
public RemoteTransition(
- @NonNull IRemoteTransition remoteTransition) {
+ @NonNull IRemoteTransition remoteTransition,
+ @Nullable IApplicationThread appThread) {
this.mRemoteTransition = remoteTransition;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mRemoteTransition);
+ this.mAppThread = appThread;
// onConstructed(); // You can define this method to get a callback
}
@@ -79,6 +92,14 @@
}
/**
+ * The application thread that will be running the remote transition.
+ */
+ @DataClass.Generated.Member
+ public @Nullable IApplicationThread getAppThread() {
+ return mAppThread;
+ }
+
+ /**
* The actual remote-transition interface used to run the transition animation.
*/
@DataClass.Generated.Member
@@ -89,6 +110,15 @@
return this;
}
+ /**
+ * The application thread that will be running the remote transition.
+ */
+ @DataClass.Generated.Member
+ public @NonNull RemoteTransition setAppThread(@NonNull IApplicationThread value) {
+ mAppThread = value;
+ return this;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -96,7 +126,8 @@
// String fieldNameToString() { ... }
return "RemoteTransition { " +
- "remoteTransition = " + mRemoteTransition +
+ "remoteTransition = " + mRemoteTransition + ", " +
+ "appThread = " + mAppThread +
" }";
}
@@ -106,7 +137,11 @@
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
+ byte flg = 0;
+ if (mAppThread != null) flg |= 0x2;
+ dest.writeByte(flg);
dest.writeStrongInterface(mRemoteTransition);
+ if (mAppThread != null) dest.writeStrongInterface(mAppThread);
}
@Override
@@ -120,11 +155,14 @@
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
+ byte flg = in.readByte();
IRemoteTransition remoteTransition = IRemoteTransition.Stub.asInterface(in.readStrongBinder());
+ IApplicationThread appThread = (flg & 0x2) == 0 ? null : IApplicationThread.Stub.asInterface(in.readStrongBinder());
this.mRemoteTransition = remoteTransition;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mRemoteTransition);
+ this.mAppThread = appThread;
// onConstructed(); // You can define this method to get a callback
}
@@ -144,10 +182,10 @@
};
@DataClass.Generated(
- time = 1630613039043L,
+ time = 1630690027011L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/RemoteTransition.java",
- inputSignatures = "private @android.annotation.NonNull android.window.IRemoteTransition mRemoteTransition\npublic @android.annotation.Nullable android.os.IBinder asBinder()\nclass RemoteTransition extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
+ inputSignatures = "private @android.annotation.NonNull android.window.IRemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.app.IApplicationThread mAppThread\npublic @android.annotation.Nullable android.os.IBinder asBinder()\nclass RemoteTransition extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/window/SplashScreen.java b/core/java/android/window/SplashScreen.java
index 3354a6c..b9bf009 100644
--- a/core/java/android/window/SplashScreen.java
+++ b/core/java/android/window/SplashScreen.java
@@ -92,6 +92,9 @@
* overrides and persists the theme used for the {@link SplashScreen} of this application.
* <p>
* To reset to the default theme, set this the themeId to {@link Resources#ID_NULL}.
+ * <p>
+ * <b>Note:</b> The theme name must be stable across versions, otherwise it won't be found
+ * after your application is updated.
*/
void setSplashScreenTheme(@StyleRes int themeId);
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
index f748d4b..f04155d 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -20,6 +20,10 @@
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLASHSCREEN_AVD;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -54,6 +58,7 @@
import android.widget.ImageView;
import com.android.internal.R;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.policy.DecorView;
import com.android.internal.util.ContrastColorUtil;
@@ -487,6 +492,23 @@
}
IconAnimateListener aniDrawable = (IconAnimateListener) iconDrawable;
aniDrawable.prepareAnimate(duration, this::animationStartCallback);
+ aniDrawable.setAnimationJankMonitoring(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ InteractionJankMonitor.getInstance().cancel(CUJ_SPLASHSCREEN_AVD);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ InteractionJankMonitor.getInstance().end(CUJ_SPLASHSCREEN_AVD);
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ InteractionJankMonitor.getInstance().begin(
+ SplashScreenView.this, CUJ_SPLASHSCREEN_AVD);
+ }
+ });
}
private void animationStartCallback() {
@@ -669,6 +691,12 @@
* Stop animation.
*/
void stopAnimation();
+
+ /**
+ * Provides a chance to start interaction jank monitoring in avd animation.
+ * @param listener a listener to start jank monitoring
+ */
+ default void setAnimationJankMonitoring(AnimatorListenerAdapter listener) {}
}
/**
diff --git a/core/java/android/window/TaskSnapshot.java b/core/java/android/window/TaskSnapshot.java
index f1e5fb9..b7bb608 100644
--- a/core/java/android/window/TaskSnapshot.java
+++ b/core/java/android/window/TaskSnapshot.java
@@ -50,6 +50,7 @@
/** The size of the snapshot before scaling */
private final Point mTaskSize;
private final Rect mContentInsets;
+ private final Rect mLetterboxInsets;
// Whether this snapshot is a down-sampled version of the high resolution snapshot, used
// mainly for loading snapshots quickly from disk when user is flinging fast
private final boolean mIsLowResolution;
@@ -67,9 +68,10 @@
public TaskSnapshot(long id,
@NonNull ComponentName topActivityComponent, HardwareBuffer snapshot,
@NonNull ColorSpace colorSpace, int orientation, int rotation, Point taskSize,
- Rect contentInsets, boolean isLowResolution, boolean isRealSnapshot,
- int windowingMode, @WindowInsetsController.Appearance int appearance,
- boolean isTranslucent, boolean hasImeSurface) {
+ Rect contentInsets, Rect letterboxInsets, boolean isLowResolution,
+ boolean isRealSnapshot, int windowingMode,
+ @WindowInsetsController.Appearance int appearance, boolean isTranslucent,
+ boolean hasImeSurface) {
mId = id;
mTopActivityComponent = topActivityComponent;
mSnapshot = snapshot;
@@ -79,6 +81,7 @@
mRotation = rotation;
mTaskSize = new Point(taskSize);
mContentInsets = new Rect(contentInsets);
+ mLetterboxInsets = new Rect(letterboxInsets);
mIsLowResolution = isLowResolution;
mIsRealSnapshot = isRealSnapshot;
mWindowingMode = windowingMode;
@@ -99,6 +102,7 @@
mRotation = source.readInt();
mTaskSize = source.readTypedObject(Point.CREATOR);
mContentInsets = source.readTypedObject(Rect.CREATOR);
+ mLetterboxInsets = source.readTypedObject(Rect.CREATOR);
mIsLowResolution = source.readBoolean();
mIsRealSnapshot = source.readBoolean();
mWindowingMode = source.readInt();
@@ -179,6 +183,14 @@
}
/**
+ * @return The letterbox insets on the snapshot. These can be clipped off in order to
+ * remove any letterbox areas in the snapshot.
+ */
+ public Rect getLetterboxInsets() {
+ return mLetterboxInsets;
+ }
+
+ /**
* @return Whether this snapshot is a down-sampled version of the full resolution.
*/
@UnsupportedAppUsage
@@ -241,6 +253,7 @@
dest.writeInt(mRotation);
dest.writeTypedObject(mTaskSize, 0);
dest.writeTypedObject(mContentInsets, 0);
+ dest.writeTypedObject(mLetterboxInsets, 0);
dest.writeBoolean(mIsLowResolution);
dest.writeBoolean(mIsRealSnapshot);
dest.writeInt(mWindowingMode);
@@ -262,6 +275,7 @@
+ " mRotation=" + mRotation
+ " mTaskSize=" + mTaskSize.toString()
+ " mContentInsets=" + mContentInsets.toShortString()
+ + " mLetterboxInsets=" + mLetterboxInsets.toShortString()
+ " mIsLowResolution=" + mIsLowResolution
+ " mIsRealSnapshot=" + mIsRealSnapshot
+ " mWindowingMode=" + mWindowingMode
@@ -289,6 +303,7 @@
private int mRotation;
private Point mTaskSize;
private Rect mContentInsets;
+ private Rect mLetterboxInsets;
private boolean mIsRealSnapshot;
private int mWindowingMode;
private @WindowInsetsController.Appearance
@@ -340,6 +355,11 @@
return this;
}
+ public Builder setLetterboxInsets(Rect letterboxInsets) {
+ mLetterboxInsets = letterboxInsets;
+ return this;
+ }
+
public Builder setIsRealSnapshot(boolean realSnapshot) {
mIsRealSnapshot = realSnapshot;
return this;
@@ -387,6 +407,7 @@
mRotation,
mTaskSize,
mContentInsets,
+ mLetterboxInsets,
// When building a TaskSnapshot with the Builder class, isLowResolution
// is always false. Low-res snapshots are only created when loading from
// disk.
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index 41efe28..ac9b2d8 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -923,7 +923,7 @@
return new BitmapDrawable(mCtx.getResources(), getIconBitmap(userHandle));
}
- public Bitmap getIconBitmap(UserHandle userHandle) {
+ public Bitmap getIconBitmap(@Nullable UserHandle userHandle) {
Drawable dr = null;
if (mHasSubstitutePermission) {
dr = getIconSubstituteInternal();
diff --git a/core/java/com/android/internal/app/SimpleIconFactory.java b/core/java/com/android/internal/app/SimpleIconFactory.java
index 2d91e64..43dacd7 100644
--- a/core/java/com/android/internal/app/SimpleIconFactory.java
+++ b/core/java/com/android/internal/app/SimpleIconFactory.java
@@ -194,7 +194,7 @@
* @deprecated Do not use, functionality will be replaced by iconloader lib eventually.
*/
@Deprecated
- Bitmap createUserBadgedIconBitmap(@Nullable Drawable icon, UserHandle user) {
+ Bitmap createUserBadgedIconBitmap(@Nullable Drawable icon, @Nullable UserHandle user) {
float [] scale = new float[1];
// If no icon is provided use the system default
diff --git a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
index 068b882..8a19f2d 100644
--- a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
+++ b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
@@ -165,7 +165,7 @@
// Now fetch app icon and raster with no badging even in work profile
Bitmap appIcon = mSelectableTargetInfoCommunicator.makePresentationGetter(info)
- .getIconBitmap(mContext.getUser());
+ .getIconBitmap(null);
// Raster target drawable with appIcon as a badge
SimpleIconFactory sif = SimpleIconFactory.obtain(mContext);
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index c863292..d14054d 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -99,6 +99,7 @@
private final ViewRootImpl.SurfaceChangedCallback mSurfaceChangedCallback;
private final Handler mHandler;
private final ChoreographerWrapper mChoreographer;
+ private final Object mLock = InteractionJankMonitor.getInstance().getLock();
@VisibleForTesting
public final boolean mSurfaceOnly;
@@ -181,7 +182,7 @@
mSurfaceChangedCallback = new ViewRootImpl.SurfaceChangedCallback() {
@Override
public void surfaceCreated(SurfaceControl.Transaction t) {
- synchronized (FrameTracker.this) {
+ synchronized (mLock) {
if (mSurfaceControl == null) {
mSurfaceControl = mViewRoot.getSurfaceControl();
if (mBeginVsyncId != INVALID_ID) {
@@ -203,12 +204,12 @@
// Wait a while to give the system a chance for the remaining
// frames to arrive, then force finish the session.
mHandler.postDelayed(() -> {
- synchronized (FrameTracker.this) {
+ synchronized (mLock) {
if (DEBUG) {
Log.d(TAG, "surfaceDestroyed: " + mSession.getName()
+ ", finalized=" + mMetricsFinalized
+ ", info=" + mJankInfos.size()
- + ", vsync=" + mBeginVsyncId + "-" + mEndVsyncId);
+ + ", vsync=" + mBeginVsyncId);
}
if (!mMetricsFinalized) {
end(REASON_END_SURFACE_DESTROYED);
@@ -227,20 +228,20 @@
/**
* Begin a trace session of the CUJ.
*/
- public synchronized void begin() {
- mBeginVsyncId = mChoreographer.getVsyncId() + 1;
- if (DEBUG) {
- Log.d(TAG, "begin: " + mSession.getName() + ", begin=" + mBeginVsyncId);
- }
- if (mSurfaceControl != null) {
- postTraceStartMarker();
- mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
- }
- if (!mSurfaceOnly) {
- mRendererWrapper.addObserver(mObserver);
- }
- if (mListener != null) {
- mListener.onCujEvents(mSession, ACTION_SESSION_BEGIN);
+ public void begin() {
+ synchronized (mLock) {
+ mBeginVsyncId = mChoreographer.getVsyncId() + 1;
+ if (DEBUG) {
+ Log.d(TAG, "begin: " + mSession.getName() + ", begin=" + mBeginVsyncId);
+ }
+ if (mSurfaceControl != null) {
+ postTraceStartMarker();
+ mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
+ }
+ if (!mSurfaceOnly) {
+ mRendererWrapper.addObserver(mObserver);
+ }
+ notifyCujEvent(ACTION_SESSION_BEGIN);
}
}
@@ -250,7 +251,7 @@
@VisibleForTesting
public void postTraceStartMarker() {
mChoreographer.mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, () -> {
- synchronized (FrameTracker.this) {
+ synchronized (mLock) {
if (mCancelled || mEndVsyncId != INVALID_ID) {
return;
}
@@ -263,88 +264,98 @@
/**
* End the trace session of the CUJ.
*/
- public synchronized void end(@Reasons int reason) {
- if (mEndVsyncId != INVALID_ID) return;
- mEndVsyncId = mChoreographer.getVsyncId();
+ public boolean end(@Reasons int reason) {
+ synchronized (mLock) {
+ if (mCancelled || mEndVsyncId != INVALID_ID) return false;
+ mEndVsyncId = mChoreographer.getVsyncId();
+ // Cancel the session if:
+ // 1. The session begins and ends at the same vsync id.
+ // 2. The session never begun.
+ if (mBeginVsyncId == INVALID_ID) {
+ return cancel(REASON_CANCEL_NOT_BEGUN);
+ } else if (mEndVsyncId <= mBeginVsyncId) {
+ return cancel(REASON_CANCEL_SAME_VSYNC);
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "end: " + mSession.getName()
+ + ", end=" + mEndVsyncId + ", reason=" + reason);
+ }
+ Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
+ mSession.setReason(reason);
- // Cancel the session if:
- // 1. The session begins and ends at the same vsync id.
- // 2. The session never begun.
- if (mBeginVsyncId == INVALID_ID) {
- cancel(REASON_CANCEL_NOT_BEGUN);
- } else if (mEndVsyncId <= mBeginVsyncId) {
- cancel(REASON_CANCEL_SAME_VSYNC);
- } else {
- if (DEBUG) {
- Log.d(TAG, "end: " + mSession.getName()
- + ", end=" + mEndVsyncId + ", reason=" + reason);
+ // We don't remove observer here,
+ // will remove it when all the frame metrics in this duration are called back.
+ // See onFrameMetricsAvailable for the logic of removing the observer.
+ // Waiting at most 10 seconds for all callbacks to finish.
+ mWaitForFinishTimedOut = () -> {
+ Log.e(TAG, "force finish cuj because of time out:" + mSession.getName());
+ finish(mJankInfos.size() - 1);
+ };
+ mHandler.postDelayed(mWaitForFinishTimedOut, TimeUnit.SECONDS.toMillis(10));
+ notifyCujEvent(ACTION_SESSION_END);
+ return true;
}
- Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
- mSession.setReason(reason);
- if (mListener != null) {
- mListener.onCujEvents(mSession, ACTION_SESSION_END);
- }
-
- // We don't remove observer here,
- // will remove it when all the frame metrics in this duration are called back.
- // See onFrameMetricsAvailable for the logic of removing the observer.
- // Waiting at most 10 seconds for all callbacks to finish.
- mWaitForFinishTimedOut = () -> {
- Log.e(TAG, "force finish cuj because of time out:" + mSession.getName());
- finish(mJankInfos.size() - 1);
- };
- mHandler.postDelayed(mWaitForFinishTimedOut, TimeUnit.SECONDS.toMillis(10));
}
}
/**
* Cancel the trace session of the CUJ.
*/
- public synchronized void cancel(@Reasons int reason) {
- mCancelled = true;
+ public boolean cancel(@Reasons int reason) {
+ synchronized (mLock) {
+ final boolean cancelFromEnd =
+ reason == REASON_CANCEL_NOT_BEGUN || reason == REASON_CANCEL_SAME_VSYNC;
+ if (mCancelled || (mEndVsyncId != INVALID_ID && !cancelFromEnd)) return false;
+ mCancelled = true;
+ // We don't need to end the trace section if it never begun.
+ if (mTracingStarted) {
+ Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
+ }
- // We don't need to end the trace section if it never begun.
- if (mTracingStarted) {
- Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
- }
+ // Always remove the observers in cancel call to avoid leakage.
+ removeObservers();
- // Always remove the observers in cancel call to avoid leakage.
- removeObservers();
+ if (DEBUG) {
+ Log.d(TAG, "cancel: " + mSession.getName() + ", begin=" + mBeginVsyncId
+ + ", end=" + mEndVsyncId + ", reason=" + reason);
+ }
- if (DEBUG) {
- Log.d(TAG, "cancel: " + mSession.getName()
- + ", begin=" + mBeginVsyncId + ", end=" + mEndVsyncId + ", reason=" + reason);
- }
-
- mSession.setReason(reason);
- // Notify the listener the session has been cancelled.
- // We don't notify the listeners if the session never begun.
- if (mListener != null) {
- mListener.onCujEvents(mSession, ACTION_SESSION_CANCEL);
+ mSession.setReason(reason);
+ // Notify the listener the session has been cancelled.
+ // We don't notify the listeners if the session never begun.
+ notifyCujEvent(ACTION_SESSION_CANCEL);
+ return true;
}
}
- @Override
- public synchronized void onJankDataAvailable(SurfaceControl.JankData[] jankData) {
- if (mCancelled) {
- return;
- }
+ private void notifyCujEvent(String action) {
+ if (mListener == null) return;
+ mListener.onCujEvents(mSession, action);
+ }
- for (SurfaceControl.JankData jankStat : jankData) {
- if (!isInRange(jankStat.frameVsyncId)) {
- continue;
+ @Override
+ public void onJankDataAvailable(SurfaceControl.JankData[] jankData) {
+ synchronized (mLock) {
+ if (mCancelled) {
+ return;
}
- JankInfo info = findJankInfo(jankStat.frameVsyncId);
- if (info != null) {
- info.surfaceControlCallbackFired = true;
- info.jankType = jankStat.jankType;
- } else {
- mJankInfos.put((int) jankStat.frameVsyncId,
- JankInfo.createFromSurfaceControlCallback(
- jankStat.frameVsyncId, jankStat.jankType));
+
+ for (SurfaceControl.JankData jankStat : jankData) {
+ if (!isInRange(jankStat.frameVsyncId)) {
+ continue;
+ }
+ JankInfo info = findJankInfo(jankStat.frameVsyncId);
+ if (info != null) {
+ info.surfaceControlCallbackFired = true;
+ info.jankType = jankStat.jankType;
+ } else {
+ mJankInfos.put((int) jankStat.frameVsyncId,
+ JankInfo.createFromSurfaceControlCallback(
+ jankStat.frameVsyncId, jankStat.jankType));
+ }
}
+ processJankInfos();
}
- processJankInfos();
}
private @Nullable JankInfo findJankInfo(long frameVsyncId) {
@@ -359,31 +370,34 @@
}
@Override
- public synchronized void onFrameMetricsAvailable(int dropCountSinceLastInvocation) {
- if (mCancelled) {
- return;
- }
+ public void onFrameMetricsAvailable(int dropCountSinceLastInvocation) {
+ synchronized (mLock) {
+ if (mCancelled) {
+ return;
+ }
- // Since this callback might come a little bit late after the end() call.
- // We should keep tracking the begin / end timestamp.
- // Then compare with vsync timestamp to check if the frame is in the duration of the CUJ.
- long totalDurationNanos = mMetricsWrapper.getMetric(FrameMetrics.TOTAL_DURATION);
- boolean isFirstFrame = mMetricsWrapper.getMetric(FrameMetrics.FIRST_DRAW_FRAME) == 1;
- long frameVsyncId = mMetricsWrapper.getTiming()[FrameMetrics.Index.FRAME_TIMELINE_VSYNC_ID];
+ // Since this callback might come a little bit late after the end() call.
+ // We should keep tracking the begin / end timestamp that we can compare with
+ // vsync timestamp to check if the frame is in the duration of the CUJ.
+ long totalDurationNanos = mMetricsWrapper.getMetric(FrameMetrics.TOTAL_DURATION);
+ boolean isFirstFrame = mMetricsWrapper.getMetric(FrameMetrics.FIRST_DRAW_FRAME) == 1;
+ long frameVsyncId =
+ mMetricsWrapper.getTiming()[FrameMetrics.Index.FRAME_TIMELINE_VSYNC_ID];
- if (!isInRange(frameVsyncId)) {
- return;
+ if (!isInRange(frameVsyncId)) {
+ return;
+ }
+ JankInfo info = findJankInfo(frameVsyncId);
+ if (info != null) {
+ info.hwuiCallbackFired = true;
+ info.totalDurationNanos = totalDurationNanos;
+ info.isFirstFrame = isFirstFrame;
+ } else {
+ mJankInfos.put((int) frameVsyncId, JankInfo.createFromHwuiCallback(
+ frameVsyncId, totalDurationNanos, isFirstFrame));
+ }
+ processJankInfos();
}
- JankInfo info = findJankInfo(frameVsyncId);
- if (info != null) {
- info.hwuiCallbackFired = true;
- info.totalDurationNanos = totalDurationNanos;
- info.isFirstFrame = isFirstFrame;
- } else {
- mJankInfos.put((int) frameVsyncId, JankInfo.createFromHwuiCallback(
- frameVsyncId, totalDurationNanos, isFirstFrame));
- }
- processJankInfos();
}
/**
@@ -497,11 +511,7 @@
(int) (maxFrameTimeNanos / NANOS_IN_MILLISECOND));
// Trigger perfetto if necessary.
- boolean overMissedFramesThreshold = mTraceThresholdMissedFrames != -1
- && missedFramesCount >= mTraceThresholdMissedFrames;
- boolean overFrameTimeThreshold = !mSurfaceOnly && mTraceThresholdFrameTimeMillis != -1
- && maxFrameTimeNanos >= mTraceThresholdFrameTimeMillis * NANOS_IN_MILLISECOND;
- if (overMissedFramesThreshold || overFrameTimeThreshold) {
+ if (shouldTriggerPerfetto(missedFramesCount, (int) maxFrameTimeNanos)) {
triggerPerfetto();
}
if (mSession.logToStatsd()) {
@@ -513,9 +523,7 @@
maxFrameTimeNanos, /* will be 0 if mSurfaceOnly == true */
missedSfFramesCount,
missedAppFramesCount);
- if (mListener != null) {
- mListener.onCujEvents(mSession, ACTION_METRICS_LOGGED);
- }
+ notifyCujEvent(ACTION_METRICS_LOGGED);
}
if (DEBUG) {
Log.i(TAG, "finish: CUJ=" + mSession.getName()
@@ -528,6 +536,14 @@
}
}
+ private boolean shouldTriggerPerfetto(int missedFramesCount, int maxFrameTimeNanos) {
+ boolean overMissedFramesThreshold = mTraceThresholdMissedFrames != -1
+ && missedFramesCount >= mTraceThresholdMissedFrames;
+ boolean overFrameTimeThreshold = !mSurfaceOnly && mTraceThresholdFrameTimeMillis != -1
+ && maxFrameTimeNanos >= mTraceThresholdFrameTimeMillis * NANOS_IN_MILLISECOND;
+ return overMissedFramesThreshold || overFrameTimeThreshold;
+ }
+
/**
* Remove all the registered listeners, observers and callbacks.
*/
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index f8eb95c..0ba5a39 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -58,6 +58,8 @@
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_AVD;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_EXIT_ANIM;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_SWITCH;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION;
@@ -173,6 +175,8 @@
public static final int CUJ_PIP_TRANSITION = 35;
public static final int CUJ_WALLPAPER_TRANSITION = 36;
public static final int CUJ_USER_SWITCH = 37;
+ public static final int CUJ_SPLASHSCREEN_AVD = 38;
+ public static final int CUJ_SPLASHSCREEN_EXIT_ANIM = 39;
private static final int NO_STATSD_LOGGING = -1;
@@ -219,6 +223,8 @@
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_SWITCH,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_AVD,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_EXIT_ANIM,
};
private static volatile InteractionJankMonitor sInstance;
@@ -230,6 +236,7 @@
private final SparseArray<FrameTracker> mRunningTrackers;
private final SparseArray<Runnable> mTimeoutActions;
private final HandlerThread mWorker;
+ private final Object mLock = new Object();
private boolean mEnabled = DEFAULT_ENABLED;
private int mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
@@ -276,6 +283,8 @@
CUJ_PIP_TRANSITION,
CUJ_WALLPAPER_TRANSITION,
CUJ_USER_SWITCH,
+ CUJ_SPLASHSCREEN_AVD,
+ CUJ_SPLASHSCREEN_EXIT_ANIM,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -325,6 +334,10 @@
mPropertiesChangedListener);
}
+ Object getLock() {
+ return mLock;
+ }
+
/**
* Creates a {@link FrameTracker} instance.
*
@@ -344,7 +357,7 @@
final ChoreographerWrapper choreographer =
new ChoreographerWrapper(Choreographer.getInstance());
- synchronized (this) {
+ synchronized (mLock) {
FrameTrackerListener eventsListener =
(s, act) -> handleCujEvents(config.getContext(), act, s);
return new FrameTracker(session, mWorker.getThreadHandler(),
@@ -372,11 +385,16 @@
final boolean badEnd = action.equals(ACTION_SESSION_END)
&& session.getReason() != REASON_END_NORMAL;
final boolean badCancel = action.equals(ACTION_SESSION_CANCEL)
- && session.getReason() != REASON_CANCEL_NORMAL;
+ && !(session.getReason() == REASON_CANCEL_NORMAL
+ || session.getReason() == REASON_CANCEL_TIMEOUT);
return badEnd || badCancel;
}
- private void notifyEvents(Context context, String action, Session session) {
+ /**
+ * Notifies who may interest in some CUJ events.
+ */
+ @VisibleForTesting
+ public void notifyEvents(Context context, String action, Session session) {
if (action.equals(ACTION_SESSION_CANCEL)
&& session.getReason() == REASON_CANCEL_NOT_BEGUN) {
return;
@@ -389,7 +407,7 @@
}
private void removeTimeout(@CujType int cujType) {
- synchronized (this) {
+ synchronized (mLock) {
Runnable timeout = mTimeoutActions.get(cujType);
if (timeout != null) {
mWorker.getThreadHandler().removeCallbacks(timeout);
@@ -432,17 +450,9 @@
}
private boolean beginInternal(@NonNull Configuration conf) {
- synchronized (this) {
+ synchronized (mLock) {
int cujType = conf.mCujType;
- boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
- if (!mEnabled || !shouldSample) {
- if (DEBUG) {
- Log.d(TAG, "Skip monitoring cuj: " + getNameOfCuj(cujType)
- + ", enable=" + mEnabled + ", debuggable=" + DEFAULT_ENABLED
- + ", sample=" + shouldSample + ", interval=" + mSamplingInterval);
- }
- return false;
- }
+ if (!shouldMonitor(cujType)) return false;
FrameTracker tracker = getTracker(cujType);
// Skip subsequent calls if we already have an ongoing tracing.
if (tracker != null) return false;
@@ -460,6 +470,24 @@
}
/**
+ * Check if the monitoring is enabled and if it should be sampled.
+ */
+ @SuppressWarnings("RandomModInteger")
+ @VisibleForTesting
+ public boolean shouldMonitor(@CujType int cujType) {
+ boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
+ if (!mEnabled || !shouldSample) {
+ if (DEBUG) {
+ Log.d(TAG, "Skip monitoring cuj: " + getNameOfCuj(cujType)
+ + ", enable=" + mEnabled + ", debuggable=" + DEFAULT_ENABLED
+ + ", sample=" + shouldSample + ", interval=" + mSamplingInterval);
+ }
+ return false;
+ }
+ return true;
+ }
+
+ /**
* Schedules a timeout action.
* @param cuj cuj type
* @param timeout duration to timeout
@@ -478,14 +506,16 @@
* @return boolean true if the tracker is ended successfully, false otherwise.
*/
public boolean end(@CujType int cujType) {
- synchronized (this) {
+ synchronized (mLock) {
// remove the timeout action first.
removeTimeout(cujType);
FrameTracker tracker = getTracker(cujType);
// Skip this call since we haven't started a trace yet.
if (tracker == null) return false;
- tracker.end(REASON_END_NORMAL);
- removeTracker(cujType);
+ // if the end call doesn't return true, another thread is handling end of the cuj.
+ if (tracker.end(REASON_END_NORMAL)) {
+ removeTracker(cujType);
+ }
return true;
}
}
@@ -499,33 +529,37 @@
return cancel(cujType, REASON_CANCEL_NORMAL);
}
- boolean cancel(@CujType int cujType, @Reasons int reason) {
- synchronized (this) {
+ /**
+ * Cancels the trace session.
+ *
+ * @return boolean true if the tracker is cancelled successfully, false otherwise.
+ */
+ @VisibleForTesting
+ public boolean cancel(@CujType int cujType, @Reasons int reason) {
+ synchronized (mLock) {
// remove the timeout action first.
removeTimeout(cujType);
FrameTracker tracker = getTracker(cujType);
// Skip this call since we haven't started a trace yet.
if (tracker == null) return false;
- tracker.cancel(reason);
- removeTracker(cujType);
+ // if the cancel call doesn't return true, another thread is handling cancel of the cuj.
+ if (tracker.cancel(reason)) {
+ removeTracker(cujType);
+ }
return true;
}
}
private FrameTracker getTracker(@CujType int cuj) {
- synchronized (this) {
- return mRunningTrackers.get(cuj);
- }
+ return mRunningTrackers.get(cuj);
}
private void removeTracker(@CujType int cuj) {
- synchronized (this) {
- mRunningTrackers.remove(cuj);
- }
+ mRunningTrackers.remove(cuj);
}
private void updateProperties(DeviceConfig.Properties properties) {
- synchronized (this) {
+ synchronized (mLock) {
mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
DEFAULT_SAMPLING_INTERVAL);
mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
@@ -547,10 +581,8 @@
*/
@VisibleForTesting
public void trigger(Session session) {
- synchronized (this) {
- mWorker.getThreadHandler().post(
- () -> PerfettoTrigger.trigger(session.getPerfettoTrigger()));
- }
+ mWorker.getThreadHandler().post(
+ () -> PerfettoTrigger.trigger(session.getPerfettoTrigger()));
}
/**
@@ -648,6 +680,10 @@
return "WALLPAPER_TRANSITION";
case CUJ_USER_SWITCH:
return "USER_SWITCH";
+ case CUJ_SPLASHSCREEN_AVD:
+ return "SPLASHSCREEN_AVD";
+ case CUJ_SPLASHSCREEN_EXIT_ANIM:
+ return "SPLASHSCREEN_EXIT_ANIM";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 9bdf608..225a79c 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -160,7 +160,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- static final int VERSION = 203;
+ static final int VERSION = 204;
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
@@ -1786,6 +1786,89 @@
}
}
+
+ private static class TimeMultiStateCounter implements TimeBaseObs {
+ private final TimeBase mTimeBase;
+ private final LongMultiStateCounter mCounter;
+
+ private TimeMultiStateCounter(TimeBase timeBase, Parcel in, long timestampMs) {
+ mTimeBase = timeBase;
+ mCounter = LongMultiStateCounter.CREATOR.createFromParcel(in);
+ mCounter.setEnabled(mTimeBase.isRunning(), timestampMs);
+ timeBase.add(this);
+ }
+
+ private TimeMultiStateCounter(TimeBase timeBase, int stateCount, long timestampMs) {
+ mTimeBase = timeBase;
+ mCounter = new LongMultiStateCounter(stateCount);
+ mCounter.setEnabled(mTimeBase.isRunning(), timestampMs);
+ timeBase.add(this);
+ }
+
+ private void writeToParcel(Parcel out) {
+ mCounter.writeToParcel(out, 0);
+ }
+
+ @Override
+ public void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
+ mCounter.setEnabled(true, elapsedRealtimeUs / 1000);
+ }
+
+ @Override
+ public void onTimeStopped(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
+ mCounter.setEnabled(false, elapsedRealtimeUs / 1000);
+ }
+
+ public LongMultiStateCounter getCounter() {
+ return mCounter;
+ }
+
+ public int getStateCount() {
+ return mCounter.getStateCount();
+ }
+
+ public void setTrackingEnabled(boolean enabled, long timestampMs) {
+ mCounter.setEnabled(enabled && mTimeBase.isRunning(), timestampMs);
+ }
+
+ private void setState(@BatteryConsumer.ProcessState int processState,
+ long elapsedRealtimeMs) {
+ mCounter.setState(processState, elapsedRealtimeMs);
+ }
+
+ private void update(long value, long timestampMs) {
+ mCounter.updateValue(value, timestampMs);
+ }
+
+ /**
+ * Returns accumulated count for the specified state.
+ */
+ public long getCountLocked(int procState) {
+ return mCounter.getCount(procState);
+ }
+
+ public void logState(Printer pw, String prefix) {
+ pw.println(prefix + "mCounter=" + mCounter);
+ }
+
+ /**
+ * Clears state of this counter.
+ */
+ @Override
+ public boolean reset(boolean detachIfReset, long elapsedRealtimeUs /* unused */) {
+ mCounter.reset();
+ if (detachIfReset) {
+ detach();
+ }
+ return true;
+ }
+
+ @Override
+ public void detach() {
+ mTimeBase.remove(this);
+ }
+ }
+
private static class TimeInFreqMultiStateCounter implements TimeBaseObs {
private final TimeBase mTimeBase;
private final LongArrayMultiStateCounter mCounter;
@@ -8024,7 +8107,7 @@
LongSamplingCounter mUserCpuTime;
LongSamplingCounter mSystemCpuTime;
LongSamplingCounter[][] mCpuClusterSpeedTimesUs;
- LongSamplingCounter mCpuActiveTimeMs;
+ TimeMultiStateCounter mCpuActiveTimeMs;
LongSamplingCounterArray mCpuFreqTimeMs;
LongSamplingCounterArray mScreenOffCpuFreqTimeMs;
@@ -8147,7 +8230,6 @@
mUserCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
mSystemCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
- mCpuActiveTimeMs = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
mCpuClusterTimesMs = new LongSamplingCounterArray(mBsi.mOnBatteryTimeBase);
mWakelockStats = mBsi.new OverflowArrayMap<Wakelock>(uid) {
@@ -8190,11 +8272,13 @@
mProcessState = procState;
getProcStateTimeCounter().setState(procState, elapsedTimeMs);
getProcStateScreenOffTimeCounter().setState(procState, elapsedTimeMs);
+ final int batteryConsumerProcessState =
+ mapUidProcessStateToBatteryConsumerProcessState(procState);
+ getCpuActiveTimeCounter().setState(batteryConsumerProcessState, elapsedTimeMs);
final MeasuredEnergyStats energyStats =
getOrCreateMeasuredEnergyStatsIfSupportedLocked();
if (energyStats != null) {
- energyStats.setState(mapUidProcessStateToBatteryConsumerProcessState(procState),
- elapsedTimeMs);
+ energyStats.setState(batteryConsumerProcessState, elapsedTimeMs);
}
}
@@ -8208,9 +8292,39 @@
return nullIfAllZeros(mScreenOffCpuFreqTimeMs, which);
}
+ private TimeMultiStateCounter getCpuActiveTimeCounter() {
+ if (mCpuActiveTimeMs == null) {
+ final long timestampMs = mBsi.mClock.elapsedRealtime();
+ mCpuActiveTimeMs = new TimeMultiStateCounter(mBsi.mOnBatteryTimeBase,
+ BatteryConsumer.PROCESS_STATE_COUNT, timestampMs);
+ mCpuActiveTimeMs.setState(
+ mapUidProcessStateToBatteryConsumerProcessState(mProcessState),
+ timestampMs);
+ }
+ return mCpuActiveTimeMs;
+ }
+
@Override
public long getCpuActiveTime() {
- return mCpuActiveTimeMs.getCountLocked(STATS_SINCE_CHARGED);
+ if (mCpuActiveTimeMs == null) {
+ return 0;
+ }
+
+ long activeTime = 0;
+ for (int procState = 0; procState < BatteryConsumer.PROCESS_STATE_COUNT; procState++) {
+ activeTime += mCpuActiveTimeMs.getCountLocked(procState);
+ }
+ return activeTime;
+ }
+
+ @Override
+ public long getCpuActiveTime(int procState) {
+ if (mCpuActiveTimeMs == null
+ || procState < 0 || procState >= BatteryConsumer.PROCESS_STATE_COUNT) {
+ return 0;
+ }
+
+ return mCpuActiveTimeMs.getCountLocked(procState);
}
@Override
@@ -9914,7 +10028,13 @@
LongSamplingCounterArray.writeToParcel(out, mCpuFreqTimeMs);
LongSamplingCounterArray.writeToParcel(out, mScreenOffCpuFreqTimeMs);
- mCpuActiveTimeMs.writeToParcel(out);
+ if (mCpuActiveTimeMs != null) {
+ out.writeInt(mCpuActiveTimeMs.getStateCount());
+ mCpuActiveTimeMs.writeToParcel(out);
+ } else {
+ out.writeInt(0);
+ }
+
mCpuClusterTimesMs.writeToParcel(out);
if (mProcStateTimeMs != null) {
@@ -10217,12 +10337,19 @@
mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel(
in, mBsi.mOnBatteryScreenOffTimeBase);
- mCpuActiveTimeMs = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
- mCpuClusterTimesMs = new LongSamplingCounterArray(mBsi.mOnBatteryTimeBase, in);
-
final long timestampMs = mBsi.mClock.elapsedRealtime();
int stateCount = in.readInt();
if (stateCount != 0) {
+ final TimeMultiStateCounter counter = new TimeMultiStateCounter(
+ mBsi.mOnBatteryTimeBase, in, timestampMs);
+ if (stateCount == BatteryConsumer.PROCESS_STATE_COUNT) {
+ mCpuActiveTimeMs = counter;
+ }
+ }
+ mCpuClusterTimesMs = new LongSamplingCounterArray(mBsi.mOnBatteryTimeBase, in);
+
+ stateCount = in.readInt();
+ if (stateCount != 0) {
// Read the object from the Parcel, whether it's usable or not
TimeInFreqMultiStateCounter counter = new TimeInFreqMultiStateCounter(
mBsi.mOnBatteryTimeBase, in, timestampMs);
@@ -11135,12 +11262,14 @@
updateOnBatteryBgTimeBase(uptimeMs * 1000, elapsedRealtimeMs * 1000);
updateOnBatteryScreenOffBgTimeBase(uptimeMs * 1000, elapsedRealtimeMs * 1000);
+ final int batteryConsumerProcessState =
+ mapUidProcessStateToBatteryConsumerProcessState(mProcessState);
+ getCpuActiveTimeCounter().setState(batteryConsumerProcessState, elapsedRealtimeMs);
+
final MeasuredEnergyStats energyStats =
getOrCreateMeasuredEnergyStatsIfSupportedLocked();
if (energyStats != null) {
- energyStats.setState(
- mapUidProcessStateToBatteryConsumerProcessState(mProcessState),
- elapsedRealtimeMs);
+ energyStats.setState(batteryConsumerProcessState, elapsedRealtimeMs);
}
}
@@ -14344,7 +14473,7 @@
public void readKernelUidCpuActiveTimesLocked(boolean onBattery) {
final long startTimeMs = mClock.uptimeMillis();
final long elapsedRealtimeMs = mClock.elapsedRealtime();
- mCpuUidActiveTimeReader.readDelta(false, (uid, cpuActiveTimesMs) -> {
+ mCpuUidActiveTimeReader.readAbsolute((uid, cpuActiveTimesMs) -> {
uid = mapUid(uid);
if (Process.isIsolated(uid)) {
if (DEBUG) Slog.w(TAG, "Got active times for an isolated uid: " + uid);
@@ -14355,7 +14484,7 @@
return;
}
final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, startTimeMs);
- u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesMs, onBattery);
+ u.getCpuActiveTimeCounter().update(cpuActiveTimesMs, mClock.elapsedRealtime());
});
final long elapsedTimeMs = mClock.uptimeMillis() - startTimeMs;
@@ -16496,13 +16625,20 @@
u.mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readSummaryFromParcelLocked(
in, mOnBatteryScreenOffTimeBase);
- u.mCpuActiveTimeMs.readSummaryFromParcelLocked(in);
+ int stateCount = in.readInt();
+ if (stateCount != 0) {
+ final TimeMultiStateCounter counter = new TimeMultiStateCounter(
+ mOnBatteryTimeBase, in, mClock.elapsedRealtime());
+ if (stateCount == BatteryConsumer.PROCESS_STATE_COUNT) {
+ u.mCpuActiveTimeMs = counter;
+ }
+ }
u.mCpuClusterTimesMs.readSummaryFromParcelLocked(in);
detachIfNotNull(u.mProcStateTimeMs);
u.mProcStateTimeMs = null;
- int stateCount = in.readInt();
+ stateCount = in.readInt();
if (stateCount != 0) {
// Read the object from the Parcel, whether it's usable or not
TimeInFreqMultiStateCounter counter = new TimeInFreqMultiStateCounter(
@@ -17025,7 +17161,13 @@
LongSamplingCounterArray.writeSummaryToParcelLocked(out, u.mCpuFreqTimeMs);
LongSamplingCounterArray.writeSummaryToParcelLocked(out, u.mScreenOffCpuFreqTimeMs);
- u.mCpuActiveTimeMs.writeSummaryFromParcelLocked(out);
+ if (u.mCpuActiveTimeMs != null) {
+ out.writeInt(u.mCpuActiveTimeMs.getStateCount());
+ u.mCpuActiveTimeMs.writeToParcel(out);
+ } else {
+ out.writeInt(0);
+ }
+
u.mCpuClusterTimesMs.writeSummaryToParcelLocked(out);
if (u.mProcStateTimeMs != null) {
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index 4599231..4ab4fae 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -184,16 +184,14 @@
continue;
}
- // TODO(b/191921016): use per-state CPU active time
- final long cpuActiveTime = 0;
// TODO(b/191921016): use per-state CPU cluster times
final long[] cpuClusterTimes = null;
final long[] cpuFreqTimes = u.getCpuFreqTimes(BatteryStats.STATS_SINCE_CHARGED,
uidProcState);
- if (cpuActiveTime != 0 || cpuClusterTimes != null || cpuFreqTimes != null) {
+ if (cpuClusterTimes != null || cpuFreqTimes != null) {
result.perProcStatePowerMah[procState] += calculateUidModeledPowerMah(u,
- cpuActiveTime, cpuClusterTimes, cpuFreqTimes);
+ 0, cpuClusterTimes, cpuFreqTimes);
}
}
@@ -202,8 +200,12 @@
continue;
}
- app.setConsumedPower(key, result.perProcStatePowerMah[key.processState],
- BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ final long cpuActiveTime = u.getCpuActiveTime(key.processState);
+
+ double powerMah = result.perProcStatePowerMah[key.processState];
+ powerMah += mCpuActivePowerEstimator.calculatePower(cpuActiveTime);
+ app.setConsumedPower(key, powerMah, BatteryConsumer.POWER_MODEL_POWER_PROFILE)
+ .setUsageDurationMillis(key, cpuActiveTime);
}
}
diff --git a/core/java/com/android/internal/os/SomeArgs.java b/core/java/com/android/internal/os/SomeArgs.java
index d1b483b..b12df6e 100644
--- a/core/java/com/android/internal/os/SomeArgs.java
+++ b/core/java/com/android/internal/os/SomeArgs.java
@@ -53,7 +53,6 @@
public Object arg5;
public Object arg6;
public Object arg7;
- public Object arg8;
public int argi1;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int argi2;
@@ -122,7 +121,6 @@
arg5 = null;
arg6 = null;
arg7 = null;
- arg8 = null;
argi1 = 0;
argi2 = 0;
argi3 = 0;
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index bfc57b4..b428970 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -1841,8 +1841,9 @@
@Override
public void setLocalFocus(boolean hasFocus, boolean inTouchMode) {
- getViewRootImpl().windowFocusChanged(hasFocus, inTouchMode);
-
+ ViewRootImpl viewRoot = getViewRootImpl();
+ viewRoot.windowFocusChanged(hasFocus, inTouchMode);
+ viewRoot.touchModeChanged(inTouchMode);
}
@Override
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 2bd385c..1a1a8ba 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -404,7 +404,6 @@
name: "libandroid_runtime_vm_headers",
host_supported: true,
vendor_available: true,
- recovery_available: true,
// TODO(b/153609531): remove when libbinder is not native_bridge_supported
native_bridge_supported: true,
// Allow only modules from the following list to create threads that can be
diff --git a/core/jni/android_media_ToneGenerator.cpp b/core/jni/android_media_ToneGenerator.cpp
index 82611ee..cc4657d 100644
--- a/core/jni/android_media_ToneGenerator.cpp
+++ b/core/jni/android_media_ToneGenerator.cpp
@@ -16,7 +16,6 @@
*/
#define LOG_TAG "ToneGenerator"
-
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
@@ -39,90 +38,78 @@
};
static fields_t fields;
-static jboolean android_media_ToneGenerator_startTone(JNIEnv *env, jobject thiz, jint toneType, jint durationMs) {
- ALOGV("android_media_ToneGenerator_startTone: %p", thiz);
-
- ToneGenerator *lpToneGen = (ToneGenerator *)env->GetLongField(thiz,
- fields.context);
- if (lpToneGen == NULL) {
+static sp<ToneGenerator> getNativeToneGenerator(JNIEnv *env, jobject thiz) {
+ auto toneGen = sp<ToneGenerator>::fromExisting(
+ reinterpret_cast<ToneGenerator *>(env->GetLongField(thiz, fields.context)));
+ if (toneGen == nullptr) {
jniThrowRuntimeException(env, "Method called after release()");
- return false;
}
+ ALOGV("ToneGenerator address %p", toneGen.get());
+ return toneGen;
+}
- return lpToneGen->startTone((ToneGenerator::tone_type) toneType, durationMs);
+static sp<ToneGenerator> setNativeToneGenerator(JNIEnv *env, jobject thiz,
+ const sp<ToneGenerator> &toneGen) {
+ auto oldToneGen = sp<ToneGenerator>::fromExisting(
+ reinterpret_cast<ToneGenerator *>(env->GetLongField(thiz, fields.context)));
+ ALOGV("ToneGenerator address changed from %p to %p", oldToneGen.get(), toneGen.get());
+ auto id = reinterpret_cast<void *>(setNativeToneGenerator);
+ if (toneGen != nullptr) {
+ toneGen->incStrong(id);
+ }
+ if (oldToneGen != nullptr) {
+ oldToneGen->decStrong(id);
+ }
+ env->SetLongField(thiz, fields.context, (jlong)toneGen.get());
+ return oldToneGen;
+}
+
+static jboolean android_media_ToneGenerator_startTone(JNIEnv *env, jobject thiz, jint toneType,
+ jint durationMs) {
+ ALOGV("%s jobject: %p", __func__, thiz);
+ auto lpToneGen = getNativeToneGenerator(env, thiz);
+ return (lpToneGen != nullptr)
+ ? lpToneGen->startTone((ToneGenerator::tone_type)toneType, durationMs)
+ : false;
}
static void android_media_ToneGenerator_stopTone(JNIEnv *env, jobject thiz) {
- ALOGV("android_media_ToneGenerator_stopTone: %p", thiz);
-
- ToneGenerator *lpToneGen = (ToneGenerator *)env->GetLongField(thiz,
- fields.context);
-
- ALOGV("ToneGenerator lpToneGen: %p", lpToneGen);
- if (lpToneGen == NULL) {
- jniThrowRuntimeException(env, "Method called after release()");
- return;
- }
- lpToneGen->stopTone();
+ ALOGV("%s jobject: %p", __func__, thiz);
+ auto lpToneGen = getNativeToneGenerator(env, thiz);
+ if (lpToneGen != nullptr) lpToneGen->stopTone();
}
static jint android_media_ToneGenerator_getAudioSessionId(JNIEnv *env, jobject thiz) {
- ToneGenerator *lpToneGen = (ToneGenerator *)env->GetLongField(thiz,
- fields.context);
- if (lpToneGen == NULL) {
- jniThrowRuntimeException(env, "Method called after release()");
- return 0;
- }
- return lpToneGen->getSessionId();
+ ALOGV("%s jobject: %p", __func__, thiz);
+ auto lpToneGen = getNativeToneGenerator(env, thiz);
+ return (lpToneGen != nullptr) ? lpToneGen->getSessionId() : 0;
}
static void android_media_ToneGenerator_release(JNIEnv *env, jobject thiz) {
- ToneGenerator *lpToneGen = (ToneGenerator *)env->GetLongField(thiz,
- fields.context);
- ALOGV("android_media_ToneGenerator_release lpToneGen: %p", lpToneGen);
-
- env->SetLongField(thiz, fields.context, 0);
-
- delete lpToneGen;
+ ALOGV("%s jobject: %p", __func__, thiz);
+ setNativeToneGenerator(env, thiz, nullptr);
}
static void android_media_ToneGenerator_native_setup(JNIEnv *env, jobject thiz, jint streamType,
jint volume, jstring opPackageName) {
- ScopedUtfChars opPackageNameStr(env, opPackageName);
- ToneGenerator *lpToneGen =
- new ToneGenerator((audio_stream_type_t)streamType, AudioSystem::linearToLog(volume),
- true /*threadCanCallJava*/, opPackageNameStr.c_str());
-
- env->SetLongField(thiz, fields.context, 0);
-
- ALOGV("android_media_ToneGenerator_native_setup jobject: %p", thiz);
-
- ALOGV("ToneGenerator lpToneGen: %p", lpToneGen);
-
+ ALOGV("%s jobject: %p", __func__, thiz);
+ ScopedUtfChars opPackageNameStr{env, opPackageName};
+ sp<ToneGenerator> lpToneGen = sp<ToneGenerator>::make((audio_stream_type_t)streamType,
+ AudioSystem::linearToLog(volume),
+ true /*threadCanCallJava*/,
+ opPackageNameStr.c_str());
if (!lpToneGen->isInited()) {
ALOGE("ToneGenerator init failed");
jniThrowRuntimeException(env, "Init failed");
- delete lpToneGen;
return;
}
-
// Stow our new C++ ToneGenerator in an opaque field in the Java object.
- env->SetLongField(thiz, fields.context, (jlong)lpToneGen);
-
- ALOGV("ToneGenerator fields.context: %p", (void*) env->GetLongField(thiz, fields.context));
+ setNativeToneGenerator(env, thiz, lpToneGen);
}
-static void android_media_ToneGenerator_native_finalize(JNIEnv *env,
- jobject thiz) {
- ALOGV("android_media_ToneGenerator_native_finalize jobject: %p", thiz);
-
- ToneGenerator *lpToneGen = (ToneGenerator *)env->GetLongField(thiz,
- fields.context);
-
- if (lpToneGen != NULL) {
- ALOGV("delete lpToneGen: %p", lpToneGen);
- delete lpToneGen;
- }
+static void android_media_ToneGenerator_native_finalize(JNIEnv *env, jobject thiz) {
+ ALOGV("%s jobject: %p", __func__, thiz);
+ android_media_ToneGenerator_release(env, thiz);
}
// ----------------------------------------------------------------------------
diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
index 70a9be7..b910d16 100644
--- a/core/jni/android_view_InputQueue.cpp
+++ b/core/jni/android_view_InputQueue.cpp
@@ -39,6 +39,7 @@
static struct {
jmethodID finishInputEvent;
+ jmethodID getNativePtr;
} gInputQueueClassInfo;
enum {
@@ -263,8 +264,15 @@
jclass clazz = FindClassOrDie(env, kInputQueuePathName);
gInputQueueClassInfo.finishInputEvent = GetMethodIDOrDie(env, clazz, "finishInputEvent",
"(JZ)V");
+ gInputQueueClassInfo.getNativePtr = GetMethodIDOrDie(env, clazz, "getNativePtr", "()J");
return RegisterMethodsOrDie(env, kInputQueuePathName, g_methods, NELEM(g_methods));
}
+AInputQueue* android_view_InputQueue_getNativePtr(jobject inputQueue) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jlong ptr = env->CallLongMethod(inputQueue, gInputQueueClassInfo.getNativePtr);
+ return reinterpret_cast<AInputQueue*>(ptr);
+}
+
} // namespace android
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 3bc3336..fb5fded 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -888,23 +888,6 @@
static_cast<int8_t>(changeFrameRateStrategy));
}
-static jlong nativeAcquireFrameRateFlexibilityToken(JNIEnv* env, jclass clazz) {
- sp<ISurfaceComposer> composer = ComposerService::getComposerService();
- sp<IBinder> token;
- status_t result = composer->acquireFrameRateFlexibilityToken(&token);
- if (result < 0) {
- ALOGE("Failed acquiring frame rate flexibility token: %s (%d)", strerror(-result), result);
- return 0;
- }
- token->incStrong((void*)nativeAcquireFrameRateFlexibilityToken);
- return reinterpret_cast<jlong>(token.get());
-}
-
-static void nativeReleaseFrameRateFlexibilityToken(JNIEnv* env, jclass clazz, jlong tokenLong) {
- sp<IBinder> token(reinterpret_cast<IBinder*>(tokenLong));
- token->decStrong((void*)nativeAcquireFrameRateFlexibilityToken);
-}
-
static void nativeSetFixedTransformHint(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jint transformHint) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -1956,10 +1939,6 @@
(void*)nativeSetShadowRadius },
{"nativeSetFrameRate", "(JJFII)V",
(void*)nativeSetFrameRate },
- {"nativeAcquireFrameRateFlexibilityToken", "()J",
- (void*)nativeAcquireFrameRateFlexibilityToken },
- {"nativeReleaseFrameRateFlexibilityToken", "(J)V",
- (void*)nativeReleaseFrameRateFlexibilityToken },
{"nativeGetPhysicalDisplayIds", "()[J",
(void*)nativeGetPhysicalDisplayIds },
{"nativeGetPrimaryPhysicalDisplayId", "()J",
diff --git a/core/jni/include/android_runtime/android_view_InputQueue.h b/core/jni/include/android_runtime/android_view_InputQueue.h
index ac8da58..c1b611c 100644
--- a/core/jni/include/android_runtime/android_view_InputQueue.h
+++ b/core/jni/include/android_runtime/android_view_InputQueue.h
@@ -80,6 +80,8 @@
Vector<key_value_pair_t<InputEvent*, bool> > mFinishedEvents;
};
+extern AInputQueue* android_view_InputQueue_getNativePtr(jobject inputQueue);
+
} // namespace android
#endif
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index df69ed0..240d3aa 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2432,7 +2432,7 @@
<permission android:name="android.permission.READ_ACTIVE_EMERGENCY_SESSION"
android:protectionLevel="signature" />
- <!-- Allows listen permission to always reported signal strength.
+ <!-- Allows listen permission to always reported system signal strength.
@hide Used internally. -->
<permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH"
android:protectionLevel="signature" />
@@ -4215,6 +4215,16 @@
<permission android:name="android.permission.INSTALL_TEST_ONLY_PACKAGE"
android:protectionLevel="signature" />
+ <!-- @SystemApi Allows an application to install DPCs only, an application is
+ considered a DPC if it has a {@link android.app.admin.DeviceAdminReceiver}
+ protected by {@link android.Manifest.permission#BIND_DEVICE_ADMIN).
+ This is a limited version of
+ {@link android.Manifest.permission#INSTALL_PACKAGES}.
+ @hide
+ -->
+ <permission android:name="android.permission.INSTALL_DPC_PACKAGES"
+ android:protectionLevel="signature|role" />
+
<!-- Allows an application to use System Data Loaders.
<p>Not for use by third-party applications.
@hide
diff --git a/core/res/res/layout/alert_dialog.xml b/core/res/res/layout/alert_dialog.xml
index 59e56af..6869c5f 100644
--- a/core/res/res/layout/alert_dialog.xml
+++ b/core/res/res/layout/alert_dialog.xml
@@ -124,21 +124,21 @@
android:layout_width="0dip"
android:layout_gravity="start"
android:layout_weight="1"
- style="?android:attr/buttonBarButtonStyle"
+ style="?android:attr/buttonBarPositiveButtonStyle"
android:maxLines="2"
android:layout_height="wrap_content" />
<Button android:id="@+id/button3"
android:layout_width="0dip"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
- style="?android:attr/buttonBarButtonStyle"
+ style="?android:attr/buttonBarNeutralButtonStyle"
android:maxLines="2"
android:layout_height="wrap_content" />
<Button android:id="@+id/button2"
android:layout_width="0dip"
android:layout_gravity="end"
android:layout_weight="1"
- style="?android:attr/buttonBarButtonStyle"
+ style="?android:attr/buttonBarNegativeButtonStyle"
android:maxLines="2"
android:layout_height="wrap_content" />
<LinearLayout android:id="@+id/rightSpacer"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 06333e1..819857f 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8735,9 +8735,14 @@
<declare-styleable name="VoiceInteractionService">
<!-- The service that hosts active voice interaction sessions. This is required. -->
<attr name="sessionService" format="string" />
- <!-- The service that provides voice recognition. This is required. When the user
- selects this voice interaction service, they will also be implicitly selecting
- the component here for their recognition service. -->
+ <!-- The service that provides voice recognition. This is required. On Android 11 and
+ earlier, this must be a valid RecognitionService.
+ <p>
+ From Android 12 onward, this attribute does nothing. However, we still require it to
+ be set to something to reduce the risk that an app with an unspecified value gets
+ pushed to older platform versions, where it will cause a boot loop. To make sure
+ developers don't miss it, the system will reset the current assistant if this isn't
+ specified.-->
<attr name="recognitionService" format="string" />
<attr name="settingsActivity" />
<!-- Flag indicating whether this voice interaction service is capable of handling the
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index e0d9ecfc..882a0da 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -31,12 +31,14 @@
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.app.Instrumentation;
import android.content.Context;
import android.os.Binder;
import android.platform.test.annotations.Presubmit;
@@ -47,7 +49,9 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import org.junit.AfterClass;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -58,7 +62,7 @@
* Tests for {@link ViewRootImpl}
*
* Build/Install/Run:
- * atest FrameworksCoreTests:ViewRootImplTest
+ * atest FrameworksCoreTests:ViewRootImplTest
*/
@Presubmit
@SmallTest
@@ -66,15 +70,32 @@
public class ViewRootImplTest {
private ViewRootImpl mViewRootImpl;
- private Context mContext;
private volatile boolean mKeyReceived = false;
+ private static Context sContext;
+ private static Instrumentation sInstrumentation = InstrumentationRegistry.getInstrumentation();
+
+ // The touch mode state before the test was started, needed to return the system to the original
+ // state after the test completes.
+ private static boolean sOriginalTouchMode;
+
+ @BeforeClass
+ public static void setUpClass() {
+ sContext = sInstrumentation.getTargetContext();
+ View view = new View(sContext);
+ sOriginalTouchMode = view.isInTouchMode();
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ sInstrumentation.setInTouchMode(sOriginalTouchMode);
+ }
+
@Before
public void setUp() throws Exception {
- mContext = getInstrumentation().getTargetContext();
-
- getInstrumentation().runOnMainSync(() ->
- mViewRootImpl = new ViewRootImpl(mContext, mContext.getDisplayNoVerify()));
+ sInstrumentation.setInTouchMode(true);
+ sInstrumentation.runOnMainSync(() ->
+ mViewRootImpl = new ViewRootImpl(sContext, sContext.getDisplayNoVerify()));
}
@Test
@@ -224,9 +245,9 @@
*/
@Test
public void requestScrollCapture_timeout() {
- final View view = new View(mContext);
+ final View view = new View(sContext);
view.setScrollCaptureCallback(new TestScrollCaptureCallback()); // Does nothing
- InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ sInstrumentation.runOnMainSync(() -> {
WindowManager.LayoutParams wmlp =
new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
// Set a fake token to bypass 'is your activity running' check
@@ -250,6 +271,29 @@
} catch (InterruptedException e) { /* ignore */ }
}
+ @Test
+ public void whenTouchModeChanges_viewRootIsNotified() throws Exception {
+ View view = new View(sContext);
+ attachViewToWindow(view);
+ ViewTreeObserver viewTreeObserver = view.getRootView().getViewTreeObserver();
+ CountDownLatch latch = new CountDownLatch(1);
+ ViewTreeObserver.OnTouchModeChangeListener touchModeListener = (boolean inTouchMode) -> {
+ assertWithMessage("addOnTouchModeChangeListener parameter").that(
+ inTouchMode).isFalse();
+ latch.countDown();
+ };
+ viewTreeObserver.addOnTouchModeChangeListener(touchModeListener);
+
+ try {
+ view.requestFocusFromTouch();
+
+ assertThat(latch.await(1, TimeUnit.SECONDS)).isTrue();
+ assertThat(view.isInTouchMode()).isFalse();
+ } finally {
+ viewTreeObserver.removeOnTouchModeChangeListener(touchModeListener);
+ }
+ }
+
/**
* When window doesn't have focus, keys should be dropped.
*/
@@ -308,27 +352,31 @@
* Next, inject an event into this view, and check whether it is received.
*/
private void checkKeyEvent(Runnable setup, boolean shouldReceiveKey) {
- final KeyView view = new KeyView(mContext);
+ final KeyView view = new KeyView(sContext);
- WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
- wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
-
- InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- WindowManager wm = mContext.getSystemService(WindowManager.class);
- wm.addView(view, wmlp);
- });
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ attachViewToWindow(view);
mViewRootImpl = view.getViewRootImpl();
- InstrumentationRegistry.getInstrumentation().runOnMainSync(setup);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ sInstrumentation.runOnMainSync(setup);
+ sInstrumentation.waitForIdleSync();
// Inject a key event, and wait for it to be processed
- InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ sInstrumentation.runOnMainSync(() -> {
KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A);
mViewRootImpl.dispatchInputEvent(event);
});
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ sInstrumentation.waitForIdleSync();
assertEquals(mKeyReceived, shouldReceiveKey);
}
+
+ private void attachViewToWindow(View view) {
+ WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
+ wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
+
+ sInstrumentation.runOnMainSync(() -> {
+ WindowManager wm = sContext.getSystemService(WindowManager.class);
+ wm.addView(view, wmlp);
+ });
+ sInstrumentation.waitForIdleSync();
+ }
}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 3045d7d5..c241e36 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -160,15 +160,27 @@
public void takeScreenshot(int displayId, RemoteCallback callback) {}
- public void setTouchExplorationPassthroughRegion(int displayId, Region region) {}
-
- public void setGestureDetectionPassthroughRegion(int displayId, Region region) {}
-
public void setFocusAppearance(int strokeWidth, int color) {}
public void logTrace(long timestamp, String where, String callingParams, int processId,
long threadId, int callingUid, Bundle callingStack) {}
+ public void setGestureDetectionPassthroughRegion(int displayId, Region region) {}
+
+ public void setTouchExplorationPassthroughRegion(int displayId, Region region) {}
+
+ public void setServiceDetectsGesturesEnabled(int displayId, boolean mode) {}
+
+ public void requestTouchExploration(int displayId) {}
+
+ public void requestDragging(int displayId, int pointerId) {}
+
+ public void requestDelegating(int displayId) {}
+
+ public void onDoubleTap(int displayId) {}
+
+ public void onDoubleTapAndHold(int displayId) {}
+
public void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
int processId, long threadId, int callingUid, Bundle serializedCallingStackInBundle) {}
}
diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
index d7a5e26..0d2d047 100644
--- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -16,6 +16,8 @@
package com.android.internal.jank;
+import static com.android.internal.jank.FrameTracker.REASON_CANCEL_TIMEOUT;
+import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_TO_STATSD_INTERACTION_TYPE;
@@ -34,6 +36,7 @@
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.view.View;
import android.view.ViewAttachTestActivity;
@@ -82,36 +85,23 @@
Handler handler = spy(new Handler(mActivity.getMainLooper()));
doReturn(true).when(handler).sendMessageAtTime(any(), anyLong());
- mWorker = spy(new HandlerThread("Interaction-jank-monitor-test"));
- doNothing().when(mWorker).start();
+ mWorker = mock(HandlerThread.class);
doReturn(handler).when(mWorker).getThreadHandler();
}
@Test
public void testBeginEnd() {
- // Should return false if the view is not attached.
- InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker));
- verify(mWorker).start();
-
- Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX);
- Configuration config = mock(Configuration.class);
- when(config.isSurfaceOnly()).thenReturn(false);
- FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(),
- new ThreadedRendererWrapper(mView.getThreadedRenderer()),
- new ViewRootWrapper(mView.getViewRootImpl()),
- new SurfaceControlWrapper(), mock(ChoreographerWrapper.class),
- new FrameMetricsWrapper(),
- /* traceThresholdMissedFrames= */ 1, /* traceThresholdFrameTimeMillis= */ -1,
- /* FrameTrackerListener */ null, config));
+ InteractionJankMonitor monitor = createMockedInteractionJankMonitor();
+ FrameTracker tracker = createMockedFrameTracker(null);
doReturn(tracker).when(monitor).createFrameTracker(any(), any());
- doNothing().when(tracker).triggerPerfetto();
- doNothing().when(tracker).postTraceStartMarker();
+ doNothing().when(tracker).begin();
+ doReturn(true).when(tracker).end(anyInt());
// Simulate a trace session and see if begin / end are invoked.
- assertThat(monitor.begin(mView, session.getCuj())).isTrue();
+ assertThat(monitor.begin(mView, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
verify(tracker).begin();
- assertThat(monitor.end(session.getCuj())).isTrue();
- verify(tracker).end(FrameTracker.REASON_END_NORMAL);
+ assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
+ verify(tracker).end(REASON_END_NORMAL);
}
@Test
@@ -140,33 +130,23 @@
}
@Test
- public void testBeginCancel() {
- InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker));
-
+ public void testBeginTimeout() {
ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
-
- Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX);
- Configuration config = mock(Configuration.class);
- when(config.isSurfaceOnly()).thenReturn(false);
- FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(),
- new ThreadedRendererWrapper(mView.getThreadedRenderer()),
- new ViewRootWrapper(mView.getViewRootImpl()),
- new SurfaceControlWrapper(), mock(FrameTracker.ChoreographerWrapper.class),
- new FrameMetricsWrapper(),
- /* traceThresholdMissedFrames= */ 1, /* traceThresholdFrameTimeMillis= */ -1,
- /* FrameTrackerListener */ null, config));
+ InteractionJankMonitor monitor = createMockedInteractionJankMonitor();
+ FrameTracker tracker = createMockedFrameTracker(null);
doReturn(tracker).when(monitor).createFrameTracker(any(), any());
- doNothing().when(tracker).triggerPerfetto();
- doNothing().when(tracker).postTraceStartMarker();
+ doNothing().when(tracker).begin();
+ doReturn(true).when(tracker).cancel(anyInt());
- assertThat(monitor.begin(mView, session.getCuj())).isTrue();
+ assertThat(monitor.begin(mView, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
verify(tracker).begin();
verify(monitor).scheduleTimeoutAction(anyInt(), anyLong(), captor.capture());
Runnable runnable = captor.getValue();
assertThat(runnable).isNotNull();
mWorker.getThreadHandler().removeCallbacks(runnable);
runnable.run();
- verify(tracker).cancel(FrameTracker.REASON_CANCEL_TIMEOUT);
+ verify(monitor).cancel(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, REASON_CANCEL_TIMEOUT);
+ verify(tracker).cancel(REASON_CANCEL_TIMEOUT);
}
@Test
@@ -192,4 +172,43 @@
.isTrue();
}
}
+
+ private InteractionJankMonitor createMockedInteractionJankMonitor() {
+ InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker));
+ doReturn(true).when(monitor).shouldMonitor(anyInt());
+ doNothing().when(monitor).notifyEvents(any(), any(), any());
+ return monitor;
+ }
+
+ private FrameTracker createMockedFrameTracker(FrameTracker.FrameTrackerListener listener) {
+ Session session = spy(new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX));
+ doReturn(false).when(session).logToStatsd();
+
+ ThreadedRendererWrapper threadedRenderer = mock(ThreadedRendererWrapper.class);
+ doNothing().when(threadedRenderer).addObserver(any());
+ doNothing().when(threadedRenderer).removeObserver(any());
+
+ ViewRootWrapper viewRoot = spy(new ViewRootWrapper(mView.getViewRootImpl()));
+ doNothing().when(viewRoot).addSurfaceChangedCallback(any());
+
+ SurfaceControlWrapper surfaceControl = mock(SurfaceControlWrapper.class);
+ doNothing().when(surfaceControl).addJankStatsListener(any(), any());
+ doNothing().when(surfaceControl).removeJankStatsListener(any());
+
+ final ChoreographerWrapper choreographer = mock(ChoreographerWrapper.class);
+ doReturn(SystemClock.elapsedRealtime()).when(choreographer).getVsyncId();
+
+ Configuration configuration = mock(Configuration.class);
+ when(configuration.isSurfaceOnly()).thenReturn(false);
+
+ FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(),
+ threadedRenderer, viewRoot, surfaceControl, choreographer,
+ new FrameMetricsWrapper(), /* traceThresholdMissedFrames= */ 1,
+ /* traceThresholdFrameTimeMillis= */ -1, listener, configuration));
+
+ doNothing().when(tracker).postTraceStartMarker();
+ doNothing().when(tracker).triggerPerfetto();
+
+ return tracker;
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
index 1ac89ea..c83619d 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
@@ -156,7 +156,7 @@
verify(mCpuUidFreqTimeReader, times(2)).perClusterTimesAvailable();
verify(mCpuUidFreqTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidFreqTimeReader.Callback.class));
- verify(mCpuUidActiveTimeReader).readDelta(anyBoolean(),
+ verify(mCpuUidActiveTimeReader).readAbsolute(
any(KernelCpuUidActiveTimeReader.Callback.class));
verify(mCpuUidClusterTimeReader).readDelta(anyBoolean(),
any(KernelCpuUidClusterTimeReader.Callback.class));
@@ -1039,6 +1039,8 @@
@Test
public void testReadKernelUidCpuActiveTimesLocked() {
+ mClocks.realtime = 1000;
+
// PRECONDITIONS
updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
@@ -1049,15 +1051,15 @@
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
});
- final long[] uidTimesMs = {8000, 25000, 3000, 0, 42000};
+ final long[] initialTimesMs = {8000, 25000, 3000, 0, 42000};
doAnswer(invocation -> {
final KernelCpuUidActiveTimeReader.Callback<Long> callback =
- invocation.getArgument(1);
+ invocation.getArgument(0);
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuTime(testUids[i], uidTimesMs[i]);
+ callback.onUidCpuTime(testUids[i], initialTimesMs[i]);
}
return null;
- }).when(mCpuUidActiveTimeReader).readDelta(anyBoolean(),
+ }).when(mCpuUidActiveTimeReader).readAbsolute(
any(KernelCpuUidActiveTimeReader.Callback.class));
// RUN
@@ -1067,23 +1069,23 @@
for (int i = 0; i < testUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
assertNotNull("No entry for uid=" + testUids[i], u);
- assertEquals("Unexpected cpu active time for uid=" + testUids[i], uidTimesMs[i],
+ assertEquals("Unexpected cpu active time for uid=" + testUids[i], 0,
u.getCpuActiveTime());
}
- // Repeat the test when the screen is off.
+ // Some time passes
+ mClocks.realtime = 2000;
// PRECONDITIONS
- updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
- final long[] deltasMs = {43000, 3345000, 2143000, 123000, 4554000};
+ final long[] cpuTimesAt2000 = {43000, 3345000, 2143000, 123000, 4554000};
doAnswer(invocation -> {
final KernelCpuUidActiveTimeReader.Callback<Long> callback =
- invocation.getArgument(1);
+ invocation.getArgument(0);
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuTime(testUids[i], deltasMs[i]);
+ callback.onUidCpuTime(testUids[i], cpuTimesAt2000[i]);
}
return null;
- }).when(mCpuUidActiveTimeReader).readDelta(anyBoolean(),
+ }).when(mCpuUidActiveTimeReader).readAbsolute(
any(KernelCpuUidActiveTimeReader.Callback.class));
// RUN
@@ -1094,12 +1096,43 @@
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
assertNotNull("No entry for uid=" + testUids[i], u);
assertEquals("Unexpected cpu active time for uid=" + testUids[i],
- uidTimesMs[i] + deltasMs[i], u.getCpuActiveTime());
+ cpuTimesAt2000[i] - initialTimesMs[i], u.getCpuActiveTime());
+ }
+
+ // Give it another second
+ mClocks.realtime = 3000;
+
+ // Repeat the test when the screen is off.
+
+ // PRECONDITIONS
+ updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+ final long[] cpuTimesAt3000 = {63000, 7345000, 8143000, 923000, 5554000};
+ doAnswer(invocation -> {
+ final KernelCpuUidActiveTimeReader.Callback<Long> callback =
+ invocation.getArgument(0);
+ for (int i = 0; i < testUids.length; ++i) {
+ callback.onUidCpuTime(testUids[i], cpuTimesAt3000[i]);
+ }
+ return null;
+ }).when(mCpuUidActiveTimeReader).readAbsolute(
+ any(KernelCpuUidActiveTimeReader.Callback.class));
+
+ // RUN
+ mBatteryStatsImpl.readKernelUidCpuActiveTimesLocked(true);
+
+ // VERIFY
+ for (int i = 0; i < testUids.length; ++i) {
+ final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+ assertNotNull("No entry for uid=" + testUids[i], u);
+ assertEquals("Unexpected cpu active time for uid=" + testUids[i],
+ cpuTimesAt3000[i] - initialTimesMs[i], u.getCpuActiveTime());
}
}
@Test
public void testReadKernelUidCpuActiveTimesLocked_invalidUid() {
+ mClocks.realtime = 1000;
+
// PRECONDITIONS
updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
@@ -1113,17 +1146,37 @@
FIRST_APPLICATION_UID + 27,
FIRST_APPLICATION_UID + 33
});
- final long[] uidTimesMs = {8000, 25000, 3000, 0, 42000};
+ final long[] cpuTimesAt1000 = {8000, 25000, 3000, 0, 42000};
doAnswer(invocation -> {
final KernelCpuUidActiveTimeReader.Callback<Long> callback =
- invocation.getArgument(1);
+ invocation.getArgument(0);
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuTime(testUids[i], uidTimesMs[i]);
+ callback.onUidCpuTime(testUids[i], cpuTimesAt1000[i]);
}
// And one for the invalid uid
callback.onUidCpuTime(invalidUid, 1200L);
return null;
- }).when(mCpuUidActiveTimeReader).readDelta(anyBoolean(),
+ }).when(mCpuUidActiveTimeReader).readAbsolute(
+ any(KernelCpuUidActiveTimeReader.Callback.class));
+
+ // RUN
+ mBatteryStatsImpl.readKernelUidCpuActiveTimesLocked(true);
+
+ // Some time passes
+ mClocks.realtime = 2000;
+
+ // Run again to compute the delta
+ final long[] cpuTimesAt2000 = {18000, 225000, 33000, 40, 542000};
+ doAnswer(invocation -> {
+ final KernelCpuUidActiveTimeReader.Callback<Long> callback =
+ invocation.getArgument(0);
+ for (int i = 0; i < testUids.length; ++i) {
+ callback.onUidCpuTime(testUids[i], cpuTimesAt2000[i]);
+ }
+ // And one for the invalid uid
+ callback.onUidCpuTime(invalidUid, 1200L);
+ return null;
+ }).when(mCpuUidActiveTimeReader).readAbsolute(
any(KernelCpuUidActiveTimeReader.Callback.class));
// RUN
@@ -1133,8 +1186,8 @@
for (int i = 0; i < testUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
assertNotNull("No entry for uid=" + testUids[i], u);
- assertEquals("Unexpected cpu active time for uid=" + testUids[i], uidTimesMs[i],
- u.getCpuActiveTime());
+ assertEquals("Unexpected cpu active time for uid=" + testUids[i],
+ cpuTimesAt2000[i] - cpuTimesAt1000[i], u.getCpuActiveTime());
}
assertNull("There shouldn't be an entry for invalid uid=" + invalidUid,
mBatteryStatsImpl.getUidStats().get(invalidUid));
diff --git a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
index 8540d91d..1ae30db 100644
--- a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
@@ -119,6 +119,20 @@
when(mMockCpuUidFreqTimeReader.perClusterTimesAvailable()).thenReturn(false);
+ mStatsRule.setTime(1000, 1000);
+
+ // Initialize active CPU time
+ doAnswer(invocation -> {
+ final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(0);
+ callback.onUidCpuTime(APP_UID1, 1000L);
+ callback.onUidCpuTime(APP_UID2, 3000L);
+ return null;
+ }).when(mMockKerneCpuUidActiveTimeReader).readAbsolute(any());
+
+ mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, null);
+
+ mStatsRule.setTime(2000, 2000);
+
// User/System CPU time
doAnswer(invocation -> {
final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
@@ -130,11 +144,11 @@
// Active CPU time
doAnswer(invocation -> {
- final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(1);
- callback.onUidCpuTime(APP_UID1, 1111L);
- callback.onUidCpuTime(APP_UID2, 3333L);
+ final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(0);
+ callback.onUidCpuTime(APP_UID1, 2111L);
+ callback.onUidCpuTime(APP_UID2, 6333L);
return null;
- }).when(mMockKerneCpuUidActiveTimeReader).readDelta(anyBoolean(), any());
+ }).when(mMockKerneCpuUidActiveTimeReader).readAbsolute(any());
// Per-cluster CPU time
doAnswer(invocation -> {
@@ -211,14 +225,6 @@
return null;
}).when(mMockKernelCpuUidUserSysTimeReader).readDelta(anyBoolean(), any());
- // Active CPU time
- doAnswer(invocation -> {
- final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(1);
- callback.onUidCpuTime(APP_UID1, 1111L);
- callback.onUidCpuTime(APP_UID2, 3333L);
- return null;
- }).when(mMockKerneCpuUidActiveTimeReader).readDelta(anyBoolean(), any());
-
// Per-cluster CPU time
doAnswer(invocation -> {
final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
@@ -296,12 +302,29 @@
mockSingleUidTimeReader(APP_UID1, new long[NUM_CPU_FREQS]);
mockSingleUidTimeReader(APP_UID2, new long[NUM_CPU_FREQS]);
+ // Active CPU time
+ doAnswer(invocation -> {
+ final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(0);
+ callback.onUidCpuTime(APP_UID1, 1111L);
+ callback.onUidCpuTime(APP_UID2, 3333L);
+ return null;
+ }).when(mMockKerneCpuUidActiveTimeReader).readAbsolute(any());
+
+ mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, null);
mStatsRule.getBatteryStats().copyFromAllUidsCpuTimes(true, true);
mockSingleUidTimeReader(APP_UID1, new long[]{1000, 2000, 3000, 4000});
mockSingleUidTimeReader(APP_UID2, new long[]{1111, 2222, 3333, 4444});
mStatsRule.setTime(2000, 2000);
+
+ doAnswer(invocation -> {
+ final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(0);
+ callback.onUidCpuTime(APP_UID1, 2222L);
+ callback.onUidCpuTime(APP_UID2, 6666L);
+ return null;
+ }).when(mMockKerneCpuUidActiveTimeReader).readAbsolute(any());
+
mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, null);
mStatsRule.getBatteryStats().copyFromAllUidsCpuTimes(true, true);
@@ -314,6 +337,14 @@
BatteryStats.Uid.PROCESS_STATE_TOP, 2000);
mStatsRule.setTime(3000, 3000);
+
+ doAnswer(invocation -> {
+ final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(0);
+ callback.onUidCpuTime(APP_UID1, 3333L);
+ callback.onUidCpuTime(APP_UID2, 8888L);
+ return null;
+ }).when(mMockKerneCpuUidActiveTimeReader).readAbsolute(any());
+
mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, null);
mStatsRule.getBatteryStats().copyFromAllUidsCpuTimes(true, true);
@@ -339,11 +370,11 @@
BatteryConsumer.POWER_COMPONENT_CPU,
BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
- assertThat(uidConsumer1.getConsumedPower(foreground)).isWithin(PRECISION).of(1.388888);
+ assertThat(uidConsumer1.getConsumedPower(foreground)).isWithin(PRECISION).of(1.611088);
assertThat(uidConsumer1.getConsumedPower(background)).isWithin(PRECISION).of(0);
- assertThat(uidConsumer1.getConsumedPower(fgs)).isWithin(PRECISION).of(2.0);
- assertThat(uidConsumer2.getConsumedPower(foreground)).isWithin(PRECISION).of(2.222);
- assertThat(uidConsumer2.getConsumedPower(background)).isWithin(PRECISION).of(1.543055);
+ assertThat(uidConsumer1.getConsumedPower(fgs)).isWithin(PRECISION).of(2.2222);
+ assertThat(uidConsumer2.getConsumedPower(foreground)).isWithin(PRECISION).of(2.6664);
+ assertThat(uidConsumer2.getConsumedPower(background)).isWithin(PRECISION).of(2.209655);
assertThat(uidConsumer2.getConsumedPower(fgs)).isWithin(PRECISION).of(0);
}
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
index 33222dd..8a12235 100644
--- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
@@ -169,6 +169,21 @@
when(mMockCpuUidFreqTimeReader.perClusterTimesAvailable()).thenReturn(false);
+ mStatsRule.setTime(1000, 1000);
+
+ // Initialize active CPU time
+ doAnswer(invocation -> {
+ final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(0);
+ callback.onUidCpuTime(APP_UID1, 1000L);
+ callback.onUidCpuTime(APP_UID2, 3000L);
+ callback.onUidCpuTime(Process.SYSTEM_UID, 5000L);
+ return null;
+ }).when(mMockKerneCpuUidActiveTimeReader).readAbsolute(any());
+
+ mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, null);
+
+ mStatsRule.setTime(2000, 2000);
+
// User/System CPU time in microseconds
doAnswer(invocation -> {
final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
@@ -180,12 +195,12 @@
// Active CPU time in milliseconds
doAnswer(invocation -> {
- final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(1);
- callback.onUidCpuTime(APP_UID1, 1111L);
- callback.onUidCpuTime(APP_UID2, 3333L);
- callback.onUidCpuTime(Process.SYSTEM_UID, 10000L);
+ final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(0);
+ callback.onUidCpuTime(APP_UID1, 2111L);
+ callback.onUidCpuTime(APP_UID2, 6333L);
+ callback.onUidCpuTime(Process.SYSTEM_UID, 15000L);
return null;
- }).when(mMockKerneCpuUidActiveTimeReader).readDelta(anyBoolean(), any());
+ }).when(mMockKerneCpuUidActiveTimeReader).readAbsolute(any());
// Per-cluster CPU time in milliseconds
doAnswer(invocation -> {
diff --git a/core/tests/utiltests/Android.bp b/core/tests/utiltests/Android.bp
index 72d6a2c..adc3676 100644
--- a/core/tests/utiltests/Android.bp
+++ b/core/tests/utiltests/Android.bp
@@ -33,6 +33,7 @@
"frameworks-base-testutils",
"mockito-target-minus-junit4",
"androidx.test.ext.junit",
+ "truth-prebuilt",
],
libs: [
diff --git a/core/tests/utiltests/src/android/util/AtomicFileTest.java b/core/tests/utiltests/src/android/util/AtomicFileTest.java
index a7d3b22..8c13579 100644
--- a/core/tests/utiltests/src/android/util/AtomicFileTest.java
+++ b/core/tests/utiltests/src/android/util/AtomicFileTest.java
@@ -16,6 +16,8 @@
package android.util;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertTrue;
@@ -257,6 +259,16 @@
}
}
+ @Test
+ public void testToString() throws Exception {
+ AtomicFile atomicFile = new AtomicFile(mBaseFile);
+
+ String toString = atomicFile.toString();
+
+ assertThat(toString).contains("AtomicFile");
+ assertThat(toString).contains(mBaseFile.getAbsolutePath());
+ }
+
private static void writeBytes(@NonNull File file, @NonNull byte[] bytes) throws IOException {
try (FileOutputStream outputStream = new FileOutputStream(file)) {
outputStream.write(bytes);
diff --git a/data/keyboards/Vendor_248a_Product_8266.idc b/data/keyboards/Vendor_248a_Product_8266.idc
deleted file mode 100644
index 47ff9f8..0000000
--- a/data/keyboards/Vendor_248a_Product_8266.idc
+++ /dev/null
@@ -1,25 +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.
-
-#
-# Input Device Configuration file for Google Reference RCU Remote.
-#
-#
-
-# Basic Parameters
-# Due to a memory error on early prototypes of the reference remote control
-# the VID/PID is mapped to 248a/8266 instead of 0957/0001
-keyboard.layout = Vendor_0957_Product_0001
-keyboard.doNotWakeByDefault = 1
-audio.mic = 1
\ No newline at end of file
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java
index e6ad011..eb94297 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java
@@ -30,18 +30,21 @@
/** Wrapper for both Extension and Sidecar versions of DisplayFeature. */
final class CommonDisplayFeature implements DisplayFeature {
private static final Pattern FEATURE_PATTERN =
- Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]");
+ Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]-?(flat|half-opened)?");
private static final String FEATURE_TYPE_FOLD = "fold";
private static final String FEATURE_TYPE_HINGE = "hinge";
+ private static final String PATTERN_STATE_FLAT = "flat";
+ private static final String PATTERN_STATE_HALF_OPENED = "half-opened";
+
// TODO(b/183049815): Support feature strings that include the state of the feature.
+
/**
* Parses a display feature from a string.
*
* @throws IllegalArgumentException if the provided string is improperly formatted or could not
- * otherwise be parsed.
- *
+ * otherwise be parsed.
* @see #FEATURE_PATTERN
*/
@NonNull
@@ -52,6 +55,7 @@
}
try {
String featureType = featureMatcher.group(1);
+ featureType = featureType == null ? "" : featureType;
int type;
switch (featureType) {
case FEATURE_TYPE_FOLD:
@@ -73,8 +77,21 @@
if (isZero(featureRect)) {
throw new IllegalArgumentException("Feature has empty bounds: " + string);
}
-
- return new CommonDisplayFeature(type, null, featureRect);
+ String stateString = featureMatcher.group(6);
+ stateString = stateString == null ? "" : stateString;
+ Integer state;
+ switch (stateString) {
+ case PATTERN_STATE_FLAT:
+ state = COMMON_STATE_FLAT;
+ break;
+ case PATTERN_STATE_HALF_OPENED:
+ state = COMMON_STATE_HALF_OPENED;
+ break;
+ default:
+ state = null;
+ break;
+ }
+ return new CommonDisplayFeature(type, state, featureRect);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Malformed feature description: " + string, e);
}
@@ -87,6 +104,7 @@
private final Rect mRect;
CommonDisplayFeature(int type, @Nullable Integer state, @NonNull Rect rect) {
+ assertValidState(state);
this.mType = type;
this.mState = state;
if (rect.width() == 0 && rect.height() == 0) {
@@ -125,4 +143,11 @@
public int hashCode() {
return Objects.hash(mType, mState, mRect);
}
+
+ private static void assertValidState(@Nullable Integer state) {
+ if (state != null && state != COMMON_STATE_FLAT && state != COMMON_STATE_HALF_OPENED) {
+ throw new IllegalArgumentException("Invalid state: " + state
+ + "must be either COMMON_STATE_FLAT or COMMON_STATE_HALF_OPENED");
+ }
+ }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java
index b6c4c43..5736418 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java
@@ -16,11 +16,15 @@
package androidx.window.common;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.graphics.Rect;
import androidx.annotation.NonNull;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/** Wrapper for both Extension and Sidecar versions of DisplayFeature. */
public interface DisplayFeature {
/** Returns the type of the feature. */
@@ -28,9 +32,29 @@
/** Returns the state of the feature, or {@code null} if the feature has no state. */
@Nullable
+ @State
Integer getState();
/** Returns the bounds of the feature. */
@NonNull
Rect getRect();
+
+ /**
+ * A common state to represent a FLAT hinge. This is needed because the definitions in Sidecar
+ * and Extensions do not match exactly.
+ */
+ int COMMON_STATE_FLAT = 3;
+ /**
+ * A common state to represent a HALF_OPENED hinge. This is needed because the definitions in
+ * Sidecar and Extensions do not match exactly.
+ */
+ int COMMON_STATE_HALF_OPENED = 2;
+
+ /**
+ * The possible states for a folding hinge.
+ */
+ @IntDef({COMMON_STATE_FLAT, COMMON_STATE_HALF_OPENED})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface State {}
+
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index 383d91d..32d447e 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -122,8 +122,19 @@
private int getFeatureState(DisplayFeature feature) {
Integer featureState = feature.getState();
Optional<Integer> posture = mDevicePostureProducer.getData();
- int fallbackPosture = posture.orElse(FoldingFeature.STATE_FLAT);
- return featureState == null ? fallbackPosture : featureState;
+ int fallbackPosture = posture.orElse(DisplayFeature.COMMON_STATE_FLAT);
+ int displayFeatureState = featureState == null ? fallbackPosture : featureState;
+ return convertToExtensionState(displayFeatureState);
+ }
+
+ private int convertToExtensionState(int state) {
+ switch (state) {
+ case DisplayFeature.COMMON_STATE_FLAT:
+ return FoldingFeature.STATE_FLAT;
+ case DisplayFeature.COMMON_STATE_HALF_OPENED:
+ return FoldingFeature.STATE_HALF_OPENED;
+ }
+ return FoldingFeature.STATE_FLAT;
}
private void onDisplayFeaturesChanged() {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
index ece198c..aa949f1 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
@@ -38,6 +38,7 @@
import androidx.window.util.PriorityDataProducer;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Optional;
@@ -47,6 +48,7 @@
*/
class SampleSidecarImpl extends StubSidecar {
private static final String TAG = "SampleSidecar";
+ private static final boolean DEBUG = false;
private final SettingsDevicePostureProducer mSettingsDevicePostureProducer;
private final DataProducer<Integer> mDevicePostureProducer;
@@ -88,10 +90,30 @@
Optional<Integer> posture = mDevicePostureProducer.getData();
SidecarDeviceState deviceState = new SidecarDeviceState();
- deviceState.posture = posture.orElse(SidecarDeviceState.POSTURE_UNKNOWN);
+ deviceState.posture = posture.orElse(deviceStateFromFeature());
return deviceState;
}
+ private int deviceStateFromFeature() {
+ List<DisplayFeature> storedFeatures = mDisplayFeatureProducer.getData()
+ .orElse(Collections.emptyList());
+ for (int i = 0; i < storedFeatures.size(); i++) {
+ DisplayFeature feature = storedFeatures.get(i);
+ final int state = feature.getState() == null ? -1 : feature.getState();
+ if (DEBUG && feature.getState() == null) {
+ Log.d(TAG, "feature#getState was null for DisplayFeature: " + feature);
+ }
+
+ switch (state) {
+ case DisplayFeature.COMMON_STATE_FLAT:
+ return SidecarDeviceState.POSTURE_OPENED;
+ case DisplayFeature.COMMON_STATE_HALF_OPENED:
+ return SidecarDeviceState.POSTURE_HALF_OPENED;
+ }
+ }
+ return SidecarDeviceState.POSTURE_UNKNOWN;
+ }
+
@NonNull
@Override
public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 3ba1a34..8e3d726 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -38,6 +38,7 @@
path: "src",
}
+// Sources that have no dependencies that can be used directly downstream of this library
filegroup {
name: "wm_shell_util-sources",
srcs: [
@@ -46,6 +47,7 @@
path: "src",
}
+// Aidls which can be used directly downstream of this library
filegroup {
name: "wm_shell-aidls",
srcs: [
@@ -130,11 +132,12 @@
"kotlinx-coroutines-android",
"kotlinx-coroutines-core",
"iconloader_base",
- "jsr330",
"protolog-lib",
"WindowManager-Shell-proto",
+ "dagger2",
"jsr330",
],
kotlincflags: ["-Xjvm-default=enable"],
manifest: "AndroidManifest.xml",
+ plugins: ["dagger2-compiler"],
}
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml b/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
new file mode 100644
index 0000000..94165a1
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@color/size_compat_hint_bubble"/>
+ <corners android:radius="@dimen/size_compat_hint_corner_radius"/>
+</shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml b/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml
new file mode 100644
index 0000000..a8f0f76
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/size_compat_hint_point_width"
+ android:height="8dp"
+ android:viewportWidth="10"
+ android:viewportHeight="8">
+ <path
+ android:fillColor="@color/size_compat_hint_bubble"
+ android:pathData="M10,0 l-4.1875,6.6875 a1,1 0 0,1 -1.625,0 l-4.1875,-6.6875z"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
index 73a48d3..3e486df 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
@@ -15,14 +15,21 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
<path
- android:fillColor="#aa000000"
- android:pathData="M0,12 a12,12 0 1,0 24,0 a12,12 0 1,0 -24,0" />
- <path
- android:fillColor="@android:color/white"
- android:pathData="M17.65,6.35c-1.63,-1.63 -3.94,-2.57 -6.48,-2.31c-3.67,0.37 -6.69,3.35 -7.1,7.02C3.52,15.91 7.27,20 12,20c3.19,0 5.93,-1.87 7.21,-4.57c0.31,-0.66 -0.16,-1.43 -0.89,-1.43h-0.01c-0.37,0 -0.72,0.2 -0.88,0.53c-1.13,2.43 -3.84,3.97 -6.81,3.32c-2.22,-0.49 -4.01,-2.3 -4.49,-4.52C5.31,9.44 8.26,6 12,6c1.66,0 3.14,0.69 4.22,1.78l-2.37,2.37C13.54,10.46 13.76,11 14.21,11H19c0.55,0 1,-0.45 1,-1V5.21c0,-0.45 -0.54,-0.67 -0.85,-0.35L17.65,6.35z"/>
+ android:fillColor="#53534D"
+ android:pathData="M0,24 a24,24 0 1,0 48,0 a24,24 0 1,0 -48,0" />
+ <group
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:fillColor="#E4E3DA"
+ android:pathData="M6,13c0,-1.65 0.67,-3.15 1.76,-4.24L6.34,7.34C4.9,8.79 4,10.79 4,13c0,4.08 3.05,7.44 7,7.93v-2.02C8.17,18.43 6,15.97 6,13z"/>
+ <path
+ android:fillColor="#E4E3DA"
+ android:pathData="M20,13c0,-4.42 -3.58,-8 -8,-8c-0.06,0 -0.12,0.01 -0.18,0.01v0l1.09,-1.09L11.5,2.5L8,6l3.5,3.5l1.41,-1.41l-1.08,-1.08C11.89,7.01 11.95,7 12,7c3.31,0 6,2.69 6,6c0,2.97 -2.17,5.43 -5,5.91v2.02C16.95,20.44 20,17.08 20,13z"/>
+ </group>
</vector>
diff --git a/libs/WindowManager/Shell/res/layout/bubble_overflow_view.xml b/libs/WindowManager/Shell/res/layout/bubble_overflow_view.xml
index 544b731..05b1506 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_overflow_view.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_overflow_view.xml
@@ -32,7 +32,7 @@
android:id="@+id/bubble_view_name"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
android:textSize="13sp"
- android:layout_width="wrap_content"
+ android:layout_width="@dimen/bubble_name_width"
android:layout_height="wrap_content"
android:maxLines="1"
android:lines="2"
diff --git a/libs/WindowManager/Shell/res/layout/pip_menu.xml b/libs/WindowManager/Shell/res/layout/pip_menu.xml
index 9fe0247..7dc2f31 100644
--- a/libs/WindowManager/Shell/res/layout/pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/pip_menu.xml
@@ -65,25 +65,28 @@
<LinearLayout
android:id="@+id/top_end_container"
android:layout_gravity="top|end"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
+
<ImageButton
android:id="@+id/settings"
android:layout_width="@dimen/pip_action_size"
android:layout_height="@dimen/pip_action_size"
android:contentDescription="@string/pip_phone_settings"
+ android:layout_gravity="top|start"
android:gravity="center"
android:src="@drawable/pip_ic_settings"
android:background="?android:selectableItemBackgroundBorderless" />
<ImageButton
- android:id="@+id/dismiss"
+ android:id="@+id/enter_split"
android:layout_width="@dimen/pip_action_size"
android:layout_height="@dimen/pip_action_size"
- android:contentDescription="@string/pip_phone_close"
+ android:layout_gravity="top|start"
android:gravity="center"
- android:src="@drawable/pip_ic_close_white"
+ android:contentDescription="@string/pip_phone_enter_split"
+ android:src="@drawable/pip_expand"
android:background="?android:selectableItemBackgroundBorderless" />
</LinearLayout>
@@ -97,4 +100,14 @@
android:padding="@dimen/pip_resize_handle_padding"
android:src="@drawable/pip_resize_handle"
android:background="?android:selectableItemBackgroundBorderless" />
+
+ <ImageButton
+ android:id="@+id/dismiss"
+ android:layout_width="@dimen/pip_action_size"
+ android:layout_height="@dimen/pip_action_size"
+ android:contentDescription="@string/pip_phone_close"
+ android:layout_gravity="top|end"
+ android:gravity="center"
+ android:src="@drawable/pip_ic_close_white"
+ android:background="?android:selectableItemBackgroundBorderless" />
</FrameLayout>
diff --git a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
index 0dea87c..17347f6 100644
--- a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
+++ b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
@@ -22,41 +22,34 @@
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:gravity="center"
android:clipToPadding="false"
- android:padding="@dimen/bubble_elevation">
+ android:paddingBottom="5dp">
<LinearLayout
+ android:id="@+id/size_compat_hint_popup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:background="@android:color/background_light"
- android:elevation="@dimen/bubble_elevation"
- android:orientation="vertical">
+ android:orientation="vertical"
+ android:clickable="true">
<TextView
- android:layout_width="180dp"
+ android:layout_width="188dp"
android:layout_height="wrap_content"
- android:paddingLeft="10dp"
- android:paddingRight="10dp"
- android:paddingTop="10dp"
+ android:lineSpacingExtra="4sp"
+ android:background="@drawable/size_compat_hint_bubble"
+ android:padding="16dp"
android:text="@string/restart_button_description"
android:textAlignment="viewStart"
- android:textColor="@android:color/primary_text_light"
- android:textSize="16sp"/>
+ android:textColor="#E4E3DA"
+ android:textSize="14sp"/>
- <Button
- android:id="@+id/got_it"
+ <ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:includeFontPadding="false"
android:layout_gravity="end"
- android:minHeight="36dp"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/got_it"
- android:textAllCaps="true"
- android:textColor="#3c78d8"
- android:textSize="16sp"
- android:textStyle="bold"/>
+ android:src="@drawable/size_compat_hint_point"
+ android:paddingHorizontal="@dimen/size_compat_hint_corner_radius"
+ android:contentDescription="@null"/>
</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/layout/size_compat_ui.xml b/libs/WindowManager/Shell/res/layout/size_compat_ui.xml
index cd31531..47e76f0 100644
--- a/libs/WindowManager/Shell/res/layout/size_compat_ui.xml
+++ b/libs/WindowManager/Shell/res/layout/size_compat_ui.xml
@@ -19,12 +19,21 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content">
- <ImageButton
- android:id="@+id/size_compat_restart_button"
- android:layout_width="@dimen/size_compat_button_size"
- android:layout_height="@dimen/size_compat_button_size"
- android:layout_gravity="center"
- android:src="@drawable/size_compat_restart_button"
- android:contentDescription="@string/restart_button_description"/>
+ <FrameLayout
+ android:layout_width="@dimen/size_compat_button_width"
+ android:layout_height="@dimen/size_compat_button_height"
+ android:clipToPadding="false"
+ android:paddingBottom="16dp">
+
+ <ImageButton
+ android:id="@+id/size_compat_restart_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:src="@drawable/size_compat_restart_button"
+ android:background="@android:color/transparent"
+ android:contentDescription="@string/restart_button_description"/>
+
+ </FrameLayout>
</com.android.wm.shell.sizecompatui.SizeCompatRestartButton>
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index 93c0352..b25a218 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -29,6 +29,7 @@
<color name="bubbles_light">#FFFFFF</color>
<color name="bubbles_dark">@color/GM2_grey_800</color>
<color name="bubbles_icon_tint">@color/GM2_grey_700</color>
+ <color name="size_compat_hint_bubble">#30312B</color>
<!-- GM2 colors -->
<color name="GM2_grey_200">#E8EAED</color>
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index d0e4f7a..0cdaa20 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -15,6 +15,10 @@
limitations under the License.
-->
<resources>
+ <!-- Determines whether the shell features all run on another thread. This is to be overrided
+ by the resources of the app using the Shell library. -->
+ <bool name="config_enableShellMainThread">false</bool>
+
<!-- Animation duration for PIP when entering. -->
<integer name="config_pipEnterAnimationDuration">425</integer>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index f8576643..e9b9ec3 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -115,6 +115,8 @@
<dimen name="bubble_spacing">3dp</dimen>
<!-- Size of the bubble. -->
<dimen name="bubble_size">60dp</dimen>
+ <!-- Width of bubble name view -->
+ <dimen name="bubble_name_width">90dp</dimen>
<!-- Size of the badge shown on the bubble. -->
<dimen name="bubble_badge_size">24dp</dimen>
<!-- Extra padding added to the touchable rect for bubbles so they are easier to grab. -->
@@ -194,8 +196,17 @@
<!-- Size of user education views on large screens (phone is just match parent). -->
<dimen name="bubbles_user_education_width_large_screen">400dp</dimen>
- <!-- The width/height of the size compat restart button. -->
- <dimen name="size_compat_button_size">48dp</dimen>
+ <!-- The width of the size compat restart button including padding. -->
+ <dimen name="size_compat_button_width">80dp</dimen>
+
+ <!-- The height of the size compat restart button including padding. -->
+ <dimen name="size_compat_button_height">64dp</dimen>
+
+ <!-- The radius of the corners of the size compat hint bubble. -->
+ <dimen name="size_compat_hint_corner_radius">28dp</dimen>
+
+ <!-- The width of the size compat hint point. -->
+ <dimen name="size_compat_hint_point_width">10dp</dimen>
<!-- The width of the brand image on staring surface. -->
<dimen name="starting_surface_brand_image_width">200dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index e512698..c88fc16 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -24,6 +24,9 @@
<!-- Label for PIP settings button [CHAR LIMIT=NONE]-->
<string name="pip_phone_settings">Settings</string>
+ <!-- Label for the PIP enter split button [CHAR LIMIT=NONE] -->
+ <string name="pip_phone_enter_split">Enter split screen</string>
+
<!-- Title of menu shown over picture-in-picture. Used for accessibility. -->
<string name="pip_menu_title">Menu</string>
@@ -155,7 +158,4 @@
<!-- Description of the restart button in the hint of size compatibility mode. [CHAR LIMIT=NONE] -->
<string name="restart_button_description">Tap to restart this app and go full screen.</string>
-
- <!-- Generic "got it" acceptance of dialog or cling [CHAR LIMIT=NONE] -->
- <string name="got_it">Got it</string>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
index e87b150..358553d7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
@@ -24,6 +24,7 @@
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreenController;
import java.io.PrintWriter;
@@ -43,6 +44,7 @@
private final Optional<OneHandedController> mOneHandedOptional;
private final Optional<HideDisplayCutoutController> mHideDisplayCutout;
private final Optional<AppPairsController> mAppPairsOptional;
+ private final Optional<RecentTasksController> mRecentTasks;
private final ShellTaskOrganizer mShellTaskOrganizer;
private final ShellExecutor mMainExecutor;
private final HandlerImpl mImpl = new HandlerImpl();
@@ -55,8 +57,10 @@
Optional<OneHandedController> oneHandedOptional,
Optional<HideDisplayCutoutController> hideDisplayCutout,
Optional<AppPairsController> appPairsOptional,
+ Optional<RecentTasksController> recentTasks,
ShellExecutor mainExecutor) {
mShellTaskOrganizer = shellTaskOrganizer;
+ mRecentTasks = recentTasks;
mLegacySplitScreenOptional = legacySplitScreenOptional;
mSplitScreenOptional = splitScreenOptional;
mPipOptional = pipOptional;
@@ -85,6 +89,9 @@
pw.println();
pw.println();
mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw, ""));
+ pw.println();
+ pw.println();
+ mRecentTasks.ifPresent(recentTasks -> recentTasks.dump(pw, ""));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
index fa58fcd..f567877 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
@@ -29,8 +29,8 @@
import com.android.wm.shell.freeform.FreeformTaskListener;
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
import com.android.wm.shell.fullscreen.FullscreenUnfoldController;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PipTouchHandler;
+import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.startingsurface.StartingWindowController;
import com.android.wm.shell.transition.Transitions;
@@ -49,7 +49,6 @@
private final DragAndDropController mDragAndDropController;
private final ShellTaskOrganizer mShellTaskOrganizer;
private final Optional<BubbleController> mBubblesOptional;
- private final Optional<LegacySplitScreenController> mLegacySplitScreenOptional;
private final Optional<SplitScreenController> mSplitScreenOptional;
private final Optional<AppPairsController> mAppPairsOptional;
private final Optional<PipTouchHandler> mPipTouchHandlerOptional;
@@ -59,6 +58,7 @@
private final ShellExecutor mMainExecutor;
private final Transitions mTransitions;
private final StartingWindowController mStartingWindow;
+ private final Optional<RecentTasksController> mRecentTasks;
private final InitImpl mImpl = new InitImpl();
@@ -69,13 +69,13 @@
DragAndDropController dragAndDropController,
ShellTaskOrganizer shellTaskOrganizer,
Optional<BubbleController> bubblesOptional,
- Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
Optional<AppPairsController> appPairsOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
Optional<FullscreenUnfoldController> fullscreenUnfoldTransitionController,
Optional<Optional<FreeformTaskListener>> freeformTaskListenerOptional,
+ Optional<RecentTasksController> recentTasks,
Transitions transitions,
StartingWindowController startingWindow,
ShellExecutor mainExecutor) {
@@ -85,13 +85,13 @@
mDragAndDropController = dragAndDropController;
mShellTaskOrganizer = shellTaskOrganizer;
mBubblesOptional = bubblesOptional;
- mLegacySplitScreenOptional = legacySplitScreenOptional;
mSplitScreenOptional = splitScreenOptional;
mAppPairsOptional = appPairsOptional;
mFullscreenTaskListener = fullscreenTaskListener;
mPipTouchHandlerOptional = pipTouchHandlerOptional;
mFullscreenUnfoldController = fullscreenUnfoldTransitionController;
mFreeformTaskListenerOptional = freeformTaskListenerOptional.flatMap(f -> f);
+ mRecentTasks = recentTasks;
mTransitions = transitions;
mMainExecutor = mainExecutor;
mStartingWindow = startingWindow;
@@ -135,6 +135,7 @@
f, ShellTaskOrganizer.TASK_LISTENER_TYPE_FREEFORM));
mFullscreenUnfoldController.ifPresent(FullscreenUnfoldController::init);
+ mRecentTasks.ifPresent(RecentTasksController::init);
}
@ExternalThread
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 020ecb7..75bc461 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -51,6 +51,7 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.wm.shell.common.ScreenshotUtils;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.sizecompatui.SizeCompatUIController;
import com.android.wm.shell.startingsurface.StartingWindowController;
@@ -59,6 +60,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
import java.util.function.Consumer;
/**
@@ -150,20 +152,34 @@
@Nullable
private final SizeCompatUIController mSizeCompatUI;
+ @Nullable
+ private final Optional<RecentTasksController> mRecentTasks;
+
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) {
- this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */);
+ this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */,
+ Optional.empty() /* recentTasksController */);
}
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
SizeCompatUIController sizeCompatUI) {
- this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI);
+ this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI,
+ Optional.empty() /* recentTasksController */);
+ }
+
+ public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
+ SizeCompatUIController sizeCompatUI,
+ Optional<RecentTasksController> recentTasks) {
+ this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI,
+ recentTasks);
}
@VisibleForTesting
ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController, ShellExecutor mainExecutor,
- Context context, @Nullable SizeCompatUIController sizeCompatUI) {
+ Context context, @Nullable SizeCompatUIController sizeCompatUI,
+ Optional<RecentTasksController> recentTasks) {
super(taskOrganizerController, mainExecutor);
mSizeCompatUI = sizeCompatUI;
+ mRecentTasks = recentTasks;
if (sizeCompatUI != null) {
sizeCompatUI.setSizeCompatUICallback(this);
}
@@ -401,6 +417,11 @@
// Notify the size compat UI if the listener or task info changed.
notifySizeCompatUI(taskInfo, newListener);
}
+ if (data.getTaskInfo().getWindowingMode() != taskInfo.getWindowingMode()) {
+ // Notify the recent tasks when a task changes windowing modes
+ mRecentTasks.ifPresent(recentTasks ->
+ recentTasks.onTaskWindowingModeChanged(taskInfo));
+ }
}
}
@@ -428,6 +449,8 @@
notifyLocusVisibilityIfNeeded(taskInfo);
// Pass null for listener to remove the size compat UI on this task if there is any.
notifySizeCompatUI(taskInfo, null /* taskListener */);
+ // Notify the recent tasks that a task has been removed
+ mRecentTasks.ifPresent(recentTasks -> recentTasks.onTaskRemoved(taskInfo));
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index b6d65be..f7af4e1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1028,13 +1028,16 @@
// If this entry is no longer allowed to bubble, dismiss with the BLOCKED reason.
// This means that the app or channel's ability to bubble has been revoked.
mBubbleData.dismissBubbleWithKey(key, DISMISS_BLOCKED);
- } else if (isActiveBubble && !shouldBubbleUp) {
- // If this entry is allowed to bubble, but cannot currently bubble up, dismiss it.
- // This happens when DND is enabled and configured to hide bubbles. Dismissing with
- // the reason DISMISS_NO_BUBBLE_UP will retain the underlying notification, so that
- // the bubble will be re-created if shouldBubbleUp returns true.
+ } else if (isActiveBubble && (!shouldBubbleUp || entry.getRanking().isSuspended())) {
+ // If this entry is allowed to bubble, but cannot currently bubble up or is
+ // suspended, dismiss it. This happens when DND is enabled and configured to hide
+ // bubbles, or focus mode is enabled and the app is designated as distracting.
+ // Dismissing with the reason DISMISS_NO_BUBBLE_UP will retain the underlying
+ // notification, so that the bubble will be re-created if shouldBubbleUp returns
+ // true.
mBubbleData.dismissBubbleWithKey(key, DISMISS_NO_BUBBLE_UP);
- } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) {
+ } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble
+ && !entry.getRanking().isSuspended()) {
entry.setFlagBubble(true);
onEntryUpdated(entry, true /* shouldBubbleUp */);
}
@@ -1134,7 +1137,8 @@
if (reason == DISMISS_USER_CHANGED || reason == DISMISS_NO_BUBBLE_UP) {
continue;
}
- if (reason == DISMISS_NOTIF_CANCEL) {
+ if (reason == DISMISS_NOTIF_CANCEL
+ || reason == DISMISS_SHORTCUT_REMOVED) {
bubblesToBeRemovedFromRepository.add(bubble);
}
if (!mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SingleInstanceRemoteListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SingleInstanceRemoteListener.java
new file mode 100644
index 0000000..b77ac8a
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SingleInstanceRemoteListener.java
@@ -0,0 +1,123 @@
+/*
+ * 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.wm.shell.common;
+
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import androidx.annotation.BinderThread;
+
+import java.util.function.Consumer;
+
+/**
+ * Manages the lifecycle of a single instance of a remote listener, including the clean up if the
+ * remote process dies. All calls on this class should happen on the main shell thread.
+ *
+ * @param <C> The controller (must be RemoteCallable)
+ * @param <L> The remote listener interface type
+ */
+public class SingleInstanceRemoteListener<C extends RemoteCallable, L extends IInterface> {
+ private static final String TAG = SingleInstanceRemoteListener.class.getSimpleName();
+
+ /**
+ * Simple callable interface that throws a remote exception.
+ */
+ public interface RemoteCall<L> {
+ void accept(L l) throws RemoteException;
+ }
+
+ private final C mCallableController;
+ private final Consumer<C> mOnRegisterCallback;
+ private final Consumer<C> mOnUnregisterCallback;
+
+ L mListener;
+
+ private final IBinder.DeathRecipient mListenerDeathRecipient =
+ new IBinder.DeathRecipient() {
+ @Override
+ @BinderThread
+ public void binderDied() {
+ final C callableController = mCallableController;
+ mCallableController.getRemoteCallExecutor().execute(() -> {
+ mListener = null;
+ mOnUnregisterCallback.accept(callableController);
+ });
+ }
+ };
+
+ /**
+ * @param onRegisterCallback Callback when register() is called (same thread)
+ * @param onUnregisterCallback Callback when unregister() is called (same thread as unregister()
+ * or the callableController.getRemoteCallbackExecutor() thread)
+ */
+ public SingleInstanceRemoteListener(C callableController,
+ Consumer<C> onRegisterCallback,
+ Consumer<C> onUnregisterCallback) {
+ mCallableController = callableController;
+ mOnRegisterCallback = onRegisterCallback;
+ mOnUnregisterCallback = onUnregisterCallback;
+ }
+
+ /**
+ * Registers this listener, storing a reference to it and calls the provided method in the
+ * constructor.
+ */
+ public void register(L listener) {
+ if (mListener != null) {
+ mListener.asBinder().unlinkToDeath(mListenerDeathRecipient, 0 /* flags */);
+ }
+ if (listener != null) {
+ try {
+ listener.asBinder().linkToDeath(mListenerDeathRecipient, 0 /* flags */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to link to death");
+ return;
+ }
+ }
+ mListener = listener;
+ mOnRegisterCallback.accept(mCallableController);
+ }
+
+ /**
+ * Unregisters this listener, removing all references to it and calls the provided method in the
+ * constructor.
+ */
+ public void unregister() {
+ if (mListener != null) {
+ mListener.asBinder().unlinkToDeath(mListenerDeathRecipient, 0 /* flags */);
+ }
+ mListener = null;
+ mOnUnregisterCallback.accept(mCallableController);
+ }
+
+ /**
+ * Safely wraps a call to the remote listener.
+ */
+ public void call(RemoteCall<L> handler) {
+ if (mListener == null) {
+ Slog.e(TAG, "Failed remote call on null listener");
+ return;
+ }
+ try {
+ handler.accept(mListener);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed remote call", e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ExternalMainThread.java
similarity index 71%
copy from packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java
copy to libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ExternalMainThread.java
index 7292b9e..9ac7a12 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ExternalMainThread.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,20 +14,21 @@
* limitations under the License.
*/
-package com.android.systemui.dagger;
+package com.android.wm.shell.common.annotations;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
-import javax.inject.Scope;
+import javax.inject.Qualifier;
/**
- * Scope annotation for singleton items within the WMComponent.
+ * Annotates a method or qualifies a provider that runs on the main-thread of the process using
+ * this library.
*/
+@Qualifier
@Documented
@Retention(RUNTIME)
-@Scope
-public @interface WMSingleton {
+public @interface ExternalMainThread {
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/README.txt b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/README.txt
new file mode 100644
index 0000000..1cd69ed
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/README.txt
@@ -0,0 +1,13 @@
+The dagger modules in this directory can be included by the host SysUI using the Shell library for
+explicity injection of Shell components. Apps using this library are not required to use these
+dagger modules for setup, but it is recommended for them to include them as needed.
+
+The modules are currently inherited as such:
+
++- WMShellBaseModule (common shell features across SysUI)
+ |
+ +- WMShellModule (handheld)
+ |
+ +- TvPipModule (tv pip)
+ |
+ +- TvWMShellModule (tv)
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index 3be1d3c..711a0ac 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,12 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.wmshell;
+package com.android.wm.shell.dagger;
import android.content.Context;
import android.os.Handler;
-import com.android.systemui.dagger.WMSingleton;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayController;
@@ -44,6 +43,7 @@
import com.android.wm.shell.pip.tv.TvPipMenuController;
import com.android.wm.shell.pip.tv.TvPipNotificationController;
import com.android.wm.shell.pip.tv.TvPipTransition;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -161,13 +161,14 @@
PipTransitionController pipTransitionController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<LegacySplitScreenController> splitScreenOptional,
+ Optional<SplitScreenController> newSplitScreenOptional,
DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context,
syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper,
- pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
- shellTaskOrganizer, mainExecutor);
+ pipTransitionController, splitScreenOptional, newSplitScreenOptional,
+ displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
index dbdc460..6997d60 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,12 @@
* limitations under the License.
*/
-package com.android.systemui.wmshell;
+package com.android.wm.shell.dagger;
import android.animation.AnimationHandler;
import android.content.Context;
import android.view.IWindowManager;
-import com.android.systemui.dagger.WMComponent;
-import com.android.systemui.dagger.WMSingleton;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 0a33930..ac2e448 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.wmshell;
+package com.android.wm.shell.dagger;
import android.app.ActivityTaskManager;
import android.content.Context;
@@ -27,8 +27,6 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.launcher3.icons.IconProvider;
-import com.android.systemui.dagger.WMComponent;
-import com.android.systemui.dagger.WMSingleton;
import com.android.wm.shell.RootDisplayAreaOrganizer;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellCommandHandler;
@@ -74,6 +72,8 @@
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.phone.PipAppOpsListener;
import com.android.wm.shell.pip.phone.PipTouchHandler;
+import com.android.wm.shell.recents.RecentTasks;
+import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.sizecompatui.SizeCompatUIController;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -144,16 +144,20 @@
@WMSingleton
@Provides
static ShellTaskOrganizer provideShellTaskOrganizer(@ShellMainThread ShellExecutor mainExecutor,
- Context context, SizeCompatUIController sizeCompatUI) {
- return new ShellTaskOrganizer(mainExecutor, context, sizeCompatUI);
+ Context context,
+ SizeCompatUIController sizeCompatUI,
+ Optional<RecentTasksController> recentTasksOptional
+ ) {
+ return new ShellTaskOrganizer(mainExecutor, context, sizeCompatUI, recentTasksOptional);
}
@WMSingleton
@Provides
static SizeCompatUIController provideSizeCompatUIController(Context context,
- DisplayController displayController, DisplayImeController imeController,
- SyncTransactionQueue syncQueue) {
- return new SizeCompatUIController(context, displayController, imeController, syncQueue);
+ DisplayController displayController, DisplayInsetsController displayInsetsController,
+ DisplayImeController imeController, SyncTransactionQueue syncQueue) {
+ return new SizeCompatUIController(context, displayController, displayInsetsController,
+ imeController, syncQueue);
}
@WMSingleton
@@ -236,8 +240,12 @@
@WMSingleton
@Provides
static FullscreenTaskListener provideFullscreenTaskListener(
- SyncTransactionQueue syncQueue, Optional<FullscreenUnfoldController> controller) {
- return new FullscreenTaskListener(syncQueue, controller);
+ SyncTransactionQueue syncQueue,
+ Optional<FullscreenUnfoldController> optionalFullscreenUnfoldController,
+ Optional<RecentTasksController> recentTasksOptional
+ ) {
+ return new FullscreenTaskListener(syncQueue, optionalFullscreenUnfoldController,
+ recentTasksOptional);
}
//
@@ -410,6 +418,28 @@
abstract PipTouchHandler optionalPipTouchHandler();
//
+ // Recent tasks
+ //
+
+ @WMSingleton
+ @Provides
+ static Optional<RecentTasks> provideRecentTasks(
+ Optional<RecentTasksController> recentTasksController) {
+ return recentTasksController.map((controller) -> controller.asRecentTasks());
+ }
+
+ @WMSingleton
+ @Provides
+ static Optional<RecentTasksController> provideRecentTasksController(
+ Context context,
+ TaskStackListenerImpl taskStackListener,
+ @ShellMainThread ShellExecutor mainExecutor
+ ) {
+ return Optional.ofNullable(
+ RecentTasksController.create(context, taskStackListener, mainExecutor));
+ }
+
+ //
// Shell transitions
//
@@ -464,12 +494,13 @@
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
TransactionPool transactionPool, IconProvider iconProvider,
+ Optional<RecentTasksController> recentTasks,
Provider<Optional<StageTaskUnfoldController>> stageTaskUnfoldControllerProvider) {
if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context,
rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController,
displayInsetsController, transitions, transactionPool, iconProvider,
- stageTaskUnfoldControllerProvider));
+ recentTasks, stageTaskUnfoldControllerProvider));
} else {
return Optional.empty();
}
@@ -555,13 +586,13 @@
DragAndDropController dragAndDropController,
ShellTaskOrganizer shellTaskOrganizer,
Optional<BubbleController> bubblesOptional,
- Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
Optional<AppPairsController> appPairsOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
Optional<FullscreenUnfoldController> appUnfoldTransitionController,
Optional<Optional<FreeformTaskListener>> freeformTaskListener,
+ Optional<RecentTasksController> recentTasksOptional,
Transitions transitions,
StartingWindowController startingWindow,
@ShellMainThread ShellExecutor mainExecutor) {
@@ -571,13 +602,13 @@
dragAndDropController,
shellTaskOrganizer,
bubblesOptional,
- legacySplitScreenOptional,
splitScreenOptional,
appPairsOptional,
pipTouchHandlerOptional,
fullscreenTaskListener,
appUnfoldTransitionController,
freeformTaskListener,
+ recentTasksOptional,
transitions,
startingWindow,
mainExecutor);
@@ -603,9 +634,10 @@
Optional<OneHandedController> oneHandedOptional,
Optional<HideDisplayCutoutController> hideDisplayCutout,
Optional<AppPairsController> appPairsOptional,
+ Optional<RecentTasksController> recentTasksOptional,
@ShellMainThread ShellExecutor mainExecutor) {
return new ShellCommandHandlerImpl(shellTaskOrganizer,
legacySplitScreenOptional, splitScreenOptional, pipOptional, oneHandedOptional,
- hideDisplayCutout, appPairsOptional, mainExecutor);
+ hideDisplayCutout, appPairsOptional, recentTasksOptional, mainExecutor);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellConcurrencyModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/wmshell/WMShellConcurrencyModule.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
index 61f50b5..5c205f9 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellConcurrencyModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.wmshell;
+package com.android.wm.shell.dagger;
import static android.os.Process.THREAD_PRIORITY_DISPLAY;
import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST;
@@ -24,18 +24,18 @@
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.Looper;
import android.os.Trace;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-import com.android.systemui.R;
-import com.android.systemui.dagger.WMSingleton;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.wm.shell.common.HandlerExecutor;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.annotations.ChoreographerSfVsync;
+import com.android.wm.shell.common.annotations.ExternalMainThread;
import com.android.wm.shell.common.annotations.ShellAnimationThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
+import com.android.wm.shell.R;
import dagger.Module;
import dagger.Provides;
@@ -61,13 +61,26 @@
// Shell Concurrency - Components used for managing threading in the Shell and SysUI
//
+
+ /**
+ * Provide a SysUI main-thread Handler.
+ *
+ * Prefer the Main Executor when possible.
+ */
+ @Provides
+ @ExternalMainThread
+ public static Handler provideMainHandler() {
+ return new Handler(Looper.getMainLooper());
+ }
+
/**
* Provide a SysUI main-thread Executor.
*/
@WMSingleton
@Provides
- @Main
- public static ShellExecutor provideSysUIMainExecutor(@Main Handler sysuiMainHandler) {
+ @ExternalMainThread
+ public static ShellExecutor provideSysUIMainExecutor(
+ @ExternalMainThread Handler sysuiMainHandler) {
return new HandlerExecutor(sysuiMainHandler);
}
@@ -78,7 +91,8 @@
@WMSingleton
@Provides
@ShellMainThread
- public static Handler provideShellMainHandler(Context context, @Main Handler sysuiMainHandler) {
+ public static Handler provideShellMainHandler(Context context,
+ @ExternalMainThread Handler sysuiMainHandler) {
if (enableShellMainThread(context)) {
HandlerThread mainThread = new HandlerThread("wmshell.main", THREAD_PRIORITY_DISPLAY);
mainThread.start();
@@ -99,7 +113,8 @@
@Provides
@ShellMainThread
public static ShellExecutor provideShellMainExecutor(Context context,
- @ShellMainThread Handler mainHandler, @Main ShellExecutor sysuiMainExecutor) {
+ @ShellMainThread Handler mainHandler,
+ @ExternalMainThread ShellExecutor sysuiMainExecutor) {
if (enableShellMainThread(context)) {
return new HandlerExecutor(mainHandler);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index a7c5ad2..ec70147 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,15 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.wmshell;
+package com.android.wm.shell.dagger;
import android.animation.AnimationHandler;
import android.content.Context;
import android.os.Handler;
import android.view.IWindowManager;
-import com.android.systemui.dagger.WMComponent;
-import com.android.systemui.dagger.WMSingleton;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.apppairs.AppPairsController;
@@ -57,6 +55,7 @@
import com.android.wm.shell.pip.phone.PipController;
import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.pip.phone.PipTouchHandler;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
import com.android.wm.shell.startingsurface.phone.PhoneStartingWindowTypeAlgorithm;
import com.android.wm.shell.transition.Transitions;
@@ -217,14 +216,15 @@
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
PipTransitionController pipTransitionController,
Optional<LegacySplitScreenController> splitScreenOptional,
+ Optional<SplitScreenController> newSplitScreenOptional,
DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context,
syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper,
- pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
- shellTaskOrganizer, mainExecutor);
+ pipTransitionController, splitScreenOptional, newSplitScreenOptional,
+ displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
}
@WMSingleton
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMSingleton.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMSingleton.java
index 7292b9e..7f45c38 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMSingleton.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.dagger;
+package com.android.wm.shell.dagger;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.java
index 6e4b815..2a7dd5a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.java
@@ -25,6 +25,8 @@
import android.content.pm.ActivityInfo;
import android.view.DragEvent;
+import androidx.annotation.Nullable;
+
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.UiEvent;
@@ -61,9 +63,7 @@
mInstanceId = mIdSequence.newInstanceId();
}
mActivityInfo = item.getActivityInfo();
- mUiEventLogger.logWithInstanceId(getStartEnum(description),
- mActivityInfo.applicationInfo.uid,
- mActivityInfo.applicationInfo.packageName, mInstanceId);
+ log(getStartEnum(description), mActivityInfo);
return mInstanceId;
}
@@ -71,18 +71,21 @@
* Logs a successful drop.
*/
public void logDrop() {
- mUiEventLogger.logWithInstanceId(DragAndDropUiEventEnum.GLOBAL_APP_DRAG_DROPPED,
- mActivityInfo.applicationInfo.uid,
- mActivityInfo.applicationInfo.packageName, mInstanceId);
+ log(DragAndDropUiEventEnum.GLOBAL_APP_DRAG_DROPPED, mActivityInfo);
}
/**
* Logs the end of a drag.
*/
public void logEnd() {
- mUiEventLogger.logWithInstanceId(DragAndDropUiEventEnum.GLOBAL_APP_DRAG_END,
- mActivityInfo.applicationInfo.uid,
- mActivityInfo.applicationInfo.packageName, mInstanceId);
+ log(DragAndDropUiEventEnum.GLOBAL_APP_DRAG_END, mActivityInfo);
+ }
+
+ private void log(UiEventLogger.UiEventEnum event, @Nullable ActivityInfo activityInfo) {
+ mUiEventLogger.logWithInstanceId(event,
+ activityInfo == null ? 0 : activityInfo.applicationInfo.uid,
+ activityInfo == null ? null : activityInfo.applicationInfo.packageName,
+ mInstanceId);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index 5fb3297..8a8d7c6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.freeform;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
@@ -27,6 +28,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.view.SurfaceControl;
+import android.window.WindowContainerTransaction;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -83,6 +85,13 @@
Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
return;
}
+
+ // Clears windowing mode and window bounds to let the task inherits from its new parent.
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setBounds(taskInfo.token, null)
+ .setWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED);
+ mSyncQueue.queue(wct);
+
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Vanished: #%d",
taskInfo.taskId);
mTasks.remove(taskInfo.taskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
index 3f17f2b..6e38e42 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
@@ -35,6 +35,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
@@ -47,15 +48,23 @@
private static final String TAG = "FullscreenTaskListener";
private final SyncTransactionQueue mSyncQueue;
+ private final FullscreenUnfoldController mFullscreenUnfoldController;
+ private final Optional<RecentTasksController> mRecentTasksOptional;
private final SparseArray<TaskData> mDataByTaskId = new SparseArray<>();
private final AnimatableTasksListener mAnimatableTasksListener = new AnimatableTasksListener();
- private final FullscreenUnfoldController mFullscreenUnfoldController;
public FullscreenTaskListener(SyncTransactionQueue syncQueue,
Optional<FullscreenUnfoldController> unfoldController) {
+ this(syncQueue, unfoldController, Optional.empty());
+ }
+
+ public FullscreenTaskListener(SyncTransactionQueue syncQueue,
+ Optional<FullscreenUnfoldController> unfoldController,
+ Optional<RecentTasksController> recentTasks) {
mSyncQueue = syncQueue;
mFullscreenUnfoldController = unfoldController.orElse(null);
+ mRecentTasksOptional = recentTasks;
}
@Override
@@ -79,6 +88,7 @@
});
mAnimatableTasksListener.onTaskAppeared(taskInfo);
+ updateRecentsForVisibleFullscreenTask(taskInfo);
}
@Override
@@ -86,6 +96,7 @@
if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
mAnimatableTasksListener.onTaskInfoChanged(taskInfo);
+ updateRecentsForVisibleFullscreenTask(taskInfo);
final TaskData data = mDataByTaskId.get(taskInfo.taskId);
final Point positionInParent = taskInfo.positionInParent;
@@ -111,6 +122,15 @@
taskInfo.taskId);
}
+ private void updateRecentsForVisibleFullscreenTask(RunningTaskInfo taskInfo) {
+ mRecentTasksOptional.ifPresent(recentTasks -> {
+ if (taskInfo.isVisible) {
+ // Remove any persisted splits if either tasks are now made fullscreen and visible
+ recentTasks.removeSplitPair(taskInfo.taskId);
+ }
+ });
+ }
+
@Override
public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
if (!mDataByTaskId.contains(taskId)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index b6e5804..6cc5f09 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -77,6 +77,7 @@
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PipMotionHelper;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
@@ -126,7 +127,8 @@
private final int mExitAnimationDuration;
private final int mCrossFadeAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
- private final Optional<LegacySplitScreenController> mSplitScreenOptional;
+ private final Optional<LegacySplitScreenController> mLegacySplitScreenOptional;
+ private final Optional<SplitScreenController> mSplitScreenOptional;
protected final ShellTaskOrganizer mTaskOrganizer;
protected final ShellExecutor mMainExecutor;
@@ -252,7 +254,8 @@
@NonNull PipAnimationController pipAnimationController,
@NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
@NonNull PipTransitionController pipTransitionController,
- Optional<LegacySplitScreenController> splitScreenOptional,
+ Optional<LegacySplitScreenController> legacySplitScreenOptional,
+ Optional<SplitScreenController> splitScreenOptional,
@NonNull DisplayController displayController,
@NonNull PipUiEventLogger pipUiEventLogger,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@@ -274,6 +277,7 @@
mPipAnimationController = pipAnimationController;
mPipUiEventLoggerLogger = pipUiEventLogger;
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
+ mLegacySplitScreenOptional = legacySplitScreenOptional;
mSplitScreenOptional = splitScreenOptional;
mTaskOrganizer = shellTaskOrganizer;
mMainExecutor = mainExecutor;
@@ -373,8 +377,11 @@
* activity render it's final configuration while the Task is still in PiP.
* - setWindowingMode to undefined at the end of transition
* @param animationDurationMs duration in millisecond for the exiting PiP transition
+ * @param requestEnterSplit whether the enterSplit button is pressed on PiP or not.
+ * Indicate the user wishes to directly put PiP into split screen
+ * mode.
*/
- public void exitPip(int animationDurationMs) {
+ public void exitPip(int animationDurationMs, boolean requestEnterSplit) {
if (!mPipTransitionState.isInPip()
|| mPipTransitionState.getTransitionState() == PipTransitionState.EXITING_PIP
|| mToken == null) {
@@ -387,7 +394,7 @@
PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
final WindowContainerTransaction wct = new WindowContainerTransaction();
final Rect destinationBounds = mPipBoundsState.getDisplayBounds();
- final int direction = syncWithSplitScreenBounds(destinationBounds)
+ final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit)
? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
: TRANSITION_DIRECTION_LEAVE_PIP;
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
@@ -396,7 +403,7 @@
// We set to fullscreen here for now, but later it will be set to UNDEFINED for
// the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
wct.setActivityWindowingMode(mToken,
- direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
+ direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN && !requestEnterSplit
? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
: WINDOWING_MODE_FULLSCREEN);
wct.setBounds(mToken, destinationBounds);
@@ -435,7 +442,7 @@
wct.setWindowingMode(mToken, getOutPipWindowingMode());
// Simply reset the activity mode set prior to the animation running.
wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
- mSplitScreenOptional.ifPresent(splitScreen -> {
+ mLegacySplitScreenOptional.ifPresent(splitScreen -> {
if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
wct.reparent(mToken, splitScreen.getSecondaryRoot(), true /* onTop */);
}
@@ -1165,6 +1172,7 @@
@PipAnimationController.TransitionDirection int direction,
@PipAnimationController.AnimationType int type) {
final Rect preResizeBounds = new Rect(mPipBoundsState.getBounds());
+ final boolean isPipTopLeft = isPipTopLeft();
mPipBoundsState.setBounds(destinationBounds);
if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
removePipImmediately();
@@ -1210,10 +1218,10 @@
null /* callback */, false /* withStartDelay */);
});
} else {
- applyFinishBoundsResize(wct, direction);
+ applyFinishBoundsResize(wct, direction, isPipTopLeft);
}
} else {
- applyFinishBoundsResize(wct, direction);
+ applyFinishBoundsResize(wct, direction, isPipTopLeft);
}
finishResizeForMenu(destinationBounds);
@@ -1241,7 +1249,11 @@
} else if (isOutPipDirection(direction)) {
// If we are animating to fullscreen or split screen, then we need to reset the
// override bounds on the task to ensure that the task "matches" the parent's bounds.
- taskBounds = null;
+ if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
+ taskBounds = destinationBounds;
+ } else {
+ taskBounds = null;
+ }
applyWindowingModeChangeOnExit(wct, direction);
} else {
// Just a resize in PIP
@@ -1261,8 +1273,20 @@
* applying it.
*/
public void applyFinishBoundsResize(@NonNull WindowContainerTransaction wct,
- @PipAnimationController.TransitionDirection int direction) {
- mTaskOrganizer.applyTransaction(wct);
+ @PipAnimationController.TransitionDirection int direction, boolean wasPipTopLeft) {
+ if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
+ mSplitScreenOptional.get().enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct);
+ } else {
+ mTaskOrganizer.applyTransaction(wct);
+ }
+ }
+
+ private boolean isPipTopLeft() {
+ final Rect topLeft = new Rect();
+ final Rect bottomRight = new Rect();
+ mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
+
+ return topLeft.contains(mPipBoundsState.getBounds());
}
/**
@@ -1347,18 +1371,27 @@
}
/**
- * Sync with {@link LegacySplitScreenController} on destination bounds if PiP is going to split
- * screen.
+ * Sync with {@link LegacySplitScreenController} or {@link SplitScreenController} on destination
+ * bounds if PiP is going to split screen.
*
* @param destinationBoundsOut contain the updated destination bounds if applicable
* @return {@code true} if destinationBounds is altered for split screen
*/
- private boolean syncWithSplitScreenBounds(Rect destinationBoundsOut) {
- if (!mSplitScreenOptional.isPresent()) {
+ private boolean syncWithSplitScreenBounds(Rect destinationBoundsOut, boolean enterSplit) {
+ if (enterSplit && mSplitScreenOptional.isPresent()) {
+ final Rect topLeft = new Rect();
+ final Rect bottomRight = new Rect();
+ mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
+ final boolean isPipTopLeft = isPipTopLeft();
+ destinationBoundsOut.set(isPipTopLeft ? topLeft : bottomRight);
+ return true;
+ }
+
+ if (!mLegacySplitScreenOptional.isPresent()) {
return false;
}
- LegacySplitScreenController legacySplitScreen = mSplitScreenOptional.get();
+ LegacySplitScreenController legacySplitScreen = mLegacySplitScreenOptional.get();
if (!legacySplitScreen.isDividerVisible()) {
// fail early if system is not in split screen mode
return false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index ae8c1b6..5687f4d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -95,6 +95,11 @@
* Called when the PIP requested to show the menu.
*/
void onPipShowMenu();
+
+ /**
+ * Called when the PIP requested to enter Split.
+ */
+ void onEnterSplit();
}
private final Matrix mMoveTransform = new Matrix();
@@ -458,6 +463,10 @@
mListeners.forEach(Listener::onPipDismiss);
}
+ void onEnterSplit() {
+ mListeners.forEach(Listener::onEnterSplit);
+ }
+
/**
* @return the best set of actions to show in the PiP menu.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
index 47a8c67..69ae45d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
@@ -151,7 +151,7 @@
result = true;
break;
case AccessibilityNodeInfo.ACTION_EXPAND:
- mMotionHelper.expandLeavePip();
+ mMotionHelper.expandLeavePip(false /* skipAnimation */);
result = true;
break;
default:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 2e918f6..aeeb73f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -43,7 +43,6 @@
import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
import android.graphics.Rect;
-import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -69,6 +68,7 @@
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.onehanded.OneHandedController;
@@ -117,13 +117,28 @@
private final Rect mTmpInsetBounds = new Rect();
private boolean mIsInFixedRotation;
- private IPipAnimationListener mPinnedStackAnimationRecentsCallback;
+ private PipAnimationListener mPinnedStackAnimationRecentsCallback;
protected PhonePipMenuController mMenuController;
protected PipTaskOrganizer mPipTaskOrganizer;
protected PinnedStackListenerForwarder.PinnedTaskListener mPinnedTaskListener =
new PipControllerPinnedTaskListener();
+ private interface PipAnimationListener {
+ /**
+ * Notifies the listener that the Pip animation is started.
+ */
+ void onPipAnimationStarted();
+
+ /**
+ * Notifies the listener about PiP round corner radius changes.
+ * Listener can expect an immediate callback the first time they attach.
+ *
+ * @param cornerRadius the pixel value of the corner radius, zero means it's disabled.
+ */
+ void onPipCornerRadiusChanged(int cornerRadius);
+ }
+
/**
* Handler for display rotation changes.
*/
@@ -467,19 +482,33 @@
false /* fromShelfAdjustment */,
wct /* windowContainerTransaction */);
if (wct != null) {
- mPipTaskOrganizer.applyFinishBoundsResize(wct, TRANSITION_DIRECTION_SAME);
+ mPipTaskOrganizer.applyFinishBoundsResize(wct, TRANSITION_DIRECTION_SAME,
+ false /* wasPipTopLeft */);
}
};
if (mPipTaskOrganizer.isInPip() && saveRestoreSnapFraction) {
// Calculate the snap fraction of the current stack along the old movement bounds
final PipSnapAlgorithm pipSnapAlgorithm = mPipBoundsAlgorithm.getSnapAlgorithm();
- final Rect postChangeStackBounds = new Rect(mPipBoundsState.getBounds());
- final float snapFraction = pipSnapAlgorithm.getSnapFraction(postChangeStackBounds,
- mPipBoundsAlgorithm.getMovementBounds(postChangeStackBounds),
+ final float snapFraction = pipSnapAlgorithm.getSnapFraction(mPipBoundsState.getBounds(),
+ mPipBoundsAlgorithm.getMovementBounds(mPipBoundsState.getBounds()),
mPipBoundsState.getStashedState());
updateDisplayLayout.run();
+ final Rect postChangeStackBounds;
+ if (mPipBoundsState.getBounds() != null
+ && (mPipBoundsState.getBounds().width() > mPipBoundsState.getMaxSize().x
+ || mPipBoundsState.getBounds().height() > mPipBoundsState.getMaxSize().y)) {
+ postChangeStackBounds = new Rect(0, 0, mPipBoundsState.getMaxSize().x,
+ mPipBoundsState.getMaxSize().y);
+ } else if (mPipBoundsState.getBounds() != null
+ && (mPipBoundsState.getBounds().width() < mPipBoundsState.getMinSize().x
+ || mPipBoundsState.getBounds().height() < mPipBoundsState.getMinSize().y)) {
+ postChangeStackBounds = new Rect(0, 0, mPipBoundsState.getMinSize().x,
+ mPipBoundsState.getMinSize().y);
+ } else {
+ postChangeStackBounds = new Rect(mPipBoundsState.getBounds());
+ }
// Calculate the stack bounds in the new orientation based on same fraction along the
// rotated movement bounds.
@@ -491,7 +520,7 @@
mPipBoundsState.getDisplayBounds(),
mPipBoundsState.getDisplayLayout().stableInsets());
- mTouchHandler.getMotionHelper().movePip(postChangeStackBounds);
+ mTouchHandler.getMotionHelper().animateResizedBounds(postChangeStackBounds);
} else {
updateDisplayLayout.run();
}
@@ -551,7 +580,7 @@
animationType == PipAnimationController.ANIM_TYPE_BOUNDS);
}
- private void setPinnedStackAnimationListener(IPipAnimationListener callback) {
+ private void setPinnedStackAnimationListener(PipAnimationListener callback) {
mPinnedStackAnimationRecentsCallback = callback;
onPipCornerRadiusChanged();
}
@@ -560,11 +589,7 @@
if (mPinnedStackAnimationRecentsCallback != null) {
final int cornerRadius =
mContext.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
- try {
- mPinnedStackAnimationRecentsCallback.onPipCornerRadiusChanged(cornerRadius);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to call onPipCornerRadiusChanged", e);
- }
+ mPinnedStackAnimationRecentsCallback.onPipCornerRadiusChanged(cornerRadius);
}
}
@@ -623,11 +648,7 @@
// Disable touches while the animation is running
mTouchHandler.setTouchEnabled(false);
if (mPinnedStackAnimationRecentsCallback != null) {
- try {
- mPinnedStackAnimationRecentsCallback.onPipAnimationStarted();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to call onPinnedStackAnimationStarted()", e);
- }
+ mPinnedStackAnimationRecentsCallback.onPipAnimationStarted();
}
}
@@ -866,22 +887,25 @@
@BinderThread
private static class IPipImpl extends IPip.Stub {
private PipController mController;
- private IPipAnimationListener mListener;
- private final IBinder.DeathRecipient mListenerDeathRecipient =
- new IBinder.DeathRecipient() {
- @Override
- @BinderThread
- public void binderDied() {
- final PipController controller = mController;
- controller.getRemoteCallExecutor().execute(() -> {
- mListener = null;
- controller.setPinnedStackAnimationListener(null);
- });
- }
- };
+ private final SingleInstanceRemoteListener<PipController,
+ IPipAnimationListener> mListener;
+ private final PipAnimationListener mPipAnimationListener = new PipAnimationListener() {
+ @Override
+ public void onPipAnimationStarted() {
+ mListener.call(l -> l.onPipAnimationStarted());
+ }
+
+ @Override
+ public void onPipCornerRadiusChanged(int cornerRadius) {
+ mListener.call(l -> l.onPipCornerRadiusChanged(cornerRadius));
+ }
+ };
IPipImpl(PipController controller) {
mController = controller;
+ mListener = new SingleInstanceRemoteListener<>(mController,
+ c -> c.setPinnedStackAnimationListener(mPipAnimationListener),
+ c -> c.setPinnedStackAnimationListener(null));
}
/**
@@ -925,23 +949,11 @@
public void setPinnedStackAnimationListener(IPipAnimationListener listener) {
executeRemoteCallWithTaskPermission(mController, "setPinnedStackAnimationListener",
(controller) -> {
- if (mListener != null) {
- // Reset the old death recipient
- mListener.asBinder().unlinkToDeath(mListenerDeathRecipient,
- 0 /* flags */);
- }
if (listener != null) {
- // Register the death recipient for the new listener to clear the listener
- try {
- listener.asBinder().linkToDeath(mListenerDeathRecipient,
- 0 /* flags */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to link to death");
- return;
- }
+ mListener.register(listener);
+ } else {
+ mListener.unregister();
}
- mListener = listener;
- controller.setPinnedStackAnimationListener(listener);
});
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
index 3eeba6e..0644657 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
@@ -18,8 +18,6 @@
import android.content.Context;
import android.graphics.Rect;
-import android.util.Log;
-import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -34,6 +32,7 @@
protected ViewGroup mViewRoot;
protected ViewGroup mTopEndContainer;
protected View mDragHandle;
+ protected View mEnterSplitButton;
protected View mSettingsButton;
protected View mDismissButton;
@@ -44,14 +43,13 @@
* Bind the necessary views.
*/
public void bindViews(ViewGroup viewRoot, ViewGroup topEndContainer, View dragHandle,
- View settingsButton, View dismissButton) {
+ View enterSplitButton, View settingsButton, View dismissButton) {
mViewRoot = viewRoot;
mTopEndContainer = topEndContainer;
mDragHandle = dragHandle;
+ mEnterSplitButton = enterSplitButton;
mSettingsButton = settingsButton;
mDismissButton = dismissButton;
-
- bindInitialViewState();
}
/**
@@ -72,22 +70,4 @@
v.setLayoutParams(params);
}
}
-
- /** Calculate the initial state of the menu icons. Called when the menu is first created. */
- private void bindInitialViewState() {
- if (mViewRoot == null || mTopEndContainer == null || mDragHandle == null
- || mSettingsButton == null || mDismissButton == null) {
- Log.e(TAG, "One of the required views is null.");
- return;
- }
- // The menu view layout starts out with the settings button aligned at the top|end of the
- // view group next to the dismiss button. On phones, the settings button should be aligned
- // to the top|start of the view, so move it to parent view group to then align it to the
- // top|start of the menu.
- mTopEndContainer.removeView(mSettingsButton);
- mViewRoot.addView(mSettingsButton);
-
- setLayoutGravity(mDragHandle, Gravity.START | Gravity.TOP);
- setLayoutGravity(mSettingsButton, Gravity.START | Gravity.TOP);
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 8ef2b6b..7bbebe5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -99,7 +99,7 @@
private static final float MENU_BACKGROUND_ALPHA = 0.3f;
private static final float DISABLED_ACTION_ALPHA = 0.54f;
- private static final boolean ENABLE_RESIZE_HANDLE = false;
+ private static final boolean ENABLE_ENTER_SPLIT = false;
private int mMenuState;
private boolean mAllowMenuTimeout = true;
@@ -139,7 +139,7 @@
protected View mViewRoot;
protected View mSettingsButton;
protected View mDismissButton;
- protected View mResizeHandle;
+ protected View mEnterSplitButton;
protected View mTopEndContainer;
protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
@@ -177,14 +177,23 @@
}
});
- mResizeHandle = findViewById(R.id.resize_handle);
- mResizeHandle.setAlpha(0);
+ mEnterSplitButton = findViewById(R.id.enter_split);
+ mEnterSplitButton.setAlpha(0);
+ mEnterSplitButton.setOnClickListener(v -> {
+ if (mMenuContainer.getAlpha() != 0) {
+ enterSplit();
+ }
+ });
+
+ findViewById(R.id.resize_handle).setAlpha(0);
+
mActionsGroup = findViewById(R.id.actions_group);
mBetweenActionPaddingLand = getResources().getDimensionPixelSize(
R.dimen.pip_between_action_padding_land);
mPipMenuIconsAlgorithm = new PipMenuIconsAlgorithm(mContext);
mPipMenuIconsAlgorithm.bindViews((ViewGroup) mViewRoot, (ViewGroup) mTopEndContainer,
- mResizeHandle, mSettingsButton, mDismissButton);
+ findViewById(R.id.resize_handle), mEnterSplitButton, mSettingsButton,
+ mDismissButton);
mDismissFadeOutDurationMs = context.getResources()
.getInteger(R.integer.config_pipExitAnimationDuration);
@@ -268,14 +277,13 @@
mSettingsButton.getAlpha(), 1f);
ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
mDismissButton.getAlpha(), 1f);
- ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
- mResizeHandle.getAlpha(),
- ENABLE_RESIZE_HANDLE && showResizeHandle ? 1f : 0f);
+ ObjectAnimator enterSplitAnim = ObjectAnimator.ofFloat(mEnterSplitButton, View.ALPHA,
+ mEnterSplitButton.getAlpha(), ENABLE_ENTER_SPLIT ? 1f : 0f);
if (menuState == MENU_STATE_FULL) {
mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
- resizeAnim);
+ enterSplitAnim);
} else {
- mMenuContainerAnimator.playTogether(resizeAnim);
+ mMenuContainerAnimator.playTogether(enterSplitAnim);
}
mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
mMenuContainerAnimator.setDuration(ANIMATION_HIDE_DURATION_MS);
@@ -328,7 +336,7 @@
mMenuContainer.setAlpha(0f);
mSettingsButton.setAlpha(0f);
mDismissButton.setAlpha(0f);
- mResizeHandle.setAlpha(0f);
+ mEnterSplitButton.setAlpha(0f);
}
void pokeMenu() {
@@ -368,9 +376,10 @@
mSettingsButton.getAlpha(), 0f);
ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
mDismissButton.getAlpha(), 0f);
- ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
- mResizeHandle.getAlpha(), 0f);
- mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim, resizeAnim);
+ ObjectAnimator enterSplitAnim = ObjectAnimator.ofFloat(mEnterSplitButton, View.ALPHA,
+ mEnterSplitButton.getAlpha(), 0f);
+ mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
+ enterSplitAnim);
mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
mMenuContainerAnimator.setDuration(getFadeOutDuration(animationType));
mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
@@ -522,6 +531,14 @@
}
}
+ private void enterSplit() {
+ // Do not notify menu visibility when hiding the menu, the controller will do this when it
+ // handles the message
+ hideMenu(mController::onEnterSplit, false /* notifyMenuVisibility */, true /* resize */,
+ ANIM_TYPE_HIDE);
+ }
+
+
private void showSettings() {
final Pair<ComponentName, Integer> topPipActivityInfo =
PipUtils.getTopPipActivity(mContext);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index c42750d..c634b7f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -69,6 +69,7 @@
private static final int UNSTASH_DURATION = 250;
private static final int LEAVE_PIP_DURATION = 300;
private static final int SHIFT_DURATION = 300;
+ private static final int ANIMATE_PIP_RESIZE_ANIMATION = 250;
/** Friction to use for PIP when it moves via physics fling animations. */
private static final float DEFAULT_FRICTION = 1.9f;
@@ -337,22 +338,29 @@
* Resizes the pinned stack back to unknown windowing mode, which could be freeform or
* * fullscreen depending on the display area's windowing mode.
*/
- void expandLeavePip() {
- expandLeavePip(false /* skipAnimation */);
+ void expandLeavePip(boolean skipAnimation) {
+ expandLeavePip(skipAnimation, false /* enterSplit */);
+ }
+
+ /**
+ * Resizes the pinned task to split-screen mode.
+ */
+ void expandIntoSplit() {
+ expandLeavePip(false, true /* enterSplit */);
}
/**
* Resizes the pinned stack back to unknown windowing mode, which could be freeform or
* fullscreen depending on the display area's windowing mode.
*/
- void expandLeavePip(boolean skipAnimation) {
+ private void expandLeavePip(boolean skipAnimation, boolean enterSplit) {
if (DEBUG) {
Log.d(TAG, "exitPip: skipAnimation=" + skipAnimation
+ " callers=\n" + Debug.getCallers(5, " "));
}
cancelPhysicsAnimation();
mMenuController.hideMenu(ANIM_TYPE_NONE, false /* resize */);
- mPipTaskOrganizer.exitPip(skipAnimation ? 0 : LEAVE_PIP_DURATION);
+ mPipTaskOrganizer.exitPip(skipAnimation ? 0 : LEAVE_PIP_DURATION, enterSplit);
}
/**
@@ -541,6 +549,14 @@
}
/**
+ * Animates the PiP from an old bound to a new bound. This is mostly used when display
+ * has changed and PiP bounds needs to be changed.
+ */
+ void animateResizedBounds(Rect newBounds) {
+ resizeAndAnimatePipUnchecked(newBounds, ANIMATE_PIP_RESIZE_ANIMATION);
+ }
+
+ /**
* Animates the PiP to offset it from the IME or shelf.
*/
void animateToOffset(Rect originalBounds, int offset) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 9f2f6a5..570fd5e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -139,7 +139,12 @@
@Override
public void onPipExpand() {
- mMotionHelper.expandLeavePip();
+ mMotionHelper.expandLeavePip(false /* skipAnimation */);
+ }
+
+ @Override
+ public void onEnterSplit() {
+ mMotionHelper.expandIntoSplit();
}
@Override
@@ -899,7 +904,7 @@
// Expand to fullscreen if this is a double tap
// the PiP should be frozen until the transition ends
setTouchEnabled(false);
- mMotionHelper.expandLeavePip();
+ mMotionHelper.expandLeavePip(false /* skipAnimation */);
}
} else if (mMenuState != MENU_STATE_FULL) {
if (mPipBoundsState.isStashed()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index a2e9b64..00083d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -219,7 +219,7 @@
public void movePipToFullscreen() {
if (DEBUG) Log.d(TAG, "movePipToFullscreen(), state=" + stateToName(mState));
- mPipTaskOrganizer.exitPip(mResizeAnimationDuration);
+ mPipTaskOrganizer.exitPip(mResizeAnimationDuration, false /* requestEnterSplit */);
onPipDisappeared();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
new file mode 100644
index 0000000..6e78fcb
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.wm.shell.recents;
+
+import com.android.wm.shell.recents.IRecentTasksListener;
+import com.android.wm.shell.util.GroupedRecentTaskInfo;
+
+/**
+ * Interface that is exposed to remote callers to fetch recent tasks.
+ */
+interface IRecentTasks {
+
+ /**
+ * Registers a recent tasks listener.
+ */
+ oneway void registerRecentTasksListener(in IRecentTasksListener listener) = 1;
+
+ /**
+ * Unregisters a recent tasks listener.
+ */
+ oneway void unregisterRecentTasksListener(in IRecentTasksListener listener) = 2;
+
+ /**
+ * Gets the set of recent tasks.
+ */
+ GroupedRecentTaskInfo[] getRecentTasks(int maxNum, int flags, int userId) = 3;
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
new file mode 100644
index 0000000..8efa428
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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 distshellributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.recents;
+
+/**
+ * Listener interface that Launcher attaches to SystemUI to get split-screen callbacks.
+ */
+oneway interface IRecentTasksListener {
+
+ /**
+ * Called when the set of recent tasks change.
+ */
+ void onRecentTasksChanged();
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
similarity index 61%
copy from packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java
copy to libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
index 7292b9e..a5748f6 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
@@ -14,20 +14,19 @@
* limitations under the License.
*/
-package com.android.systemui.dagger;
+package com.android.wm.shell.recents;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-
-import javax.inject.Scope;
+import com.android.wm.shell.common.annotations.ExternalThread;
/**
- * Scope annotation for singleton items within the WMComponent.
+ * Interface for interacting with the recent tasks.
*/
-@Documented
-@Retention(RUNTIME)
-@Scope
-public @interface WMSingleton {
+@ExternalThread
+public interface RecentTasks {
+ /**
+ * Returns a binder that can be passed to an external process to fetch recent tasks.
+ */
+ default IRecentTasks createExternalInterface() {
+ return null;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
new file mode 100644
index 0000000..836a6f6
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -0,0 +1,297 @@
+/*
+ * 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.wm.shell.recents;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
+import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
+
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.TaskInfo;
+import android.content.Context;
+import android.os.RemoteException;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import androidx.annotation.BinderThread;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.wm.shell.common.RemoteCallable;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SingleInstanceRemoteListener;
+import com.android.wm.shell.common.TaskStackListenerCallback;
+import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.common.annotations.ExternalThread;
+import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.util.GroupedRecentTaskInfo;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manages the recent task list from the system, caching it as necessary.
+ */
+public class RecentTasksController implements TaskStackListenerCallback,
+ RemoteCallable<RecentTasksController> {
+ private static final String TAG = RecentTasksController.class.getSimpleName();
+
+ private final Context mContext;
+ private final ShellExecutor mMainExecutor;
+ private final TaskStackListenerImpl mTaskStackListener;
+ private final RecentTasks mImpl = new RecentTasksImpl();
+
+ private final ArrayList<Runnable> mCallbacks = new ArrayList<>();
+ // Mapping of split task ids, mappings are symmetrical (ie. if t1 is the taskid of a task in a
+ // pair, then mSplitTasks[t1] = t2, and mSplitTasks[t2] = t1)
+ private final SparseIntArray mSplitTasks = new SparseIntArray();
+
+ /**
+ * Creates {@link RecentTasksController}, returns {@code null} if the feature is not
+ * supported.
+ */
+ @Nullable
+ public static RecentTasksController create(
+ Context context,
+ TaskStackListenerImpl taskStackListener,
+ @ShellMainThread ShellExecutor mainExecutor
+ ) {
+ if (!context.getResources().getBoolean(com.android.internal.R.bool.config_hasRecents)) {
+ return null;
+ }
+ return new RecentTasksController(context, taskStackListener, mainExecutor);
+ }
+
+ RecentTasksController(Context context, TaskStackListenerImpl taskStackListener,
+ ShellExecutor mainExecutor) {
+ mContext = context;
+ mTaskStackListener = taskStackListener;
+ mMainExecutor = mainExecutor;
+ }
+
+ public RecentTasks asRecentTasks() {
+ return mImpl;
+ }
+
+ public void init() {
+ mTaskStackListener.addListener(this);
+ }
+
+ /**
+ * Adds a split pair. This call does not validate the taskIds, only that they are not the same.
+ */
+ public void addSplitPair(int taskId1, int taskId2) {
+ if (taskId1 == taskId2) {
+ return;
+ }
+ // Remove any previous pairs
+ removeSplitPair(taskId1);
+ removeSplitPair(taskId2);
+ mSplitTasks.put(taskId1, taskId2);
+ mSplitTasks.put(taskId2, taskId1);
+ }
+
+ /**
+ * Removes a split pair.
+ */
+ public void removeSplitPair(int taskId) {
+ int pairedTaskId = mSplitTasks.get(taskId, INVALID_TASK_ID);
+ if (pairedTaskId != INVALID_TASK_ID) {
+ mSplitTasks.delete(taskId);
+ mSplitTasks.delete(pairedTaskId);
+ }
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ public ShellExecutor getRemoteCallExecutor() {
+ return mMainExecutor;
+ }
+
+ @Override
+ public void onTaskStackChanged() {
+ notifyRecentTasksChanged();
+ }
+
+ @Override
+ public void onRecentTaskListUpdated() {
+ // In some cases immediately after booting, the tasks in the system recent task list may be
+ // loaded, but not in the active task hierarchy in the system. These tasks are displayed in
+ // overview, but removing them don't result in a onTaskStackChanged() nor a onTaskRemoved()
+ // callback (those are for changes to the active tasks), but the task list is still updated,
+ // so we should also invalidate the change id to ensure we load a new list instead of
+ // reusing a stale list.
+ notifyRecentTasksChanged();
+ }
+
+ public void onTaskRemoved(TaskInfo taskInfo) {
+ // Remove any split pairs associated with this task
+ removeSplitPair(taskInfo.taskId);
+ notifyRecentTasksChanged();
+ }
+
+ public void onTaskWindowingModeChanged(TaskInfo taskInfo) {
+ notifyRecentTasksChanged();
+ }
+
+ @VisibleForTesting
+ void notifyRecentTasksChanged() {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).run();
+ }
+ }
+
+ private void registerRecentTasksListener(Runnable listener) {
+ if (!mCallbacks.contains(listener)) {
+ mCallbacks.add(listener);
+ }
+ }
+
+ private void unregisterRecentTasksListener(Runnable listener) {
+ mCallbacks.remove(listener);
+ }
+
+ @VisibleForTesting
+ List<ActivityManager.RecentTaskInfo> getRawRecentTasks(int maxNum, int flags, int userId) {
+ return ActivityTaskManager.getInstance().getRecentTasks(maxNum, flags, userId);
+ }
+
+ @VisibleForTesting
+ ArrayList<GroupedRecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId) {
+ // Note: the returned task list is from the most-recent to least-recent order
+ final List<ActivityManager.RecentTaskInfo> rawList = getRawRecentTasks(maxNum, flags,
+ userId);
+
+ // Make a mapping of task id -> task info
+ final SparseArray<ActivityManager.RecentTaskInfo> rawMapping = new SparseArray<>();
+ for (int i = 0; i < rawList.size(); i++) {
+ final ActivityManager.RecentTaskInfo taskInfo = rawList.get(i);
+ rawMapping.put(taskInfo.taskId, taskInfo);
+ }
+
+ // Pull out the pairs as we iterate back in the list
+ ArrayList<GroupedRecentTaskInfo> recentTasks = new ArrayList<>();
+ for (int i = 0; i < rawList.size(); i++) {
+ final ActivityManager.RecentTaskInfo taskInfo = rawList.get(i);
+ if (!rawMapping.contains(taskInfo.taskId)) {
+ // If it's not in the mapping, then it was already paired with another task
+ continue;
+ }
+
+ final int pairedTaskId = mSplitTasks.get(taskInfo.taskId);
+ if (pairedTaskId != INVALID_TASK_ID) {
+ final ActivityManager.RecentTaskInfo pairedTaskInfo = rawMapping.get(pairedTaskId);
+ rawMapping.remove(pairedTaskId);
+ recentTasks.add(new GroupedRecentTaskInfo(taskInfo, pairedTaskInfo));
+ } else {
+ recentTasks.add(new GroupedRecentTaskInfo(taskInfo));
+ }
+ }
+ return recentTasks;
+ }
+
+ public void dump(@NonNull PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+ pw.println(prefix + TAG);
+ ArrayList<GroupedRecentTaskInfo> recentTasks = getRecentTasks(Integer.MAX_VALUE,
+ ActivityManager.RECENT_IGNORE_UNAVAILABLE, ActivityManager.getCurrentUser());
+ for (int i = 0; i < recentTasks.size(); i++) {
+ pw.println(innerPrefix + recentTasks.get(i));
+ }
+ }
+
+ /**
+ * The interface for calls from outside the Shell, within the host process.
+ */
+ @ExternalThread
+ private class RecentTasksImpl implements RecentTasks {
+ private IRecentTasksImpl mIRecentTasks;
+
+ @Override
+ public IRecentTasks createExternalInterface() {
+ if (mIRecentTasks != null) {
+ mIRecentTasks.invalidate();
+ }
+ mIRecentTasks = new IRecentTasksImpl(RecentTasksController.this);
+ return mIRecentTasks;
+ }
+ }
+
+
+ /**
+ * The interface for calls from outside the host process.
+ */
+ @BinderThread
+ private static class IRecentTasksImpl extends IRecentTasks.Stub {
+ private RecentTasksController mController;
+ private final SingleInstanceRemoteListener<RecentTasksController,
+ IRecentTasksListener> mListener;
+ private final Runnable mRecentTasksListener =
+ new Runnable() {
+ @Override
+ public void run() {
+ mListener.call(l -> l.onRecentTasksChanged());
+ }
+ };
+
+ public IRecentTasksImpl(RecentTasksController controller) {
+ mController = controller;
+ mListener = new SingleInstanceRemoteListener<>(controller,
+ c -> c.registerRecentTasksListener(mRecentTasksListener),
+ c -> c.unregisterRecentTasksListener(mRecentTasksListener));
+ }
+
+ /**
+ * Invalidates this instance, preventing future calls from updating the controller.
+ */
+ void invalidate() {
+ mController = null;
+ }
+
+ @Override
+ public void registerRecentTasksListener(IRecentTasksListener listener)
+ throws RemoteException {
+ executeRemoteCallWithTaskPermission(mController, "registerRecentTasksListener",
+ (controller) -> mListener.register(listener));
+ }
+
+ @Override
+ public void unregisterRecentTasksListener(IRecentTasksListener listener)
+ throws RemoteException {
+ executeRemoteCallWithTaskPermission(mController, "unregisterRecentTasksListener",
+ (controller) -> mListener.unregister());
+ }
+
+ @Override
+ public GroupedRecentTaskInfo[] getRecentTasks(int maxNum, int flags, int userId)
+ throws RemoteException {
+ final GroupedRecentTaskInfo[][] out = new GroupedRecentTaskInfo[][]{null};
+ executeRemoteCallWithTaskPermission(mController, "getRecentTasks",
+ (controller) -> out[0] = controller.getRecentTasks(maxNum, flags, userId)
+ .toArray(new GroupedRecentTaskInfo[0]),
+ true /* blocking */);
+ return out[0];
+ }
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java
index 78af9df..ff6f913 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java
@@ -17,13 +17,10 @@
package com.android.wm.shell.sizecompatui;
import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
import android.view.View;
-import android.widget.Button;
import android.widget.FrameLayout;
+import android.widget.LinearLayout;
import androidx.annotation.Nullable;
@@ -58,10 +55,8 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- final Button gotItButton = findViewById(R.id.got_it);
- gotItButton.setBackground(new RippleDrawable(ColorStateList.valueOf(Color.LTGRAY),
- null /* content */, null /* mask */));
- gotItButton.setOnClickListener(this);
+ final LinearLayout hintPopup = findViewById(R.id.size_compat_hint_popup);
+ hintPopup.setOnClickListener(this);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
index 08a8402..d75fe517 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
@@ -17,10 +17,6 @@
package com.android.wm.shell.sizecompatui;
import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
@@ -63,11 +59,6 @@
protected void onFinishInflate() {
super.onFinishInflate();
final ImageButton restartButton = findViewById(R.id.size_compat_restart_button);
- final ColorStateList color = ColorStateList.valueOf(Color.LTGRAY);
- final GradientDrawable mask = new GradientDrawable();
- mask.setShape(GradientDrawable.OVAL);
- mask.setColor(color);
- restartButton.setBackground(new RippleDrawable(color, null /* content */, mask));
restartButton.setOnClickListener(this);
restartButton.setOnLongClickListener(this);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
index ab3cbd6..04d974a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
@@ -24,11 +24,16 @@
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -42,7 +47,7 @@
* Controls to show/update restart-activity buttons on Tasks based on whether the foreground
* activities are in size compatibility mode.
*/
-public class SizeCompatUIController implements DisplayController.OnDisplaysChangedListener,
+public class SizeCompatUIController implements OnDisplaysChangedListener,
DisplayImeController.ImePositionProcessor {
/** Callback for size compat UI interaction. */
@@ -58,6 +63,10 @@
/** Whether the IME is shown on display id. */
private final Set<Integer> mDisplaysWithIme = new ArraySet<>(1);
+ /** {@link PerDisplayOnInsetsChangedListener} by display id. */
+ private final SparseArray<PerDisplayOnInsetsChangedListener> mOnInsetsChangedListeners =
+ new SparseArray<>(0);
+
/** The showing UIs by task id. */
private final SparseArray<SizeCompatUILayout> mActiveLayouts = new SparseArray<>(0);
@@ -66,6 +75,7 @@
private final Context mContext;
private final DisplayController mDisplayController;
+ private final DisplayInsetsController mDisplayInsetsController;
private final DisplayImeController mImeController;
private final SyncTransactionQueue mSyncQueue;
@@ -76,10 +86,12 @@
public SizeCompatUIController(Context context,
DisplayController displayController,
+ DisplayInsetsController displayInsetsController,
DisplayImeController imeController,
SyncTransactionQueue syncQueue) {
mContext = context;
mDisplayController = displayController;
+ mDisplayInsetsController = displayInsetsController;
mImeController = imeController;
mSyncQueue = syncQueue;
mDisplayController.addDisplayWindowListener(this);
@@ -115,8 +127,14 @@
}
@Override
+ public void onDisplayAdded(int displayId) {
+ addOnInsetsChangedListener(displayId);
+ }
+
+ @Override
public void onDisplayRemoved(int displayId) {
mDisplayContextCache.remove(displayId);
+ removeOnInsetsChangedListener(displayId);
// Remove all size compat UIs on the removed display.
final List<Integer> toRemoveTaskIds = new ArrayList<>();
@@ -126,8 +144,29 @@
}
}
+ private void addOnInsetsChangedListener(int displayId) {
+ PerDisplayOnInsetsChangedListener listener = new PerDisplayOnInsetsChangedListener(
+ displayId);
+ listener.register();
+ mOnInsetsChangedListeners.put(displayId, listener);
+ }
+
+ private void removeOnInsetsChangedListener(int displayId) {
+ PerDisplayOnInsetsChangedListener listener = mOnInsetsChangedListeners.get(displayId);
+ if (listener == null) {
+ return;
+ }
+ listener.unregister();
+ mOnInsetsChangedListeners.remove(displayId);
+ }
+
+
@Override
public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+ updateDisplayLayout(displayId);
+ }
+
+ private void updateDisplayLayout(int displayId) {
final DisplayLayout displayLayout = mDisplayController.getDisplayLayout(displayId);
forAllLayoutsOnDisplay(displayId, layout -> layout.updateDisplayLayout(displayLayout));
}
@@ -219,4 +258,37 @@
}
}
}
+
+ /** An implementation of {@link OnInsetsChangedListener} for a given display id. */
+ private class PerDisplayOnInsetsChangedListener implements OnInsetsChangedListener {
+ final int mDisplayId;
+ final InsetsState mInsetsState = new InsetsState();
+
+ PerDisplayOnInsetsChangedListener(int displayId) {
+ mDisplayId = displayId;
+ }
+
+ void register() {
+ mDisplayInsetsController.addInsetsChangedListener(mDisplayId, this);
+ }
+
+ void unregister() {
+ mDisplayInsetsController.removeInsetsChangedListener(mDisplayId, this);
+ }
+
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
+ if (mInsetsState.equals(insetsState)) {
+ return;
+ }
+ mInsetsState.set(insetsState);
+ updateDisplayLayout(mDisplayId);
+ }
+
+ @Override
+ public void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] activeControls) {
+ insetsChanged(insetsState);
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
index 7cf9559..1a2c94f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
@@ -25,6 +25,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Binder;
@@ -54,6 +55,11 @@
private final int mTaskId;
private ShellTaskOrganizer.TaskListener mTaskListener;
private DisplayLayout mDisplayLayout;
+ private final Rect mStableBounds;
+ private final int mButtonWidth;
+ private final int mButtonHeight;
+ private final int mPopupOffsetX;
+ private final int mPopupOffsetY;
@VisibleForTesting
final SizeCompatUIWindowManager mButtonWindowManager;
@@ -66,9 +72,7 @@
@VisibleForTesting
@Nullable
SizeCompatHintPopup mHint;
- final int mButtonSize;
- final int mPopupOffsetX;
- final int mPopupOffsetY;
+ @VisibleForTesting
boolean mShouldShowHint;
SizeCompatUILayout(SyncTransactionQueue syncQueue,
@@ -86,10 +90,16 @@
mShouldShowHint = !hasShownHint;
mButtonWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this);
- mButtonSize =
- mContext.getResources().getDimensionPixelSize(R.dimen.size_compat_button_size);
- mPopupOffsetX = mButtonSize / 4;
- mPopupOffsetY = mButtonSize;
+ mStableBounds = new Rect();
+ mDisplayLayout.getStableBounds(mStableBounds);
+
+ final Resources resources = mContext.getResources();
+ mButtonWidth = resources.getDimensionPixelSize(R.dimen.size_compat_button_width);
+ mButtonHeight = resources.getDimensionPixelSize(R.dimen.size_compat_button_height);
+ mPopupOffsetX = (mButtonWidth / 2) - resources.getDimensionPixelSize(
+ R.dimen.size_compat_hint_corner_radius) - (resources.getDimensionPixelSize(
+ R.dimen.size_compat_hint_point_width) / 2);
+ mPopupOffsetY = mButtonHeight;
}
/** Creates the activity restart button window. */
@@ -167,8 +177,7 @@
if (!taskConfig.windowConfiguration.getBounds()
.equals(prevTaskConfig.windowConfiguration.getBounds())) {
// Reposition the UI surfaces.
- updateButtonSurfacePosition();
- updateHintSurfacePosition();
+ updateAllSurfacePositions();
}
if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) {
@@ -184,19 +193,14 @@
/** Called when display layout changed. */
void updateDisplayLayout(DisplayLayout displayLayout) {
- if (displayLayout == mDisplayLayout) {
- return;
- }
-
- final Rect prevStableBounds = new Rect();
+ final Rect prevStableBounds = mStableBounds;
final Rect curStableBounds = new Rect();
- mDisplayLayout.getStableBounds(prevStableBounds);
displayLayout.getStableBounds(curStableBounds);
mDisplayLayout = displayLayout;
if (!prevStableBounds.equals(curStableBounds)) {
// Stable bounds changed, update UI surface positions.
- updateButtonSurfacePosition();
- updateHintSurfacePosition();
+ updateAllSurfacePositions();
+ mStableBounds.set(curStableBounds);
}
}
@@ -222,7 +226,7 @@
WindowManager.LayoutParams getButtonWindowLayoutParams() {
final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
// Cannot be wrap_content as this determines the actual window size
- mButtonSize, mButtonSize,
+ mButtonWidth, mButtonHeight,
TYPE_APPLICATION_OVERLAY,
FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT);
@@ -262,6 +266,11 @@
createSizeCompatHint();
}
+ private void updateAllSurfacePositions() {
+ updateButtonSurfacePosition();
+ updateHintSurfacePosition();
+ }
+
@VisibleForTesting
void updateButtonSurfacePosition() {
if (mButton == null || mButtonWindowManager.getSurfaceControl() == null) {
@@ -278,12 +287,13 @@
// Position of the button in the container coordinate.
final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
? stableBounds.left - taskBounds.left
- : stableBounds.right - taskBounds.left - mButtonSize;
- final int positionY = stableBounds.bottom - taskBounds.top - mButtonSize;
+ : stableBounds.right - taskBounds.left - mButtonWidth;
+ final int positionY = stableBounds.bottom - taskBounds.top - mButtonHeight;
updateSurfacePosition(leash, positionX, positionY);
}
+ @VisibleForTesting
void updateHintSurfacePosition() {
if (mHint == null || mHintWindowManager == null
|| mHintWindowManager.getSurfaceControl() == null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 36f1406..7457be2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -34,7 +34,6 @@
import android.content.pm.LauncherApps;
import android.graphics.Rect;
import android.os.Bundle;
-import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArrayMap;
@@ -49,11 +48,11 @@
import android.window.WindowContainerTransaction;
import androidx.annotation.BinderThread;
+import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.logging.InstanceId;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -61,15 +60,19 @@
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
import com.android.wm.shell.draganddrop.DragAndDropPolicy;
+import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.transition.LegacyTransitions;
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.Executor;
@@ -87,6 +90,29 @@
RemoteCallable<SplitScreenController> {
private static final String TAG = SplitScreenController.class.getSimpleName();
+ static final int EXIT_REASON_UNKNOWN = 0;
+ static final int EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW = 1;
+ static final int EXIT_REASON_APP_FINISHED = 2;
+ static final int EXIT_REASON_DEVICE_FOLDED = 3;
+ static final int EXIT_REASON_DRAG_DIVIDER = 4;
+ static final int EXIT_REASON_RETURN_HOME = 5;
+ static final int EXIT_REASON_ROOT_TASK_VANISHED = 6;
+ static final int EXIT_REASON_SCREEN_LOCKED = 7;
+ static final int EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP = 8;
+ @IntDef(value = {
+ EXIT_REASON_UNKNOWN,
+ EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW,
+ EXIT_REASON_APP_FINISHED,
+ EXIT_REASON_DEVICE_FOLDED,
+ EXIT_REASON_DRAG_DIVIDER,
+ EXIT_REASON_RETURN_HOME,
+ EXIT_REASON_ROOT_TASK_VANISHED,
+ EXIT_REASON_SCREEN_LOCKED,
+ EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ExitReason{}
+
private final ShellTaskOrganizer mTaskOrganizer;
private final SyncTransactionQueue mSyncQueue;
private final Context mContext;
@@ -99,16 +125,31 @@
private final TransactionPool mTransactionPool;
private final SplitscreenEventLogger mLogger;
private final IconProvider mIconProvider;
+ private final Optional<RecentTasksController> mRecentTasksOptional;
private final Provider<Optional<StageTaskUnfoldController>> mUnfoldControllerProvider;
private StageCoordinator mStageCoordinator;
+ // TODO(b/205019015): Remove after we clean up downstream modules
+ public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer,
+ SyncTransactionQueue syncQueue, Context context,
+ RootTaskDisplayAreaOrganizer rootTDAOrganizer,
+ ShellExecutor mainExecutor, DisplayImeController displayImeController,
+ DisplayInsetsController displayInsetsController,
+ Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider,
+ Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
+ this(shellTaskOrganizer, syncQueue, context, rootTDAOrganizer, mainExecutor,
+ displayImeController, displayInsetsController, transitions, transactionPool,
+ iconProvider, Optional.empty(), unfoldControllerProvider);
+ }
+
public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue, Context context,
RootTaskDisplayAreaOrganizer rootTDAOrganizer,
ShellExecutor mainExecutor, DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider,
+ Optional<RecentTasksController> recentTasks,
Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
mTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
@@ -122,6 +163,7 @@
mUnfoldControllerProvider = unfoldControllerProvider;
mLogger = new SplitscreenEventLogger();
mIconProvider = iconProvider;
+ mRecentTasksOptional = recentTasks;
}
public SplitScreen asSplitScreen() {
@@ -144,7 +186,7 @@
mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController,
mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
- mIconProvider, mUnfoldControllerProvider);
+ mIconProvider, mRecentTasksOptional, mUnfoldControllerProvider);
}
}
@@ -160,11 +202,25 @@
return moveToSideStage(task, sideStagePosition);
}
+ public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition,
+ WindowContainerTransaction wct) {
+ final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
+ if (task == null) {
+ throw new IllegalArgumentException("Unknown taskId" + taskId);
+ }
+ return moveToSideStage(task, sideStagePosition, wct);
+ }
+
public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
@SplitPosition int sideStagePosition) {
return mStageCoordinator.moveToSideStage(task, sideStagePosition);
}
+ public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
+ @SplitPosition int sideStagePosition, WindowContainerTransaction wct) {
+ return mStageCoordinator.moveToSideStage(task, sideStagePosition, wct);
+ }
+
public boolean removeFromSideStage(int taskId) {
return mStageCoordinator.removeFromSideStage(taskId);
}
@@ -182,7 +238,12 @@
leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT);
}
- public void exitSplitScreen(int toTopTaskId, int exitReason) {
+ public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) {
+ moveToSideStage(taskId,
+ leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
+ }
+
+ public void exitSplitScreen(int toTopTaskId, @ExitReason int exitReason) {
mStageCoordinator.exitSplitScreen(toTopTaskId, exitReason);
}
@@ -329,6 +390,34 @@
mStageCoordinator.logOnDroppedToSplit(position, dragSessionId);
}
+ /**
+ * Return the {@param exitReason} as a string.
+ */
+ public static String exitReasonToString(int exitReason) {
+ switch (exitReason) {
+ case EXIT_REASON_UNKNOWN:
+ return "UNKNOWN_EXIT";
+ case EXIT_REASON_DRAG_DIVIDER:
+ return "DRAG_DIVIDER";
+ case EXIT_REASON_RETURN_HOME:
+ return "RETURN_HOME";
+ case EXIT_REASON_SCREEN_LOCKED:
+ return "SCREEN_LOCKED";
+ case EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP:
+ return "SCREEN_LOCKED_SHOW_ON_TOP";
+ case EXIT_REASON_DEVICE_FOLDED:
+ return "DEVICE_FOLDED";
+ case EXIT_REASON_ROOT_TASK_VANISHED:
+ return "ROOT_TASK_VANISHED";
+ case EXIT_REASON_APP_FINISHED:
+ return "APP_FINISHED";
+ case EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW:
+ return "APP_DOES_NOT_SUPPORT_MULTIWINDOW";
+ default:
+ return "unknown reason, reason int = " + exitReason;
+ }
+ }
+
public void dump(@NonNull PrintWriter pw, String prefix) {
pw.println(prefix + TAG);
if (mStageCoordinator != null) {
@@ -433,46 +522,26 @@
@BinderThread
private static class ISplitScreenImpl extends ISplitScreen.Stub {
private SplitScreenController mController;
- private ISplitScreenListener mListener;
+ private final SingleInstanceRemoteListener<SplitScreenController,
+ ISplitScreenListener> mListener;
private final SplitScreen.SplitScreenListener mSplitScreenListener =
new SplitScreen.SplitScreenListener() {
@Override
public void onStagePositionChanged(int stage, int position) {
- try {
- if (mListener != null) {
- mListener.onStagePositionChanged(stage, position);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "onStagePositionChanged", e);
- }
+ mListener.call(l -> l.onStagePositionChanged(stage, position));
}
@Override
public void onTaskStageChanged(int taskId, int stage, boolean visible) {
- try {
- if (mListener != null) {
- mListener.onTaskStageChanged(taskId, stage, visible);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "onTaskStageChanged", e);
- }
- }
- };
- private final IBinder.DeathRecipient mListenerDeathRecipient =
- new IBinder.DeathRecipient() {
- @Override
- @BinderThread
- public void binderDied() {
- final SplitScreenController controller = mController;
- controller.getRemoteCallExecutor().execute(() -> {
- mListener = null;
- controller.unregisterSplitScreenListener(mSplitScreenListener);
- });
+ mListener.call(l -> l.onTaskStageChanged(taskId, stage, visible));
}
};
public ISplitScreenImpl(SplitScreenController controller) {
mController = controller;
+ mListener = new SingleInstanceRemoteListener<>(controller,
+ c -> c.registerSplitScreenListener(mSplitScreenListener),
+ c -> c.unregisterSplitScreenListener(mSplitScreenListener));
}
/**
@@ -485,44 +554,20 @@
@Override
public void registerSplitScreenListener(ISplitScreenListener listener) {
executeRemoteCallWithTaskPermission(mController, "registerSplitScreenListener",
- (controller) -> {
- if (mListener != null) {
- mListener.asBinder().unlinkToDeath(mListenerDeathRecipient,
- 0 /* flags */);
- }
- if (listener != null) {
- try {
- listener.asBinder().linkToDeath(mListenerDeathRecipient,
- 0 /* flags */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to link to death");
- return;
- }
- }
- mListener = listener;
- controller.registerSplitScreenListener(mSplitScreenListener);
- });
+ (controller) -> mListener.register(listener));
}
@Override
public void unregisterSplitScreenListener(ISplitScreenListener listener) {
executeRemoteCallWithTaskPermission(mController, "unregisterSplitScreenListener",
- (controller) -> {
- if (mListener != null) {
- mListener.asBinder().unlinkToDeath(mListenerDeathRecipient,
- 0 /* flags */);
- }
- mListener = null;
- controller.unregisterSplitScreenListener(mSplitScreenListener);
- });
+ (controller) -> mListener.unregister());
}
@Override
public void exitSplitScreen(int toTopTaskId) {
executeRemoteCallWithTaskPermission(mController, "exitSplitScreen",
(controller) -> {
- controller.exitSplitScreen(toTopTaskId,
- FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__UNKNOWN_EXIT);
+ controller.exitSplitScreen(toTopTaskId, EXIT_REASON_UNKNOWN);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
index 319079b..e320c2a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
@@ -17,13 +17,34 @@
package com.android.wm.shell.splitscreen;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__OVERVIEW;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_DOES_NOT_SUPPORT_MULTIWINDOW;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__ROOT_TASK_VANISHED;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__UNKNOWN_EXIT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ROOT_TASK_VANISHED;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_SCREEN_LOCKED;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_UNKNOWN;
+
+import android.util.Slog;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.util.FrameworkStatsLog;
import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
+import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
/**
* Helper class that to log Drag & Drop UIEvents for a single session, see also go/uievent
@@ -96,10 +117,40 @@
}
/**
+ * Returns the framework logging constant given a splitscreen exit reason.
+ */
+ private int getLoggerExitReason(@ExitReason int exitReason) {
+ switch (exitReason) {
+ case EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW:
+ return SPLITSCREEN_UICHANGED__EXIT_REASON__APP_DOES_NOT_SUPPORT_MULTIWINDOW;
+ case EXIT_REASON_APP_FINISHED:
+ return SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED;
+ case EXIT_REASON_DEVICE_FOLDED:
+ return SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED;
+ case EXIT_REASON_DRAG_DIVIDER:
+ return SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER;
+ case EXIT_REASON_RETURN_HOME:
+ return SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
+ case EXIT_REASON_ROOT_TASK_VANISHED:
+ return SPLITSCREEN_UICHANGED__EXIT_REASON__ROOT_TASK_VANISHED;
+ case EXIT_REASON_SCREEN_LOCKED:
+ return SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED;
+ case EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP:
+ return SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP;
+ case EXIT_REASON_UNKNOWN:
+ // Fall through
+ default:
+ Slog.e("SplitscreenEventLogger", "Unknown exit reason: " + exitReason);
+ return SPLITSCREEN_UICHANGED__EXIT_REASON__UNKNOWN_EXIT;
+ }
+ }
+
+ /**
* Logs when the user exits splitscreen. Only one of the main or side stages should be
* specified to indicate which position was focused as a part of exiting (both can be unset).
*/
- public void logExit(int exitReason, @SplitPosition int mainStagePosition, int mainStageUid,
+ public void logExit(@ExitReason int exitReason,
+ @SplitPosition int mainStagePosition, int mainStageUid,
@SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape) {
if (mLoggerSessionId == null) {
// Ignore changes until we've started logging the session
@@ -113,7 +164,7 @@
FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__EXIT,
0 /* enterReason */,
- exitReason,
+ getLoggerExitReason(exitReason),
0f /* splitRatio */,
getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape),
mainStageUid,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 7be199c..3c35e6a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.splitscreen;
import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.TRANSIT_OPEN;
@@ -25,12 +26,6 @@
import static android.view.WindowManager.transitTypeToString;
import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER;
-import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_DOES_NOT_SUPPORT_MULTIWINDOW;
-import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED;
-import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED;
-import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER;
-import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
-import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
@@ -38,6 +33,13 @@
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP;
+import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
import static com.android.wm.shell.splitscreen.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP;
@@ -91,6 +93,8 @@
import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
import com.android.wm.shell.common.split.SplitWindowManager;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
@@ -145,6 +149,10 @@
private final DisplayInsetsController mDisplayInsetsController;
private final SplitScreenTransitions mSplitTransitions;
private final SplitscreenEventLogger mLogger;
+ private final Optional<RecentTasksController> mRecentTasks;
+ // Tracks whether we should update the recent tasks. Only allow this to happen in between enter
+ // and exit, since exit itself can trigger a number of changes that update the stages.
+ private boolean mShouldUpdateRecents;
private boolean mExitSplitScreenOnHide;
private boolean mKeyguardOccluded;
@@ -189,6 +197,7 @@
DisplayInsetsController displayInsetsController, Transitions transitions,
TransactionPool transactionPool, SplitscreenEventLogger logger,
IconProvider iconProvider,
+ Optional<RecentTasksController> recentTasks,
Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
mContext = context;
mDisplayId = displayId;
@@ -196,6 +205,7 @@
mRootTDAOrganizer = rootTDAOrganizer;
mTaskOrganizer = taskOrganizer;
mLogger = logger;
+ mRecentTasks = recentTasks;
mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
@@ -236,6 +246,7 @@
DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
SplitscreenEventLogger logger,
+ Optional<RecentTasksController> recentTasks,
Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
mContext = context;
mDisplayId = displayId;
@@ -253,6 +264,7 @@
mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
mLogger = logger;
+ mRecentTasks = recentTasks;
transitions.addHandler(this);
}
@@ -268,6 +280,11 @@
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
@SplitPosition int sideStagePosition) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
+ return moveToSideStage(task, sideStagePosition, wct);
+ }
+
+ boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
+ @SplitPosition int sideStagePosition, WindowContainerTransaction wct) {
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
setSideStagePosition(sideStagePosition, wct);
mSideStage.evictAllChildren(evictWct);
@@ -340,8 +357,13 @@
}
augmentedNonApps[augmentedNonApps.length - 1] = getDividerBarLegacyTarget();
try {
- ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(
- adapter.getCallingApplication());
+ try {
+ ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(
+ adapter.getCallingApplication());
+ } catch (SecurityException e) {
+ Slog.e(TAG, "Unable to boost animation thread. This should only happen"
+ + " during unit tests");
+ }
adapter.getRunner().onAnimationStart(transit, apps, wallpapers, nonApps,
finishedCallback);
} catch (RemoteException e) {
@@ -514,7 +536,7 @@
if (!showing && mMainStage.isActive()
&& mTopStageAfterFoldDismiss != STAGE_TYPE_UNDEFINED) {
exitSplitScreen(mTopStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage,
- SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED);
+ EXIT_REASON_DEVICE_FOLDED);
}
}
@@ -522,7 +544,9 @@
mExitSplitScreenOnHide = exitSplitScreenOnHide;
}
- void exitSplitScreen(int toTopTaskId, int exitReason) {
+ void exitSplitScreen(int toTopTaskId, @ExitReason int exitReason) {
+ if (!mMainStage.isActive()) return;
+
StageTaskListener childrenToTop = null;
if (mMainStage.containsTask(toTopTaskId)) {
childrenToTop = mMainStage;
@@ -537,23 +561,38 @@
applyExitSplitScreen(childrenToTop, wct, exitReason);
}
- private void exitSplitScreen(StageTaskListener childrenToTop, int exitReason) {
+ private void exitSplitScreen(StageTaskListener childrenToTop, @ExitReason int exitReason) {
+ if (!mMainStage.isActive()) return;
+
final WindowContainerTransaction wct = new WindowContainerTransaction();
applyExitSplitScreen(childrenToTop, wct, exitReason);
}
private void applyExitSplitScreen(StageTaskListener childrenToTop,
- WindowContainerTransaction wct, int exitReason) {
+ WindowContainerTransaction wct, @ExitReason int exitReason) {
+ mRecentTasks.ifPresent(recentTasks -> {
+ // Notify recents if we are exiting in a way that breaks the pair, and disable further
+ // updates to splits in the recents until we enter split again
+ if (shouldBreakPairedTaskInRecents(exitReason) && mShouldUpdateRecents) {
+ recentTasks.removeSplitPair(mMainStage.getTopVisibleChildTaskId());
+ recentTasks.removeSplitPair(mSideStage.getTopVisibleChildTaskId());
+ }
+ });
+ mShouldUpdateRecents = false;
+
mSideStage.removeAllTasks(wct, childrenToTop == mSideStage);
mMainStage.deactivate(wct, childrenToTop == mMainStage);
mTaskOrganizer.applyTransaction(wct);
mSyncQueue.runInSync(t -> t
.setWindowCrop(mMainStage.mRootLeash, null)
.setWindowCrop(mSideStage.mRootLeash, null));
+
// Hide divider and reset its position.
setDividerVisibility(false);
mSplitLayout.resetDividerPosition();
mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
+ Slog.i(TAG, "applyExitSplitScreen, reason = " + exitReasonToString(exitReason));
+ // Log the exit
if (childrenToTop != null) {
logExitToStage(exitReason, childrenToTop == mMainStage);
} else {
@@ -562,6 +601,23 @@
}
/**
+ * Returns whether the split pair in the recent tasks list should be broken.
+ */
+ private boolean shouldBreakPairedTaskInRecents(@ExitReason int exitReason) {
+ switch (exitReason) {
+ // One of the apps doesn't support MW
+ case EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW:
+ // User has explicitly dragged the divider to dismiss split
+ case EXIT_REASON_DRAG_DIVIDER:
+ // Either of the split apps have finished
+ case EXIT_REASON_APP_FINISHED:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
* Unlike exitSplitScreen, this takes a stagetype vs an actual stage-reference and populates
* an existing WindowContainerTransaction (rather than applying immediately). This is intended
* to be used when exiting split might be bundled with other window operations.
@@ -627,12 +683,28 @@
mLogger.logSideStageAppChange(getSideStagePosition(), mSideStage.getTopChildTaskUid(),
mSplitLayout.isLandscape());
}
+ updateRecentTasksSplitPair();
for (int i = mListeners.size() - 1; i >= 0; --i) {
mListeners.get(i).onTaskStageChanged(taskId, stage, visible);
}
}
+ private void updateRecentTasksSplitPair() {
+ if (!mShouldUpdateRecents) {
+ return;
+ }
+
+ mRecentTasks.ifPresent(recentTasks -> {
+ int mainStageTopTaskId = mMainStage.getTopVisibleChildTaskId();
+ int sideStageTopTaskId = mSideStage.getTopVisibleChildTaskId();
+ if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) {
+ // Update the pair for the top tasks
+ recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId);
+ }
+ });
+ }
+
private void sendSplitVisibilityChanged() {
for (int i = mListeners.size() - 1; i >= 0; --i) {
final SplitScreen.SplitScreenListener l = mListeners.get(i);
@@ -706,8 +778,9 @@
// display, like the cases keyguard showing or screen off.
|| (!mMainStage.mRootTaskInfo.isSleeping
&& !mSideStage.mRootTaskInfo.isSleeping)) {
- exitSplitScreen(null /* childrenToTop */,
- SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME);
+ // Don't dismiss staged split when both stages are not visible due to sleeping display,
+ // like the cases keyguard showing or screen off.
+ exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RETURN_HOME);
}
} else if (mKeyguardOccluded) {
// At least one of the stages is visible while keyguard occluded. Dismiss split because
@@ -715,7 +788,7 @@
// task contains show-when-locked activity remains on top after split dismissed.
final StageTaskListener toTop =
mainStageVisible ? mMainStage : (sideStageVisible ? mSideStage : null);
- exitSplitScreen(toTop, SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP);
+ exitSplitScreen(toTop, EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP);
}
mSyncQueue.runInSync(t -> {
@@ -752,10 +825,10 @@
if (!hasChildren) {
if (isSideStage && mMainStageListener.mVisible) {
// Exit to main stage if side stage no longer has children.
- exitSplitScreen(mMainStage, SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED);
+ exitSplitScreen(mMainStage, EXIT_REASON_APP_FINISHED);
} else if (!isSideStage && mSideStageListener.mVisible) {
// Exit to side stage if main stage no longer has children.
- exitSplitScreen(mSideStage, SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED);
+ exitSplitScreen(mSideStage, EXIT_REASON_APP_FINISHED);
}
} else if (isSideStage) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -765,12 +838,16 @@
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> updateSurfaceBounds(mSplitLayout, t));
}
- if (!mLogger.hasStartedSession() && mMainStageListener.mHasChildren
- && mSideStageListener.mHasChildren) {
- mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
- getMainStagePosition(), mMainStage.getTopChildTaskUid(),
- getSideStagePosition(), mSideStage.getTopChildTaskUid(),
- mSplitLayout.isLandscape());
+ if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) {
+ mShouldUpdateRecents = true;
+ updateRecentTasksSplitPair();
+
+ if (!mLogger.hasStartedSession()) {
+ mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
+ getMainStagePosition(), mMainStage.getTopChildTaskUid(),
+ getSideStagePosition(), mSideStage.getTopChildTaskUid(),
+ mSplitLayout.isLandscape());
+ }
}
}
@@ -790,8 +867,7 @@
onSnappedToDismissTransition(mainStageToTop);
return;
}
- exitSplitScreen(mainStageToTop ? mMainStage : mSideStage,
- SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER);
+ exitSplitScreen(mainStageToTop ? mMainStage : mSideStage, EXIT_REASON_DRAG_DIVIDER);
}
@Override
@@ -1249,7 +1325,7 @@
/**
* Logs the exit of splitscreen.
*/
- private void logExit(int exitReason) {
+ private void logExit(@ExitReason int exitReason) {
mLogger.logExit(exitReason,
SPLIT_POSITION_UNDEFINED, 0 /* mainStageUid */,
SPLIT_POSITION_UNDEFINED, 0 /* sideStageUid */,
@@ -1260,7 +1336,7 @@
* Logs the exit of splitscreen to a specific stage. This must be called before the exit is
* executed.
*/
- private void logExitToStage(int exitReason, boolean toMainStage) {
+ private void logExitToStage(@ExitReason int exitReason, boolean toMainStage) {
mLogger.logExit(exitReason,
toMainStage ? getMainStagePosition() : SPLIT_POSITION_UNDEFINED,
toMainStage ? mMainStage.getTopChildTaskUid() : 0 /* mainStageUid */,
@@ -1309,7 +1385,7 @@
public void onNoLongerSupportMultiWindow() {
if (mMainStage.isActive()) {
StageCoordinator.this.exitSplitScreen(null /* childrenToTop */,
- SPLITSCREEN_UICHANGED__EXIT_REASON__APP_DOES_NOT_SUPPORT_MULTIWINDOW);
+ EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 5100c56..190006e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.splitscreen;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
@@ -113,6 +114,19 @@
}
/**
+ * Returns the top visible child task's id.
+ */
+ int getTopVisibleChildTaskId() {
+ for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
+ final ActivityManager.RunningTaskInfo info = mChildrenTaskInfo.valueAt(i);
+ if (info.isVisible) {
+ return info.taskId;
+ }
+ }
+ return INVALID_TASK_ID;
+ }
+
+ /**
* Returns the top activity uid for the top child task.
*/
int getTopChildTaskUid() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
index 4e477ca1..003d8a3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
@@ -18,6 +18,8 @@
import static android.view.Choreographer.CALLBACK_COMMIT;
import static android.view.View.GONE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLASHSCREEN_EXIT_ANIM;
+
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
@@ -42,6 +44,7 @@
import android.view.animation.PathInterpolator;
import android.window.SplashScreenView;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.TransactionPool;
@@ -311,17 +314,19 @@
@Override
public void onAnimationStart(Animator animation) {
- // ignore
+ InteractionJankMonitor.getInstance().begin(mSplashScreenView, CUJ_SPLASHSCREEN_EXIT_ANIM);
}
@Override
public void onAnimationEnd(Animator animation) {
reset();
+ InteractionJankMonitor.getInstance().end(CUJ_SPLASHSCREEN_EXIT_ANIM);
}
@Override
public void onAnimationCancel(Animator animation) {
reset();
+ InteractionJankMonitor.getInstance().cancel(CUJ_SPLASHSCREEN_EXIT_ANIM);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
index e2a72bd..709e221 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
@@ -19,6 +19,7 @@
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.ColorInt;
import android.annotation.NonNull;
@@ -263,11 +264,12 @@
* A lightweight AdaptiveIconDrawable which support foreground to be Animatable, and keep this
* drawable masked by config_icon_mask.
*/
- private static class AnimatableIconAnimateListener extends AdaptiveForegroundDrawable
+ public static class AnimatableIconAnimateListener extends AdaptiveForegroundDrawable
implements SplashScreenView.IconAnimateListener {
private Animatable mAnimatableIcon;
private Animator mIconAnimator;
private boolean mAnimationTriggered;
+ private AnimatorListenerAdapter mJankMonitoringListener;
AnimatableIconAnimateListener(@NonNull Drawable foregroundDrawable) {
super(foregroundDrawable);
@@ -275,6 +277,11 @@
}
@Override
+ public void setAnimationJankMonitoring(AnimatorListenerAdapter listener) {
+ mJankMonitoringListener = listener;
+ }
+
+ @Override
public boolean prepareAnimate(long duration, Runnable startListener) {
mAnimatableIcon = (Animatable) mForegroundDrawable;
mIconAnimator = ValueAnimator.ofInt(0, 1);
@@ -286,6 +293,9 @@
startListener.run();
}
try {
+ if (mJankMonitoringListener != null) {
+ mJankMonitoringListener.onAnimationStart(animation);
+ }
mAnimatableIcon.start();
} catch (Exception ex) {
Log.e(TAG, "Error while running the splash screen animated icon", ex);
@@ -296,11 +306,17 @@
@Override
public void onAnimationEnd(Animator animation) {
mAnimatableIcon.stop();
+ if (mJankMonitoringListener != null) {
+ mJankMonitoringListener.onAnimationEnd(animation);
+ }
}
@Override
public void onAnimationCancel(Animator animation) {
mAnimatableIcon.stop();
+ if (mJankMonitoringListener != null) {
+ mJankMonitoringListener.onAnimationCancel(animation);
+ }
}
@Override
@@ -316,6 +332,7 @@
public void stopAnimation() {
if (mIconAnimator != null && mIconAnimator.isRunning()) {
mIconAnimator.end();
+ mJankMonitoringListener = null;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
index a86e07a..e98a3e8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
@@ -46,6 +46,7 @@
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.common.TransactionPool;
/**
@@ -237,24 +238,19 @@
@BinderThread
private static class IStartingWindowImpl extends IStartingWindow.Stub {
private StartingWindowController mController;
- private IStartingWindowListener mListener;
+ private SingleInstanceRemoteListener<StartingWindowController,
+ IStartingWindowListener> mListener;
private final TriConsumer<Integer, Integer, Integer> mStartingWindowListener =
- this::notifyIStartingWindowListener;
- private final IBinder.DeathRecipient mListenerDeathRecipient =
- new IBinder.DeathRecipient() {
- @Override
- @BinderThread
- public void binderDied() {
- final StartingWindowController controller = mController;
- controller.getRemoteCallExecutor().execute(() -> {
- mListener = null;
- controller.setStartingWindowListener(null);
- });
- }
+ (taskId, supportedType, startingWindowBackgroundColor) -> {
+ mListener.call(l -> l.onTaskLaunching(taskId, supportedType,
+ startingWindowBackgroundColor));
};
public IStartingWindowImpl(StartingWindowController controller) {
mController = controller;
+ mListener = new SingleInstanceRemoteListener<>(controller,
+ c -> c.setStartingWindowListener(mStartingWindowListener),
+ c -> c.setStartingWindowListener(null));
}
/**
@@ -268,36 +264,12 @@
public void setStartingWindowListener(IStartingWindowListener listener) {
executeRemoteCallWithTaskPermission(mController, "setStartingWindowListener",
(controller) -> {
- if (mListener != null) {
- // Reset the old death recipient
- mListener.asBinder().unlinkToDeath(mListenerDeathRecipient,
- 0 /* flags */);
- }
if (listener != null) {
- try {
- listener.asBinder().linkToDeath(mListenerDeathRecipient,
- 0 /* flags */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to link to death");
- return;
- }
+ mListener.register(listener);
+ } else {
+ mListener.unregister();
}
- mListener = listener;
- controller.setStartingWindowListener(mStartingWindowListener);
});
}
-
- private void notifyIStartingWindowListener(int taskId, int supportedType,
- int startingWindowBackgroundColor) {
- if (mListener == null) {
- return;
- }
-
- try {
- mListener.onTaskLaunching(taskId, supportedType, startingWindowBackgroundColor);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to notify task launching", e);
- }
- }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index 3be896e..3e2a0e6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -18,9 +18,11 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Slog;
import android.view.SurfaceControl;
import android.window.IRemoteTransition;
import android.window.IRemoteTransitionFinishedCallback;
@@ -89,6 +91,13 @@
if (mRemote.asBinder() != null) {
mRemote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
}
+ try {
+ ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(
+ mRemote.getAppThread());
+ } catch (SecurityException e) {
+ Slog.e(Transitions.TAG, "Unable to boost animation thread. This should only happen"
+ + " during unit tests");
+ }
mRemote.getRemoteTransition().startAnimation(transition, info, startTransaction, cb);
} catch (RemoteException e) {
Log.e(Transitions.TAG, "Error running remote transition.", e);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index c798ace..ece9f47 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
@@ -130,6 +131,13 @@
};
try {
handleDeath(remote.asBinder(), finishCallback);
+ try {
+ ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(
+ remote.getAppThread());
+ } catch (SecurityException e) {
+ Log.e(Transitions.TAG, "Unable to boost animation thread. This should only happen"
+ + " during unit tests");
+ }
remote.getRemoteTransition().startAnimation(transition, info, startTransaction, cb);
} catch (RemoteException e) {
Log.e(Transitions.TAG, "Error running remote transition.", e);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ShellTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ShellTransitions.java
index 802d25f..b34049d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ShellTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ShellTransitions.java
@@ -38,11 +38,11 @@
/**
* Registers a remote transition.
*/
- void registerRemote(@NonNull TransitionFilter filter,
- @NonNull RemoteTransition remoteTransition);
+ default void registerRemote(@NonNull TransitionFilter filter,
+ @NonNull RemoteTransition remoteTransition) {}
/**
* Unregisters a remote transition.
*/
- void unregisterRemote(@NonNull RemoteTransition remoteTransition);
+ default void unregisterRemote(@NonNull RemoteTransition remoteTransition) {}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index c369831..804e449 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -171,24 +171,6 @@
}
}
- /** Create an empty/non-registering transitions object for system-ui tests. */
- @VisibleForTesting
- public static ShellTransitions createEmptyForTesting() {
- return new ShellTransitions() {
- @Override
- public void registerRemote(@androidx.annotation.NonNull TransitionFilter filter,
- @androidx.annotation.NonNull RemoteTransition remoteTransition) {
- // Do nothing
- }
-
- @Override
- public void unregisterRemote(
- @androidx.annotation.NonNull RemoteTransition remoteTransition) {
- // Do nothing
- }
- };
- }
-
/** Register this transition handler with Core */
public void register(ShellTaskOrganizer taskOrganizer) {
if (mPlayerImpl == null) return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.aidl
new file mode 100644
index 0000000..15797cd
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.wm.shell.util;
+
+parcelable GroupedRecentTaskInfo;
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
new file mode 100644
index 0000000..0331ba1
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.util;
+
+import android.app.ActivityManager;
+import android.app.WindowConfiguration;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Simple container for recent tasks. May contain either a single or pair of tasks.
+ */
+public class GroupedRecentTaskInfo implements Parcelable {
+ public @NonNull ActivityManager.RecentTaskInfo mTaskInfo1;
+ public @Nullable ActivityManager.RecentTaskInfo mTaskInfo2;
+
+ public GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo task1) {
+ this(task1, null);
+ }
+
+ public GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo task1,
+ @Nullable ActivityManager.RecentTaskInfo task2) {
+ mTaskInfo1 = task1;
+ mTaskInfo2 = task2;
+ }
+
+ GroupedRecentTaskInfo(Parcel parcel) {
+ mTaskInfo1 = parcel.readTypedObject(ActivityManager.RecentTaskInfo.CREATOR);
+ mTaskInfo2 = parcel.readTypedObject(ActivityManager.RecentTaskInfo.CREATOR);
+ }
+
+ @Override
+ public String toString() {
+ return "Task1: " + getTaskInfo(mTaskInfo1) + ", Task2: " + getTaskInfo(mTaskInfo2);
+ }
+
+ private String getTaskInfo(ActivityManager.RecentTaskInfo taskInfo) {
+ if (taskInfo == null) {
+ return null;
+ }
+ return "id=" + taskInfo.taskId
+ + " baseIntent=" + (taskInfo.baseIntent != null
+ ? taskInfo.baseIntent.getComponent()
+ : "null")
+ + " winMode=" + WindowConfiguration.windowingModeToString(
+ taskInfo.getWindowingMode());
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeTypedObject(mTaskInfo1, flags);
+ parcel.writeTypedObject(mTaskInfo2, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @android.annotation.NonNull Creator<GroupedRecentTaskInfo> CREATOR =
+ new Creator<GroupedRecentTaskInfo>() {
+ public GroupedRecentTaskInfo createFromParcel(Parcel source) {
+ return new GroupedRecentTaskInfo(source);
+ }
+ public GroupedRecentTaskInfo[] newArray(int size) {
+ return new GroupedRecentTaskInfo[size];
+ }
+ };
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt
index 9c172a2..bbdf997 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt
@@ -48,9 +48,11 @@
eachRun {
val addBubbleBtn = waitAndGetAddBubbleBtn()
addBubbleBtn?.click() ?: error("Bubble widget not found")
+ device.sleep()
wmHelper.waitFor("noAppWindowsOnTop") {
it.wmState.topVisibleAppWindow.isEmpty()
}
+ device.wakeUp()
}
}
transitions {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index d5acbbcf..1fcbf14 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -65,6 +65,7 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Optional;
/**
* Tests for the shell task organizer.
@@ -131,7 +132,7 @@
.when(mTaskOrganizerController).registerTaskOrganizer(any());
} catch (RemoteException e) {}
mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mTestExecutor, mContext,
- mSizeCompatUI));
+ mSizeCompatUI, Optional.empty()));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java
index 5bdf831..6080f3a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java
@@ -26,6 +26,7 @@
import org.junit.After;
import org.junit.Before;
+import org.mockito.MockitoAnnotations;
/**
* Base class that does shell test case setup.
@@ -36,6 +37,7 @@
@Before
public void shellSetup() {
+ MockitoAnnotations.initMocks(this);
final Context context =
InstrumentationRegistry.getInstrumentation().getTargetContext();
final DisplayManager dm = context.getSystemService(DisplayManager.class);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java
index d6f7e54..46fe201 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.inOrder;
@@ -30,11 +31,13 @@
import android.app.WindowConfiguration;
import android.content.res.Configuration;
import android.graphics.Point;
+import android.os.SystemProperties;
import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.recents.RecentTasksController;
import org.junit.Before;
import org.junit.Test;
@@ -46,12 +49,16 @@
@SmallTest
public class FullscreenTaskListenerTest {
+ private static final boolean ENABLE_SHELL_TRANSITIONS =
+ SystemProperties.getBoolean("persist.debug.shell_transit", false);
@Mock
private SyncTransactionQueue mSyncQueue;
@Mock
private FullscreenUnfoldController mUnfoldController;
@Mock
+ private RecentTasksController mRecentTasksController;
+ @Mock
private SurfaceControl mSurfaceControl;
private Optional<FullscreenUnfoldController> mFullscreenUnfoldController;
@@ -62,11 +69,13 @@
public void setup() {
MockitoAnnotations.initMocks(this);
mFullscreenUnfoldController = Optional.of(mUnfoldController);
- mListener = new FullscreenTaskListener(mSyncQueue, mFullscreenUnfoldController);
+ mListener = new FullscreenTaskListener(mSyncQueue, mFullscreenUnfoldController,
+ Optional.empty());
}
@Test
public void testAnimatableTaskAppeared_notifiesUnfoldController() {
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
RunningTaskInfo info = createTaskInfo(/* visible */ true, /* taskId */ 0);
mListener.onTaskAppeared(info, mSurfaceControl);
@@ -76,6 +85,7 @@
@Test
public void testMultipleAnimatableTasksAppeared_notifiesUnfoldController() {
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
RunningTaskInfo animatable1 = createTaskInfo(/* visible */ true, /* taskId */ 0);
RunningTaskInfo animatable2 = createTaskInfo(/* visible */ true, /* taskId */ 1);
@@ -89,6 +99,7 @@
@Test
public void testNonAnimatableTaskAppeared_doesNotNotifyUnfoldController() {
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
RunningTaskInfo info = createTaskInfo(/* visible */ false, /* taskId */ 0);
mListener.onTaskAppeared(info, mSurfaceControl);
@@ -98,6 +109,7 @@
@Test
public void testNonAnimatableTaskChanged_doesNotNotifyUnfoldController() {
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
RunningTaskInfo info = createTaskInfo(/* visible */ false, /* taskId */ 0);
mListener.onTaskAppeared(info, mSurfaceControl);
@@ -108,6 +120,7 @@
@Test
public void testNonAnimatableTaskVanished_doesNotNotifyUnfoldController() {
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
RunningTaskInfo info = createTaskInfo(/* visible */ false, /* taskId */ 0);
mListener.onTaskAppeared(info, mSurfaceControl);
@@ -118,6 +131,7 @@
@Test
public void testAnimatableTaskBecameInactive_notifiesUnfoldController() {
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
RunningTaskInfo animatableTask = createTaskInfo(/* visible */ true, /* taskId */ 0);
mListener.onTaskAppeared(animatableTask, mSurfaceControl);
RunningTaskInfo notAnimatableTask = createTaskInfo(/* visible */ false, /* taskId */ 0);
@@ -129,6 +143,7 @@
@Test
public void testAnimatableTaskVanished_notifiesUnfoldController() {
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
RunningTaskInfo taskInfo = createTaskInfo(/* visible */ true, /* taskId */ 0);
mListener.onTaskAppeared(taskInfo, mSurfaceControl);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 0270093..0172cf32 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -50,6 +50,7 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import org.junit.Before;
import org.junit.Test;
@@ -75,7 +76,8 @@
@Mock private PipTransitionController mMockPipTransitionController;
@Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
@Mock private PipUiEventLogger mMockPipUiEventLogger;
- @Mock private Optional<LegacySplitScreenController> mMockOptionalSplitScreen;
+ @Mock private Optional<LegacySplitScreenController> mMockOptionalLegacySplitScreen;
+ @Mock private Optional<SplitScreenController> mMockOptionalSplitScreen;
@Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
private TestShellExecutor mMainExecutor;
private PipBoundsState mPipBoundsState;
@@ -99,8 +101,9 @@
mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
mPipBoundsAlgorithm, mMockPhonePipMenuController,
mMockPipAnimationController, mMockPipSurfaceTransactionHelper,
- mMockPipTransitionController, mMockOptionalSplitScreen, mMockDisplayController,
- mMockPipUiEventLogger, mMockShellTaskOrganizer, mMainExecutor));
+ mMockPipTransitionController, mMockOptionalLegacySplitScreen,
+ mMockOptionalSplitScreen, mMockDisplayController, mMockPipUiEventLogger,
+ mMockShellTaskOrganizer, mMainExecutor));
mMainExecutor.flushAll();
preparePipTaskOrg();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 935f669..c2f58b8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -191,7 +191,7 @@
mPipController.mDisplaysChangedListener.onDisplayConfigurationChanged(
displayId, new Configuration());
- verify(mMockPipMotionHelper).movePip(any(Rect.class));
+ verify(mMockPipMotionHelper).animateResizedBounds(any(Rect.class));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
new file mode 100644
index 0000000..a1e1231
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -0,0 +1,213 @@
+/*
+ * 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.wm.shell.recents;
+
+import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import static java.lang.Integer.MAX_VALUE;
+
+import android.app.ActivityManager;
+import android.app.WindowConfiguration;
+import android.content.Context;
+import android.view.SurfaceControl;
+import android.window.TaskAppearedInfo;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.util.GroupedRecentTaskInfo;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Optional;
+
+/**
+ * Tests for {@link RecentTasksController}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class RecentTasksControllerTest extends ShellTestCase {
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private TaskStackListenerImpl mTaskStackListener;
+
+ private ShellTaskOrganizer mShellTaskOrganizer;
+ private RecentTasksController mRecentTasksController;
+ private ShellExecutor mMainExecutor;
+
+ @Before
+ public void setUp() {
+ mMainExecutor = new TestShellExecutor();
+ mRecentTasksController = spy(new RecentTasksController(mContext, mTaskStackListener,
+ mMainExecutor));
+ mShellTaskOrganizer = new ShellTaskOrganizer(mMainExecutor, mContext,
+ null /* sizeCompatUI */, Optional.of(mRecentTasksController));
+ }
+
+ @Test
+ public void testGetRecentTasks() {
+ ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
+ ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+ ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
+ setRawList(t1, t2, t3);
+
+ ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
+ MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
+ assertGroupedTasksListEquals(recentTasks,
+ t1.taskId, -1,
+ t2.taskId, -1,
+ t3.taskId, -1);
+ }
+
+ @Test
+ public void testGetRecentTasks_withPairs() {
+ ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
+ ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+ ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
+ ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
+ ActivityManager.RecentTaskInfo t5 = makeTaskInfo(5);
+ ActivityManager.RecentTaskInfo t6 = makeTaskInfo(6);
+ setRawList(t1, t2, t3, t4, t5, t6);
+
+ // Mark a couple pairs [t2, t4], [t3, t5]
+ mRecentTasksController.addSplitPair(t2.taskId, t4.taskId);
+ mRecentTasksController.addSplitPair(t3.taskId, t5.taskId);
+
+ ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
+ MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
+ assertGroupedTasksListEquals(recentTasks,
+ t1.taskId, -1,
+ t2.taskId, t4.taskId,
+ t3.taskId, t5.taskId,
+ t6.taskId, -1);
+ }
+
+ @Test
+ public void testRemovedTaskRemovesSplit() {
+ ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
+ ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+ ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
+ setRawList(t1, t2, t3);
+
+ // Add a pair
+ mRecentTasksController.addSplitPair(t2.taskId, t3.taskId);
+ reset(mRecentTasksController);
+
+ // Remove one of the tasks and ensure the pair is removed
+ SurfaceControl mockLeash = mock(SurfaceControl.class);
+ ActivityManager.RunningTaskInfo rt2 = makeRunningTaskInfo(2);
+ mShellTaskOrganizer.onTaskAppeared(rt2, mockLeash);
+ mShellTaskOrganizer.onTaskVanished(rt2);
+
+ verify(mRecentTasksController).removeSplitPair(t2.taskId);
+ }
+
+ @Test
+ public void testTaskWindowingModeChangedNotifiesChange() {
+ ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
+ setRawList(t1);
+
+ // Remove one of the tasks and ensure the pair is removed
+ SurfaceControl mockLeash = mock(SurfaceControl.class);
+ ActivityManager.RunningTaskInfo rt2Fullscreen = makeRunningTaskInfo(2);
+ rt2Fullscreen.configuration.windowConfiguration.setWindowingMode(
+ WINDOWING_MODE_FULLSCREEN);
+ mShellTaskOrganizer.onTaskAppeared(rt2Fullscreen, mockLeash);
+
+ // Change the windowing mode and ensure the recent tasks change is notified
+ ActivityManager.RunningTaskInfo rt2MultiWIndow = makeRunningTaskInfo(2);
+ rt2MultiWIndow.configuration.windowConfiguration.setWindowingMode(
+ WINDOWING_MODE_MULTI_WINDOW);
+ mShellTaskOrganizer.onTaskInfoChanged(rt2MultiWIndow);
+
+ verify(mRecentTasksController).notifyRecentTasksChanged();
+ }
+
+ /**
+ * Helper to create a task with a given task id.
+ */
+ private ActivityManager.RecentTaskInfo makeTaskInfo(int taskId) {
+ ActivityManager.RecentTaskInfo info = new ActivityManager.RecentTaskInfo();
+ info.taskId = taskId;
+ return info;
+ }
+
+ /**
+ * Helper to create a running task with a given task id.
+ */
+ private ActivityManager.RunningTaskInfo makeRunningTaskInfo(int taskId) {
+ ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
+ info.taskId = taskId;
+ return info;
+ }
+
+ /**
+ * Helper to set the raw task list on the controller.
+ */
+ private ArrayList<ActivityManager.RecentTaskInfo> setRawList(
+ ActivityManager.RecentTaskInfo... tasks) {
+ ArrayList<ActivityManager.RecentTaskInfo> rawList = new ArrayList<>();
+ for (ActivityManager.RecentTaskInfo task : tasks) {
+ rawList.add(task);
+ }
+ doReturn(rawList).when(mRecentTasksController).getRawRecentTasks(anyInt(), anyInt(),
+ anyInt());
+ return rawList;
+ }
+
+ /**
+ * Asserts that the recent tasks matches the given task ids.
+ * @param expectedTaskIds list of task ids that map to the flattened task ids of the tasks in
+ * the grouped task list
+ */
+ private void assertGroupedTasksListEquals(ArrayList<GroupedRecentTaskInfo> recentTasks,
+ int... expectedTaskIds) {
+ int[] flattenedTaskIds = new int[recentTasks.size() * 2];
+ for (int i = 0; i < recentTasks.size(); i++) {
+ GroupedRecentTaskInfo pair = recentTasks.get(i);
+ flattenedTaskIds[2 * i] = pair.mTaskInfo1.taskId;
+ flattenedTaskIds[2 * i + 1] = pair.mTaskInfo2 != null
+ ? pair.mTaskInfo2.taskId
+ : -1;
+ }
+ assertTrue("Expected: " + Arrays.toString(expectedTaskIds)
+ + " Received: " + Arrays.toString(flattenedTaskIds),
+ Arrays.equals(flattenedTaskIds, expectedTaskIds));
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java
index 10fd7d7..3a14a33 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java
@@ -24,7 +24,7 @@
import android.content.res.Configuration;
import android.testing.AndroidTestingRunner;
import android.view.LayoutInflater;
-import android.widget.Button;
+import android.widget.LinearLayout;
import androidx.test.filters.SmallTest;
@@ -77,8 +77,8 @@
public void testOnClick() {
doNothing().when(mLayout).dismissHint();
- final Button button = mHint.findViewById(R.id.got_it);
- button.performClick();
+ final LinearLayout hintPopup = mHint.findViewById(R.id.size_compat_hint_popup);
+ hintPopup.performClick();
verify(mLayout).dismissHint();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
index 8839f58..d12cf5c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
@@ -16,11 +16,14 @@
package com.android.wm.shell.sizecompatui;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -28,6 +31,8 @@
import android.content.Context;
import android.content.res.Configuration;
import android.testing.AndroidTestingRunner;
+import android.view.InsetsSource;
+import android.view.InsetsState;
import androidx.test.filters.SmallTest;
@@ -35,12 +40,16 @@
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -58,12 +67,16 @@
private SizeCompatUIController mController;
private @Mock DisplayController mMockDisplayController;
+ private @Mock DisplayInsetsController mMockDisplayInsetsController;
private @Mock DisplayLayout mMockDisplayLayout;
private @Mock DisplayImeController mMockImeController;
private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener;
private @Mock SyncTransactionQueue mMockSyncQueue;
private @Mock SizeCompatUILayout mMockLayout;
+ @Captor
+ ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -72,7 +85,7 @@
doReturn(DISPLAY_ID).when(mMockLayout).getDisplayId();
doReturn(TASK_ID).when(mMockLayout).getTaskId();
mController = new SizeCompatUIController(mContext, mMockDisplayController,
- mMockImeController, mMockSyncQueue) {
+ mMockDisplayInsetsController, mMockImeController, mMockSyncQueue) {
@Override
SizeCompatUILayout createLayout(Context context, int displayId, int taskId,
Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) {
@@ -114,7 +127,17 @@
}
@Test
+ public void testOnDisplayAdded() {
+ mController.onDisplayAdded(DISPLAY_ID);
+ mController.onDisplayAdded(DISPLAY_ID + 1);
+
+ verify(mMockDisplayInsetsController).addInsetsChangedListener(eq(DISPLAY_ID), any());
+ verify(mMockDisplayInsetsController).addInsetsChangedListener(eq(DISPLAY_ID + 1), any());
+ }
+
+ @Test
public void testOnDisplayRemoved() {
+ mController.onDisplayAdded(DISPLAY_ID);
final Configuration taskConfig = new Configuration();
mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
mMockTaskListener);
@@ -122,9 +145,12 @@
mController.onDisplayRemoved(DISPLAY_ID + 1);
verify(mMockLayout, never()).release();
+ verify(mMockDisplayInsetsController, never()).removeInsetsChangedListener(eq(DISPLAY_ID),
+ any());
mController.onDisplayRemoved(DISPLAY_ID);
+ verify(mMockDisplayInsetsController).removeInsetsChangedListener(eq(DISPLAY_ID), any());
verify(mMockLayout).release();
}
@@ -145,6 +171,29 @@
}
@Test
+ public void testInsetsChanged() {
+ mController.onDisplayAdded(DISPLAY_ID);
+ final Configuration taskConfig = new Configuration();
+ mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
+ mMockTaskListener);
+ InsetsState insetsState = new InsetsState();
+ InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
+ insetsSource.setFrame(0, 0, 1000, 1000);
+ insetsState.addSource(insetsSource);
+
+ verify(mMockDisplayInsetsController).addInsetsChangedListener(eq(DISPLAY_ID),
+ mOnInsetsChangedListenerCaptor.capture());
+ mOnInsetsChangedListenerCaptor.getValue().insetsChanged(insetsState);
+
+ verify(mMockLayout).updateDisplayLayout(mMockDisplayLayout);
+
+ // No update if the insets state is the same.
+ clearInvocations(mMockLayout);
+ mOnInsetsChangedListenerCaptor.getValue().insetsChanged(new InsetsState(insetsState));
+ verify(mMockLayout, never()).updateDisplayLayout(mMockDisplayLayout);
+ }
+
+ @Test
public void testChangeButtonVisibilityOnImeShowHide() {
final Configuration taskConfig = new Configuration();
mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java
index ee4c815..2ba603c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java
@@ -16,6 +16,8 @@
package com.android.wm.shell.sizecompatui;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.junit.Assert.assertFalse;
@@ -33,6 +35,8 @@
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.view.DisplayInfo;
+import android.view.InsetsSource;
+import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.View;
@@ -77,7 +81,7 @@
mTaskConfig = new Configuration();
mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext,
- new Configuration(), TASK_ID, mTaskListener, mDisplayLayout,
+ new Configuration(), TASK_ID, mTaskListener, new DisplayLayout(),
false /* hasShownHint */);
spyOn(mLayout);
@@ -176,7 +180,7 @@
displayInfo.logicalWidth = 1000;
displayInfo.logicalHeight = 2000;
final DisplayLayout displayLayout1 = new DisplayLayout(displayInfo,
- mContext.getResources(), false, false);
+ mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
mLayout.updateDisplayLayout(displayLayout1);
verify(mLayout).updateButtonSurfacePosition();
@@ -185,13 +189,37 @@
// No update if the display bounds is the same.
clearInvocations(mLayout);
final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo,
- mContext.getResources(), false, false);
+ mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
mLayout.updateDisplayLayout(displayLayout2);
verify(mLayout, never()).updateButtonSurfacePosition();
verify(mLayout, never()).updateHintSurfacePosition();
}
@Test
+ public void testUpdateDisplayLayoutInsets() {
+ final DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.logicalWidth = 1000;
+ displayInfo.logicalHeight = 2000;
+ final DisplayLayout displayLayout = new DisplayLayout(displayInfo,
+ mContext.getResources(), /* hasNavigationBar= */ true, /* hasStatusBar= */ false);
+
+ mLayout.updateDisplayLayout(displayLayout);
+ verify(mLayout).updateButtonSurfacePosition();
+ verify(mLayout).updateHintSurfacePosition();
+
+ // Update if the insets change on the existing display layout
+ clearInvocations(mLayout);
+ InsetsState insetsState = new InsetsState();
+ InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
+ insetsSource.setFrame(0, 0, 1000, 1000);
+ insetsState.addSource(insetsSource);
+ displayLayout.setInsets(mContext.getResources(), insetsState);
+ mLayout.updateDisplayLayout(displayLayout);
+ verify(mLayout).updateButtonSurfacePosition();
+ verify(mLayout).updateHintSurfacePosition();
+ }
+
+ @Test
public void testUpdateImeVisibility() {
// Create button if it is not created.
mLayout.mButton = null;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index f90af23..aab1e3a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -36,6 +36,7 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
+import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -72,10 +73,11 @@
DisplayInsetsController insetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
SplitscreenEventLogger logger,
+ Optional<RecentTasksController> recentTasks,
Provider<Optional<StageTaskUnfoldController>> unfoldController) {
super(context, displayId, syncQueue, rootTDAOrganizer, taskOrganizer, mainStage,
sideStage, imeController, insetsController, splitLayout, transitions,
- transactionPool, logger, unfoldController);
+ transactionPool, logger, recentTasks, unfoldController);
// Prepare default TaskDisplayArea for testing.
mDisplayAreaInfo = new DisplayAreaInfo(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index d5dee82..1eae625 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -66,6 +66,7 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
+import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
@@ -120,8 +121,7 @@
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions,
- mTransactionPool,
- mLogger, Optional::empty);
+ mTransactionPool, mLogger, Optional.empty(), Optional::empty);
mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
.when(mTransitions).startTransition(anyInt(), any(), any());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 617e94a..ad65c04 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -22,6 +22,7 @@
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -48,6 +49,7 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
+import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
@@ -161,19 +163,19 @@
@Test
public void testExitSplitScreen() {
- mStageCoordinator.exitSplitScreen(INVALID_TASK_ID,
- SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME);
+ when(mMainStage.isActive()).thenReturn(true);
+ mStageCoordinator.exitSplitScreen(INVALID_TASK_ID, EXIT_REASON_RETURN_HOME);
verify(mSideStage).removeAllTasks(any(WindowContainerTransaction.class), eq(false));
verify(mMainStage).deactivate(any(WindowContainerTransaction.class), eq(false));
}
@Test
public void testExitSplitScreenToMainStage() {
+ when(mMainStage.isActive()).thenReturn(true);
final int testTaskId = 12345;
when(mMainStage.containsTask(eq(testTaskId))).thenReturn(true);
when(mSideStage.containsTask(eq(testTaskId))).thenReturn(false);
- mStageCoordinator.exitSplitScreen(testTaskId,
- SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME);
+ mStageCoordinator.exitSplitScreen(testTaskId, EXIT_REASON_RETURN_HOME);
verify(mMainStage).reorderChild(eq(testTaskId), eq(true),
any(WindowContainerTransaction.class));
verify(mSideStage).removeAllTasks(any(WindowContainerTransaction.class), eq(false));
@@ -182,11 +184,11 @@
@Test
public void testExitSplitScreenToSideStage() {
+ when(mMainStage.isActive()).thenReturn(true);
final int testTaskId = 12345;
when(mMainStage.containsTask(eq(testTaskId))).thenReturn(false);
when(mSideStage.containsTask(eq(testTaskId))).thenReturn(true);
- mStageCoordinator.exitSplitScreen(testTaskId,
- SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME);
+ mStageCoordinator.exitSplitScreen(testTaskId, EXIT_REASON_RETURN_HOME);
verify(mSideStage).reorderChild(eq(testTaskId), eq(true),
any(WindowContainerTransaction.class));
verify(mSideStage).removeAllTasks(any(WindowContainerTransaction.class), eq(true));
@@ -197,7 +199,8 @@
return new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
mDisplayImeController, mDisplayInsetsController, splitLayout,
- mTransitions, mTransactionPool, mLogger, new UnfoldControllerProvider());
+ mTransitions, mTransactionPool, mLogger, Optional.empty(),
+ new UnfoldControllerProvider());
}
private class UnfoldControllerProvider implements
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index 53d5076..13b726e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -132,6 +132,7 @@
@Test
public void testTaskAppeared_notifiesUnfoldListener() {
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
final ActivityManager.RunningTaskInfo task =
new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
@@ -142,6 +143,7 @@
@Test
public void testTaskVanished_notifiesUnfoldListener() {
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
final ActivityManager.RunningTaskInfo task =
new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
mStageTaskListener.onTaskAppeared(task, mSurfaceControl);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index b866bf9..70b7c67 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -301,8 +301,8 @@
System.currentTimeMillis(),
new ComponentName("", ""), buffer,
ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
- Surface.ROTATION_0, taskSize, contentInsets, false,
- true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN,
+ Surface.ROTATION_0, taskSize, contentInsets, new Rect() /* letterboxInsets */,
+ false, true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN,
0 /* systemUiVisibility */, false /* isTranslucent */,
hasImeSurface /* hasImeSurface */);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
index aad9528..78e27c9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
@@ -94,8 +94,8 @@
System.currentTimeMillis(),
new ComponentName("", ""), buffer,
ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
- Surface.ROTATION_0, taskSize, contentInsets, false,
- true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN,
+ Surface.ROTATION_0, taskSize, contentInsets, new Rect() /* letterboxInsets */,
+ false, true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN,
0 /* systemUiVisibility */, false /* isTranslucent */, false /* hasImeSurface */);
}
diff --git a/libs/hwui/jni/GIFMovie.cpp b/libs/hwui/jni/GIFMovie.cpp
index c5c4711..fef51b8 100644
--- a/libs/hwui/jni/GIFMovie.cpp
+++ b/libs/hwui/jni/GIFMovie.cpp
@@ -10,13 +10,13 @@
#include "SkColor.h"
#include "SkColorPriv.h"
#include "SkStream.h"
-#include "SkTemplates.h"
-#include "SkUtils.h"
#include "gif_lib.h"
#include <log/log.h>
+#include <string.h>
+
#if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)
#define DGifCloseFile(a, b) DGifCloseFile(a)
#endif
@@ -219,8 +219,9 @@
copyHeight = bmHeight - top;
}
+ size_t bytes = copyWidth * SkColorTypeBytesPerPixel(bm->colorType());
for (; copyHeight > 0; copyHeight--) {
- sk_memset32(dst, col, copyWidth);
+ memset(dst, col, bytes);
dst += bmWidth;
}
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 42bbc72..becaca9 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -180,6 +180,11 @@
* <p>If present, this provider determines location using GNSS satellites. The responsiveness
* and accuracy of location fixes may depend on GNSS signal conditions.
*
+ * <p>Locations returned from this provider are with respect to the primary GNSS antenna
+ * position within the device. {@link #getGnssAntennaInfos()} may be used to determine the GNSS
+ * antenna position with respect to the Android Coordinate System, and convert between them if
+ * necessary. This is generally only necessary for high accuracy applications.
+ *
* <p>The extras Bundle for locations derived by this location provider may contain the
* following key/value pairs:
* <ul>
@@ -1451,6 +1456,11 @@
* provider availability update will be sent. As soon as the provider is enabled again, another
* provider availability update will be sent and location updates will resume.
*
+ * <p>Locations returned from {@link #GPS_PROVIDER} are with respect to the primary GNSS antenna
+ * position within the device. {@link #getGnssAntennaInfos()} may be used to determine the GNSS
+ * antenna position with respect to the Android Coordinate System, and convert between them if
+ * necessary. This is generally only necessary for high accuracy applications.
+ *
* <p>When location callbacks are invoked, the system will hold a wakelock on your
* application's behalf for some period of time, but not indefinitely. If your application
* requires a long running wakelock within the location callback, you should acquire it
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index 4ce9a320..190e1cc 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -16,6 +16,8 @@
package com.android.internal.location;
+import android.Manifest;
+import android.annotation.RequiresPermission;
import android.app.Notification;
import android.app.NotificationManager;
import android.compat.annotation.UnsupportedAppUsage;
@@ -28,8 +30,9 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
import android.util.Log;
import com.android.internal.R;
@@ -90,7 +93,6 @@
private final Context mContext;
private final TelephonyManager mTelephonyManager;
- private final PhoneStateListener mPhoneStateListener;
// parent gps location provider
private final LocationManager mLocationManager;
@@ -138,33 +140,37 @@
public int requestorIdEncoding;
@UnsupportedAppUsage
public int textEncoding;
- };
+ }
- public static class GpsNiResponse {
- /* User response, one of the values in GpsUserResponseType */
- int userResponse;
- };
+ private class EmergencyCallListener extends TelephonyCallback implements
+ TelephonyCallback.OutgoingEmergencyCallListener,
+ TelephonyCallback.CallStateListener {
- private final BroadcastReceiver mBroadcastReciever = new BroadcastReceiver() {
+ @Override
+ @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+ public void onOutgoingEmergencyCall(EmergencyNumber placedEmergencyNumber,
+ int subscriptionId) {
+ mIsInEmergencyCall = true;
+ if (DEBUG) Log.d(TAG, "onOutgoingEmergencyCall(): inEmergency = " + getInEmergency());
+ }
- @Override public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
- String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
- /*
- Tracks the emergency call:
- mIsInEmergencyCall records if the phone is in emergency call or not. It will
- be set to true when the phone is having emergency call, and then will
- be set to false by mPhoneStateListener when the emergency call ends.
- */
- mIsInEmergencyCall = mTelephonyManager.isEmergencyNumber(phoneNumber);
- if (DEBUG) Log.v(TAG, "ACTION_NEW_OUTGOING_CALL - " + getInEmergency());
- } else if (action.equals(LocationManager.MODE_CHANGED_ACTION)) {
- updateLocationMode();
- if (DEBUG) Log.d(TAG, "location enabled :" + getLocationEnabled());
+ @Override
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ public void onCallStateChanged(int state) {
+ if (DEBUG) Log.d(TAG, "onCallStateChanged(): state is " + state);
+ // listening for emergency call ends
+ if (state == TelephonyManager.CALL_STATE_IDLE) {
+ if (mIsInEmergencyCall) {
+ mCallEndElapsedRealtimeMillis = SystemClock.elapsedRealtime();
+ mIsInEmergencyCall = false;
+ }
}
}
- };
+ }
+
+ // The internal implementation of TelephonyManager uses WeakReference so we have to keep a
+ // reference here.
+ private final EmergencyCallListener mEmergencyCallListener = new EmergencyCallListener();
/**
* The notification that is shown when a network-initiated notification
@@ -190,26 +196,22 @@
updateLocationMode();
mTelephonyManager =
(TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
+ mTelephonyManager.registerTelephonyCallback(mContext.getMainExecutor(),
+ mEmergencyCallListener);
- mPhoneStateListener = new PhoneStateListener() {
+ BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
+
@Override
- public void onCallStateChanged(int state, String incomingNumber) {
- if (DEBUG) Log.d(TAG, "onCallStateChanged(): state is "+ state);
- // listening for emergency call ends
- if (state == TelephonyManager.CALL_STATE_IDLE) {
- if (mIsInEmergencyCall) {
- mCallEndElapsedRealtimeMillis = SystemClock.elapsedRealtime();
- mIsInEmergencyCall = false;
- }
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(LocationManager.MODE_CHANGED_ACTION)) {
+ updateLocationMode();
+ if (DEBUG) Log.d(TAG, "location enabled :" + getLocationEnabled());
}
}
};
- mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
-
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_NEW_OUTGOING_CALL);
- intentFilter.addAction(LocationManager.MODE_CHANGED_ACTION);
- mContext.registerReceiver(mBroadcastReciever, intentFilter);
+ mContext.registerReceiver(broadcastReceiver,
+ new IntentFilter(LocationManager.MODE_CHANGED_ACTION));
}
public void setSuplEsEnabled(boolean isEnabled) {
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 5cdb898..d06bbc6 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -675,8 +675,44 @@
public static final int CHANNEL_IN_Z_AXIS = 0x2000;
public static final int CHANNEL_IN_VOICE_UPLINK = 0x4000;
public static final int CHANNEL_IN_VOICE_DNLINK = 0x8000;
+ // CHANNEL_IN_BACK_LEFT to TOP_RIGHT are not microphone positions
+ // but surround channels which are used when dealing with multi-channel inputs,
+ // e.g. via HDMI input on TV.
+ /** @hide */
+ public static final int CHANNEL_IN_BACK_LEFT = 0x10000;
+ /** @hide */
+ public static final int CHANNEL_IN_BACK_RIGHT = 0x20000;
+ /** @hide */
+ public static final int CHANNEL_IN_CENTER = 0x40000;
+ /** @hide */
+ public static final int CHANNEL_IN_LOW_FREQUENCY = 0x100000;
+ /** @hide */
+ public static final int CHANNEL_IN_TOP_LEFT = 0x200000;
+ /** @hide */
+ public static final int CHANNEL_IN_TOP_RIGHT = 0x400000;
public static final int CHANNEL_IN_MONO = CHANNEL_IN_FRONT;
public static final int CHANNEL_IN_STEREO = (CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT);
+ // Surround channel masks corresponding to output masks, used for
+ // surround sound inputs.
+ /** @hide */
+ public static final int CHANNEL_IN_2POINT0POINT2 = (
+ CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT | CHANNEL_IN_TOP_LEFT | CHANNEL_IN_TOP_RIGHT);
+ /** @hide */
+ public static final int CHANNEL_IN_2POINT1POINT2 = (
+ CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT | CHANNEL_IN_TOP_LEFT | CHANNEL_IN_TOP_RIGHT
+ | CHANNEL_IN_LOW_FREQUENCY);
+ /** @hide */
+ public static final int CHANNEL_IN_3POINT0POINT2 = (
+ CHANNEL_IN_LEFT | CHANNEL_IN_CENTER | CHANNEL_IN_RIGHT | CHANNEL_IN_TOP_LEFT
+ | CHANNEL_IN_TOP_RIGHT);
+ /** @hide */
+ public static final int CHANNEL_IN_3POINT1POINT2 = (
+ CHANNEL_IN_LEFT | CHANNEL_IN_CENTER | CHANNEL_IN_RIGHT | CHANNEL_IN_TOP_LEFT
+ | CHANNEL_IN_TOP_RIGHT | CHANNEL_IN_LOW_FREQUENCY);
+ /** @hide */
+ public static final int CHANNEL_IN_5POINT1 = (
+ CHANNEL_IN_LEFT | CHANNEL_IN_CENTER | CHANNEL_IN_RIGHT | CHANNEL_IN_BACK_LEFT
+ | CHANNEL_IN_BACK_RIGHT | CHANNEL_IN_LOW_FREQUENCY);
/** @hide */
public static final int CHANNEL_IN_FRONT_BACK = CHANNEL_IN_FRONT | CHANNEL_IN_BACK;
// CHANNEL_IN_ALL is not yet defined; if added then it should match AUDIO_CHANNEL_IN_ALL
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index a6c6fe6..c6cc077 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -55,12 +55,10 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -3274,6 +3272,54 @@
}
/**
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
+ public void setHfpEnabled(boolean enable) {
+ AudioSystem.setParameters("hfp_enable=" + enable);
+ }
+
+ /**
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
+ public void setHfpVolume(int volume) {
+ AudioSystem.setParameters("hfp_volume=" + volume);
+ }
+
+ /**
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
+ public void setHfpSamplingRate(int rate) {
+ AudioSystem.setParameters("hfp_set_sampling_rate=" + rate);
+ }
+
+ /**
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
+ public void setBluetoothHeadsetProperties(@NonNull String name, boolean hasNrecEnabled,
+ boolean hasWbsEnabled) {
+ AudioSystem.setParameters("bt_headset_name=" + name
+ + ";bt_headset_nrec=" + (hasNrecEnabled ? "on" : "off")
+ + ";bt_wbs=" + (hasWbsEnabled ? "on" : "off"));
+ }
+
+ /**
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
+ public void setA2dpSuspended(boolean enable) {
+ AudioSystem.setParameters("A2dpSuspended=" + enable);
+ }
+
+ /**
* Gets a variable number of parameter values from audio hardware.
*
* @param keys list of parameters
@@ -3500,20 +3546,7 @@
* whether sounds are heard or not.
*/
public void playSoundEffect(@SystemSoundEffect int effectType) {
- if (effectType < 0 || effectType >= NUM_SOUND_EFFECTS) {
- return;
- }
-
- if (!querySoundEffectsEnabled(Process.myUserHandle().getIdentifier())) {
- return;
- }
-
- final IAudioService service = getService();
- try {
- service.playSoundEffect(effectType);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ playSoundEffect(effectType, UserHandle.USER_CURRENT);
}
/**
@@ -3529,13 +3562,9 @@
return;
}
- if (!querySoundEffectsEnabled(userId)) {
- return;
- }
-
final IAudioService service = getService();
try {
- service.playSoundEffect(effectType);
+ service.playSoundEffect(effectType, userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -3564,14 +3593,6 @@
}
/**
- * Settings has an in memory cache, so this is fast.
- */
- private boolean querySoundEffectsEnabled(int user) {
- return Settings.System.getIntForUser(getContext().getContentResolver(),
- Settings.System.SOUND_EFFECTS_ENABLED, 0, user) != 0;
- }
-
- /**
* Load Sound effects.
* This method must be called when sound effects are enabled.
*/
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index d736c25..6f03895 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -158,7 +158,7 @@
int getMode();
- oneway void playSoundEffect(int effectType);
+ oneway void playSoundEffect(int effectType, int userId);
oneway void playSoundEffectVolume(int effectType, float volume);
diff --git a/media/java/android/media/audio/common/AidlConversion.java b/media/java/android/media/audio/common/AidlConversion.java
index f96ba8c..1053fb7 100644
--- a/media/java/android/media/audio/common/AidlConversion.java
+++ b/media/java/android/media/audio/common/AidlConversion.java
@@ -131,6 +131,124 @@
/** Convert from legacy audio_usage_t to AIDL AudioUsage. */
public static native int legacy2aidl_audio_usage_t_AudioUsage(int /*audio_usage_t*/ legacy);
+ /** Convert from a channel bit of AIDL AudioChannelLayout to SDK AudioFormat.CHANNEL_* bit. */
+ private static int aidl2api_AudioChannelLayoutBit_AudioFormatChannel(
+ int aidlBit, boolean isInput) {
+ if (isInput) {
+ switch (aidlBit) {
+ case AudioChannelLayout.CHANNEL_FRONT_LEFT:
+ return AudioFormat.CHANNEL_IN_LEFT;
+ case AudioChannelLayout.CHANNEL_FRONT_RIGHT:
+ return AudioFormat.CHANNEL_IN_RIGHT;
+ case AudioChannelLayout.CHANNEL_FRONT_CENTER:
+ return AudioFormat.CHANNEL_IN_CENTER;
+ case AudioChannelLayout.CHANNEL_BACK_CENTER:
+ return AudioFormat.CHANNEL_IN_BACK;
+ // CHANNEL_IN_*_PROCESSED not supported
+ // CHANNEL_IN_PRESSURE not supported
+ // CHANNEL_IN_*_AXIS not supported
+ // CHANNEL_IN_VOICE_* not supported
+ case AudioChannelLayout.CHANNEL_BACK_LEFT:
+ return AudioFormat.CHANNEL_IN_BACK_LEFT;
+ case AudioChannelLayout.CHANNEL_BACK_RIGHT:
+ return AudioFormat.CHANNEL_IN_BACK_RIGHT;
+ case AudioChannelLayout.CHANNEL_LOW_FREQUENCY:
+ return AudioFormat.CHANNEL_IN_LOW_FREQUENCY;
+ case AudioChannelLayout.CHANNEL_TOP_SIDE_LEFT:
+ return AudioFormat.CHANNEL_IN_TOP_LEFT;
+ case AudioChannelLayout.CHANNEL_TOP_SIDE_RIGHT:
+ return AudioFormat.CHANNEL_IN_TOP_RIGHT;
+ default:
+ return AudioFormat.CHANNEL_INVALID;
+ }
+ } else {
+ switch (aidlBit) {
+ case AudioChannelLayout.CHANNEL_FRONT_LEFT:
+ return AudioFormat.CHANNEL_OUT_FRONT_LEFT;
+ case AudioChannelLayout.CHANNEL_FRONT_RIGHT:
+ return AudioFormat.CHANNEL_OUT_FRONT_RIGHT;
+ case AudioChannelLayout.CHANNEL_FRONT_CENTER:
+ return AudioFormat.CHANNEL_OUT_FRONT_CENTER;
+ case AudioChannelLayout.CHANNEL_LOW_FREQUENCY:
+ return AudioFormat.CHANNEL_OUT_LOW_FREQUENCY;
+ case AudioChannelLayout.CHANNEL_BACK_LEFT:
+ return AudioFormat.CHANNEL_OUT_BACK_LEFT;
+ case AudioChannelLayout.CHANNEL_BACK_RIGHT:
+ return AudioFormat.CHANNEL_OUT_BACK_RIGHT;
+ case AudioChannelLayout.CHANNEL_FRONT_LEFT_OF_CENTER:
+ return AudioFormat.CHANNEL_OUT_FRONT_LEFT_OF_CENTER;
+ case AudioChannelLayout.CHANNEL_FRONT_RIGHT_OF_CENTER:
+ return AudioFormat.CHANNEL_OUT_FRONT_RIGHT_OF_CENTER;
+ case AudioChannelLayout.CHANNEL_BACK_CENTER:
+ return AudioFormat.CHANNEL_OUT_BACK_CENTER;
+ case AudioChannelLayout.CHANNEL_SIDE_LEFT:
+ return AudioFormat.CHANNEL_OUT_SIDE_LEFT;
+ case AudioChannelLayout.CHANNEL_SIDE_RIGHT:
+ return AudioFormat.CHANNEL_OUT_SIDE_RIGHT;
+ case AudioChannelLayout.CHANNEL_TOP_CENTER:
+ return AudioFormat.CHANNEL_OUT_TOP_CENTER;
+ case AudioChannelLayout.CHANNEL_TOP_FRONT_LEFT:
+ return AudioFormat.CHANNEL_OUT_TOP_FRONT_LEFT;
+ case AudioChannelLayout.CHANNEL_TOP_FRONT_CENTER:
+ return AudioFormat.CHANNEL_OUT_TOP_FRONT_CENTER;
+ case AudioChannelLayout.CHANNEL_TOP_FRONT_RIGHT:
+ return AudioFormat.CHANNEL_OUT_TOP_FRONT_RIGHT;
+ case AudioChannelLayout.CHANNEL_TOP_BACK_LEFT:
+ return AudioFormat.CHANNEL_OUT_TOP_BACK_LEFT;
+ case AudioChannelLayout.CHANNEL_TOP_BACK_CENTER:
+ return AudioFormat.CHANNEL_OUT_TOP_BACK_CENTER;
+ case AudioChannelLayout.CHANNEL_TOP_BACK_RIGHT:
+ return AudioFormat.CHANNEL_OUT_TOP_BACK_RIGHT;
+ case AudioChannelLayout.CHANNEL_TOP_SIDE_LEFT:
+ return AudioFormat.CHANNEL_OUT_TOP_SIDE_LEFT;
+ case AudioChannelLayout.CHANNEL_TOP_SIDE_RIGHT:
+ return AudioFormat.CHANNEL_OUT_TOP_SIDE_RIGHT;
+ case AudioChannelLayout.CHANNEL_BOTTOM_FRONT_LEFT:
+ return AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_LEFT;
+ case AudioChannelLayout.CHANNEL_BOTTOM_FRONT_CENTER:
+ return AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_CENTER;
+ case AudioChannelLayout.CHANNEL_BOTTOM_FRONT_RIGHT:
+ return AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_RIGHT;
+ case AudioChannelLayout.CHANNEL_LOW_FREQUENCY_2:
+ return AudioFormat.CHANNEL_OUT_LOW_FREQUENCY_2;
+ case AudioChannelLayout.CHANNEL_FRONT_WIDE_LEFT:
+ return AudioFormat.CHANNEL_OUT_FRONT_WIDE_LEFT;
+ case AudioChannelLayout.CHANNEL_FRONT_WIDE_RIGHT:
+ return AudioFormat.CHANNEL_OUT_FRONT_WIDE_RIGHT;
+ case AudioChannelLayout.CHANNEL_HAPTIC_A:
+ return AudioFormat.CHANNEL_OUT_HAPTIC_A;
+ case AudioChannelLayout.CHANNEL_HAPTIC_B:
+ return AudioFormat.CHANNEL_OUT_HAPTIC_B;
+ default:
+ return AudioFormat.CHANNEL_INVALID;
+ }
+ }
+ }
+
+ /**
+ * Convert from a channel bitmask of AIDL AudioChannelLayout to
+ * SDK AudioFormat.CHANNEL_* bitmask.
+ */
+ private static int aidl2api_AudioChannelLayoutBitMask_AudioFormatChannelMask(
+ int aidlBitMask, boolean isInput) {
+ int apiMask = 0;
+ for (int bit = 1 << 31; bit != 0; bit >>>= 1) {
+ if ((aidlBitMask & bit) == bit) {
+ int apiBit = aidl2api_AudioChannelLayoutBit_AudioFormatChannel(bit, isInput);
+ if (apiBit != AudioFormat.CHANNEL_INVALID) {
+ apiMask |= apiBit;
+ aidlBitMask &= ~bit;
+ if (aidlBitMask == 0) {
+ return apiMask;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ return AudioFormat.CHANNEL_INVALID;
+ }
+
/** Convert from AIDL AudioChannelLayout to SDK AudioFormat.CHANNEL_*. */
public static int aidl2api_AudioChannelLayout_AudioFormatChannelMask(
@NonNull AudioChannelLayout aidlMask, boolean isInput) {
@@ -155,8 +273,17 @@
return AudioFormat.CHANNEL_IN_STEREO;
case AudioChannelLayout.LAYOUT_FRONT_BACK:
return AudioFormat.CHANNEL_IN_FRONT_BACK;
- default:
- return AudioFormat.CHANNEL_INVALID;
+ case AudioChannelLayout.LAYOUT_2POINT0POINT2:
+ return AudioFormat.CHANNEL_IN_2POINT0POINT2;
+ case AudioChannelLayout.LAYOUT_2POINT1POINT2:
+ return AudioFormat.CHANNEL_IN_2POINT1POINT2;
+ case AudioChannelLayout.LAYOUT_3POINT0POINT2:
+ return AudioFormat.CHANNEL_IN_3POINT0POINT2;
+ case AudioChannelLayout.LAYOUT_3POINT1POINT2:
+ return AudioFormat.CHANNEL_IN_3POINT1POINT2;
+ case AudioChannelLayout.LAYOUT_5POINT1:
+ return AudioFormat.CHANNEL_IN_5POINT1;
+ default: // fall through
}
} else {
switch (aidlMask.getLayoutMask()) {
@@ -264,10 +391,12 @@
case AudioChannelLayout.LAYOUT_FRONT_BACK:
return AudioFormat.CHANNEL_OUT_FRONT_CENTER
| AudioFormat.CHANNEL_OUT_BACK_CENTER;
- default:
- return AudioFormat.CHANNEL_INVALID;
+ default: // fall through
}
}
+ // If a match for a predefined layout wasn't found, make a custom one from bits.
+ return aidl2api_AudioChannelLayoutBitMask_AudioFormatChannelMask(
+ aidlMask.getLayoutMask(), isInput);
case AudioChannelLayout.voiceMask:
if (isInput) {
switch (aidlMask.getVoiceMask()) {
diff --git a/media/java/android/media/tv/interactive/ITvIAppClient.aidl b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
index 4197934..0dd64b8 100644
--- a/media/java/android/media/tv/interactive/ITvIAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
@@ -24,4 +24,5 @@
oneway interface ITvIAppClient {
void onSessionCreated(in String iAppServiceId, IBinder token, int seq);
void onSessionReleased(int seq);
+ void onLayoutSurface(int left, int top, int right, int bottom, int seq);
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppManager.aidl b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
index a435e20..c7b08d5 100644
--- a/media/java/android/media/tv/interactive/ITvIAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
@@ -17,6 +17,7 @@
package android.media.tv.interactive;
import android.media.tv.interactive.ITvIAppClient;
+import android.view.Surface;
/**
* Interface to the TV interactive app service.
@@ -27,4 +28,7 @@
void createSession(
in ITvIAppClient client, in String iAppServiceId, int type, int seq, int userId);
void releaseSession(in IBinder sessionToken, int userId);
+ void setSurface(in IBinder sessionToken, in Surface surface, int userId);
+ void dispatchSurfaceChanged(in IBinder sessionToken, int format, int width, int height,
+ int userId);
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppSession.aidl b/media/java/android/media/tv/interactive/ITvIAppSession.aidl
index 0cbdc8e..0afa971 100644
--- a/media/java/android/media/tv/interactive/ITvIAppSession.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppSession.aidl
@@ -16,6 +16,8 @@
package android.media.tv.interactive;
+import android.view.Surface;
+
/**
* Sub-interface of ITvIAppService.aidl which is created per session and has its own context.
* @hide
@@ -23,4 +25,6 @@
oneway interface ITvIAppSession {
void startIApp();
void release();
+ void setSurface(in Surface surface);
+ void dispatchSurfaceChanged(int format, int width, int height);
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
index b3b317e..0873aad 100644
--- a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
@@ -25,4 +25,5 @@
*/
oneway interface ITvIAppSessionCallback {
void onSessionCreated(in ITvIAppSession session);
+ void onLayoutSurface(int left, int top, int right, int bottom);
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java
index f656534..16e19e7 100644
--- a/media/java/android/media/tv/interactive/TvIAppManager.java
+++ b/media/java/android/media/tv/interactive/TvIAppManager.java
@@ -25,6 +25,7 @@
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;
+import android.view.Surface;
import com.android.internal.util.Preconditions;
@@ -88,6 +89,18 @@
record.postSessionReleased();
}
}
+
+ @Override
+ public void onLayoutSurface(int left, int top, int right, int bottom, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postLayoutSurface(left, top, right, bottom);
+ }
+ }
};
}
@@ -159,6 +172,44 @@
}
/**
+ * Sets the {@link android.view.Surface} for this session.
+ *
+ * @param surface A {@link android.view.Surface} used to render video.
+ */
+ public void setSurface(Surface surface) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ // surface can be null.
+ try {
+ mService.setSurface(mToken, surface, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Notifies of any structural changes (format or size) of the surface passed in
+ * {@link #setSurface}.
+ *
+ * @param format The new PixelFormat of the surface.
+ * @param width The new width of the surface.
+ * @param height The new height of the surface.
+ */
+ public void dispatchSurfaceChanged(int format, int width, int height) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.dispatchSurfaceChanged(mToken, format, width, height, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Releases this session.
*/
public void release() {
@@ -211,6 +262,16 @@
}
});
}
+
+ void postLayoutSurface(final int left, final int top, final int right,
+ final int bottom) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onLayoutSurface(mSession, left, top, right, bottom);
+ }
+ });
+ }
}
/**
@@ -235,5 +296,18 @@
*/
public void onSessionReleased(@NonNull Session session) {
}
+
+ /**
+ * This is called when {@link TvIAppService.Session#layoutSurface} is called to change the
+ * layout of surface.
+ *
+ * @param session A {@link TvIAppManager.Session} associated with this callback.
+ * @param left Left position.
+ * @param top Top position.
+ * @param right Right position.
+ * @param bottom Bottom position.
+ */
+ public void onLayoutSurface(Session session, int left, int top, int right, int bottom) {
+ }
}
}
diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java
index f363728..b385d9c 100644
--- a/media/java/android/media/tv/interactive/TvIAppService.java
+++ b/media/java/android/media/tv/interactive/TvIAppService.java
@@ -16,10 +16,12 @@
package android.media.tv.interactive;
+import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.Service;
+import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
@@ -27,6 +29,7 @@
import android.os.RemoteException;
import android.util.Log;
import android.view.KeyEvent;
+import android.view.Surface;
import com.android.internal.os.SomeArgs;
@@ -94,6 +97,20 @@
// @GuardedBy("mLock")
private final List<Runnable> mPendingActions = new ArrayList<>();
+ private final Context mContext;
+ private final Handler mHandler;
+ private Surface mSurface;
+
+ /**
+ * Creates a new Session.
+ *
+ * @param context The context of the application
+ */
+ public Session(Context context) {
+ mContext = context;
+ mHandler = new Handler(context.getMainLooper());
+ }
+
/**
* Starts TvIAppService session.
*/
@@ -101,16 +118,79 @@
}
/**
+ * Called when the application sets the surface.
+ *
+ * <p>The TV IApp service should render interactive app UI onto the given surface. When
+ * called with {@code null}, the input service should immediately free any references to the
+ * currently set surface and stop using it.
+ *
+ * @param surface The surface to be used for interactive app UI rendering. Can be
+ * {@code null}.
+ * @return {@code true} if the surface was set successfully, {@code false} otherwise.
+ */
+ public abstract boolean onSetSurface(@Nullable Surface surface);
+
+ /**
+ * Called after any structural changes (format or size) have been made to the surface passed
+ * in {@link #onSetSurface}. This method is always called at least once, after
+ * {@link #onSetSurface} is called with non-null surface.
+ *
+ * @param format The new PixelFormat of the surface.
+ * @param width The new width of the surface.
+ * @param height The new height of the surface.
+ */
+ public void onSurfaceChanged(int format, int width, int height) {
+ }
+
+ /**
* Releases TvIAppService session.
*/
public void onRelease() {
}
+ /**
+ * Assigns a size and position to the surface passed in {@link #onSetSurface}. The position
+ * is relative to the overlay view that sits on top of this surface.
+ *
+ * @param left Left position in pixels, relative to the overlay view.
+ * @param top Top position in pixels, relative to the overlay view.
+ * @param right Right position in pixels, relative to the overlay view.
+ * @param bottom Bottom position in pixels, relative to the overlay view.
+ */
+ public void layoutSurface(final int left, final int top, final int right,
+ final int bottom) {
+ if (left > right || top > bottom) {
+ throw new IllegalArgumentException("Invalid parameter");
+ }
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "layoutSurface (l=" + left + ", t=" + top
+ + ", r=" + right + ", b=" + bottom + ",)");
+ }
+ if (mSessionCallback != null) {
+ mSessionCallback.onLayoutSurface(left, top, right, bottom);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in layoutSurface", e);
+ }
+ }
+ });
+ }
+
void startIApp() {
onStartIApp();
}
+
void release() {
onRelease();
+ if (mSurface != null) {
+ mSurface.release();
+ mSurface = null;
+ }
}
private void initialize(ITvIAppSessionCallback callback) {
@@ -122,6 +202,45 @@
mPendingActions.clear();
}
}
+
+ /**
+ * Calls {@link #onSetSurface}.
+ */
+ void setSurface(Surface surface) {
+ onSetSurface(surface);
+ if (mSurface != null) {
+ mSurface.release();
+ }
+ mSurface = surface;
+ // TODO: Handle failure.
+ }
+
+ /**
+ * Calls {@link #onSurfaceChanged}.
+ */
+ void dispatchSurfaceChanged(int format, int width, int height) {
+ if (DEBUG) {
+ Log.d(TAG, "dispatchSurfaceChanged(format=" + format + ", width=" + width
+ + ", height=" + height + ")");
+ }
+ onSurfaceChanged(format, width, height);
+ }
+
+ private void executeOrPostRunnableOnMainThread(Runnable action) {
+ synchronized (mLock) {
+ if (mSessionCallback == null) {
+ // The session is not initialized yet.
+ mPendingActions.add(action);
+ } else {
+ if (mHandler.getLooper().isCurrentThread()) {
+ action.run();
+ } else {
+ // Posts the runnable if this is not called from the main thread
+ mHandler.post(action);
+ }
+ }
+ }
+ }
}
/**
@@ -143,6 +262,16 @@
public void release() {
mSessionImpl.release();
}
+
+ @Override
+ public void setSurface(Surface surface) {
+ mSessionImpl.setSurface(surface);
+ }
+
+ @Override
+ public void dispatchSurfaceChanged(int format, int width, int height) {
+ mSessionImpl.dispatchSurfaceChanged(format, width, height);
+ }
}
@SuppressLint("HandlerLeak")
diff --git a/media/java/android/media/tv/interactive/TvIAppView.java b/media/java/android/media/tv/interactive/TvIAppView.java
index f56ea0a..adaaab0 100644
--- a/media/java/android/media/tv/interactive/TvIAppView.java
+++ b/media/java/android/media/tv/interactive/TvIAppView.java
@@ -17,10 +17,18 @@
package android.media.tv.interactive;
import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
import android.media.tv.interactive.TvIAppManager.Session;
import android.media.tv.interactive.TvIAppManager.SessionCallback;
import android.os.Handler;
+import android.util.AttributeSet;
import android.util.Log;
+import android.util.Xml;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
import android.view.ViewGroup;
/**
@@ -35,18 +43,137 @@
private final Handler mHandler = new Handler();
private Session mSession;
private MySessionCallback mSessionCallback;
+ private SurfaceView mSurfaceView;
+ private Surface mSurface;
+
+ private boolean mSurfaceChanged;
+ private int mSurfaceFormat;
+ private int mSurfaceWidth;
+ private int mSurfaceHeight;
+
+ private boolean mUseRequestedSurfaceLayout;
+ private int mSurfaceViewLeft;
+ private int mSurfaceViewRight;
+ private int mSurfaceViewTop;
+ private int mSurfaceViewBottom;
+
+ private final AttributeSet mAttrs;
+ private final int mDefStyleAttr;
+ private final XmlResourceParser mParser;
+
+ private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ if (DEBUG) {
+ Log.d(TAG, "surfaceChanged(holder=" + holder + ", format=" + format
+ + ", width=" + width + ", height=" + height + ")");
+ }
+ mSurfaceFormat = format;
+ mSurfaceWidth = width;
+ mSurfaceHeight = height;
+ mSurfaceChanged = true;
+ dispatchSurfaceChanged(mSurfaceFormat, mSurfaceWidth, mSurfaceHeight);
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ mSurface = holder.getSurface();
+ setSessionSurface(mSurface);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ mSurface = null;
+ mSurfaceChanged = false;
+ setSessionSurface(null);
+ }
+ };
public TvIAppView(Context context) {
+ this(context, null, 0);
+ }
+
+ public TvIAppView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, /* attrs = */null, /* defStyleAttr = */0);
+ int sourceResId = Resources.getAttributeSetSourceResId(attrs);
+ if (sourceResId != Resources.ID_NULL) {
+ Log.d(TAG, "Build local AttributeSet");
+ mParser = context.getResources().getXml(sourceResId);
+ mAttrs = Xml.asAttributeSet(mParser);
+ } else {
+ Log.d(TAG, "Use passed in AttributeSet");
+ mParser = null;
+ mAttrs = attrs;
+ }
+ mDefStyleAttr = defStyleAttr;
+ resetSurfaceView();
mTvIAppManager = (TvIAppManager) getContext().getSystemService("tv_interactive_app");
}
@Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (DEBUG) {
- Log.d(TAG,
- "onLayout (left=" + l + ", top=" + t + ", right=" + r + ", bottom=" + b + ",)");
+ Log.d(TAG, "onLayout (left=" + left + ", top=" + top + ", right=" + right
+ + ", bottom=" + bottom + ",)");
}
+ if (mUseRequestedSurfaceLayout) {
+ mSurfaceView.layout(mSurfaceViewLeft, mSurfaceViewTop, mSurfaceViewRight,
+ mSurfaceViewBottom);
+ } else {
+ mSurfaceView.layout(0, 0, right - left, bottom - top);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ mSurfaceView.measure(widthMeasureSpec, heightMeasureSpec);
+ int width = mSurfaceView.getMeasuredWidth();
+ int height = mSurfaceView.getMeasuredHeight();
+ int childState = mSurfaceView.getMeasuredState();
+ setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, childState),
+ resolveSizeAndState(height, heightMeasureSpec,
+ childState << MEASURED_HEIGHT_STATE_SHIFT));
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ mSurfaceView.setVisibility(visibility);
+ }
+
+ private void resetSurfaceView() {
+ if (mSurfaceView != null) {
+ mSurfaceView.getHolder().removeCallback(mSurfaceHolderCallback);
+ removeView(mSurfaceView);
+ }
+ mSurface = null;
+ mSurfaceView = new SurfaceView(getContext(), mAttrs, mDefStyleAttr);
+ // The surface view's content should be treated as secure all the time.
+ mSurfaceView.setSecure(true);
+ mSurfaceView.getHolder().addCallback(mSurfaceHolderCallback);
+ addView(mSurfaceView);
+ }
+
+ /**
+ * Resets this TvIAppView.
+ */
+ public void reset() {
+ if (DEBUG) Log.d(TAG, "reset()");
+ resetInternal();
+ }
+
+ private void setSessionSurface(Surface surface) {
+ if (mSession == null) {
+ return;
+ }
+ mSession.setSurface(surface);
+ }
+
+ private void dispatchSurfaceChanged(int format, int width, int height) {
+ if (mSession == null) {
+ return;
+ }
+ mSession.dispatchSurfaceChanged(format, width, height);
}
/**
@@ -75,6 +202,17 @@
}
}
+ private void resetInternal() {
+ mSessionCallback = null;
+ if (mSession != null) {
+ setSessionSurface(null);
+ mUseRequestedSurfaceLayout = false;
+ mSession.release();
+ mSession = null;
+ resetSurfaceView();
+ }
+ }
+
private class MySessionCallback extends SessionCallback {
final String mIAppServiceId;
int mType;
@@ -99,7 +237,15 @@
}
mSession = session;
if (session != null) {
- // TODO: handle SurfaceView and InputChannel.
+ // mSurface may not be ready yet as soon as starting an application.
+ // In the case, we don't send Session.setSurface(null) unnecessarily.
+ // setSessionSurface will be called in surfaceCreated.
+ if (mSurface != null) {
+ setSessionSurface(mSurface);
+ if (mSurfaceChanged) {
+ dispatchSurfaceChanged(mSurfaceFormat, mSurfaceWidth, mSurfaceHeight);
+ }
+ }
} else {
// Failed to create
// Todo: forward error to Tv App
@@ -119,5 +265,23 @@
mSessionCallback = null;
mSession = null;
}
+
+ @Override
+ public void onLayoutSurface(Session session, int left, int top, int right, int bottom) {
+ if (DEBUG) {
+ Log.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top + ", right="
+ + right + ", bottom=" + bottom + ",)");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onLayoutSurface - session not created");
+ return;
+ }
+ mSurfaceViewLeft = left;
+ mSurfaceViewTop = top;
+ mSurfaceViewRight = right;
+ mSurfaceViewBottom = bottom;
+ mUseRequestedSurfaceLayout = true;
+ requestLayout();
+ }
}
}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index c484309..73a821e 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -510,10 +510,10 @@
if (res != Tuner.RESULT_SUCCESS) {
TunerUtils.throwExceptionForResult(res, "failed to close frontend");
}
+ mTunerResourceManager.releaseFrontend(mFrontendHandle, mClientId);
}
mIsSharedFrontend = false;
}
- mTunerResourceManager.releaseFrontend(mFrontendHandle, mClientId);
FrameworkStatsLog
.write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__UNKNOWN);
@@ -749,6 +749,12 @@
* supported in Tuner 1.1 or higher version. Unsupported version will cause no-op. Use {@link
* TunerVersionChecker#getTunerVersion()} to get the version information.
*
+ * <p>Tuning with {@link
+ * android.media.tv.tuner.frontend.IsdbtFrontendSettings.PartialReceptionFlag} or {@link
+ * android.media.tv.tuner.frontend.IsdbtFrontendSettings.IsdbtLayerSettings} is only supported
+ * in Tuner 2.0 or higher version. Unsupported version will cause no-op. Use {@link
+ * TunerVersionChecker#getTunerVersion()} to get the version information.
+ *
* @param settings Signal delivery information the frontend uses to
* search and lock the signal.
* @return result status of tune operation.
@@ -803,6 +809,12 @@
* supported in Tuner 1.1 or higher version. Unsupported version will cause no-op. Use {@link
* TunerVersionChecker#getTunerVersion()} to get the version information.
*
+ * * <p>Scanning with {@link
+ * android.media.tv.tuner.frontend.IsdbtFrontendSettings.PartialReceptionFlag} or {@link
+ * android.media.tv.tuner.frontend.IsdbtFrontendSettings.IsdbtLayerSettings} is only supported
+ * in Tuner 2.0 or higher version. Unsupported version will cause no-op. Use {@link
+ * TunerVersionChecker#getTunerVersion()} to get the version information.
+ *
* @param settings A {@link FrontendSettings} to configure the frontend.
* @param scanType The scan type.
* @throws SecurityException if the caller does not have appropriate permissions.
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index 53adc75..31f1a63 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -52,7 +52,8 @@
FRONTEND_STATUS_TYPE_ISDBT_SEGMENTS, FRONTEND_STATUS_TYPE_TS_DATA_RATES,
FRONTEND_STATUS_TYPE_MODULATIONS_EXT, FRONTEND_STATUS_TYPE_ROLL_OFF,
FRONTEND_STATUS_TYPE_IS_MISO_ENABLED, FRONTEND_STATUS_TYPE_IS_LINEAR,
- FRONTEND_STATUS_TYPE_IS_SHORT_FRAMES_ENABLED})
+ FRONTEND_STATUS_TYPE_IS_SHORT_FRAMES_ENABLED, FRONTEND_STATUS_TYPE_ISDBT_MODE,
+ FRONTEND_STATUS_TYPE_ISDBT_PARTIAL_RECEPTION_FLAG})
@Retention(RetentionPolicy.SOURCE)
public @interface FrontendStatusType {}
@@ -242,6 +243,16 @@
*/
public static final int FRONTEND_STATUS_TYPE_IS_SHORT_FRAMES_ENABLED =
android.hardware.tv.tuner.FrontendStatusType.IS_SHORT_FRAMES;
+ /**
+ * ISDB-T mode. Only supported in Tuner HAL 2.0 or higher.
+ */
+ public static final int FRONTEND_STATUS_TYPE_ISDBT_MODE =
+ android.hardware.tv.tuner.FrontendStatusType.ISDBT_MODE;
+ /**
+ * ISDB-T partial reception flag. Only supported in Tuner HAL 2.0 or higher.
+ */
+ public static final int FRONTEND_STATUS_TYPE_ISDBT_PARTIAL_RECEPTION_FLAG =
+ android.hardware.tv.tuner.FrontendStatusType.ISDBT_PARTIAL_RECEPTION_FLAG;
/** @hide */
@IntDef(value = {
@@ -337,7 +348,21 @@
DvbcFrontendSettings.TIME_INTERLEAVE_MODE_8_16,
DvbcFrontendSettings.TIME_INTERLEAVE_MODE_128_2,
DvbcFrontendSettings.TIME_INTERLEAVE_MODE_128_3,
- DvbcFrontendSettings.TIME_INTERLEAVE_MODE_128_4})
+ DvbcFrontendSettings.TIME_INTERLEAVE_MODE_128_4,
+ IsdbtFrontendSettings.TIME_INTERLEAVE_MODE_UNDEFINED,
+ IsdbtFrontendSettings.TIME_INTERLEAVE_MODE_AUTO,
+ IsdbtFrontendSettings.TIME_INTERLEAVE_MODE_1_0,
+ IsdbtFrontendSettings.TIME_INTERLEAVE_MODE_1_4,
+ IsdbtFrontendSettings.TIME_INTERLEAVE_MODE_1_8,
+ IsdbtFrontendSettings.TIME_INTERLEAVE_MODE_1_16,
+ IsdbtFrontendSettings.TIME_INTERLEAVE_MODE_2_0,
+ IsdbtFrontendSettings.TIME_INTERLEAVE_MODE_2_2,
+ IsdbtFrontendSettings.TIME_INTERLEAVE_MODE_2_4,
+ IsdbtFrontendSettings.TIME_INTERLEAVE_MODE_2_8,
+ IsdbtFrontendSettings.TIME_INTERLEAVE_MODE_3_0,
+ IsdbtFrontendSettings.TIME_INTERLEAVE_MODE_3_1,
+ IsdbtFrontendSettings.TIME_INTERLEAVE_MODE_3_2,
+ IsdbtFrontendSettings.TIME_INTERLEAVE_MODE_3_4})
@Retention(RetentionPolicy.SOURCE)
public @interface FrontendInterleaveMode {}
@@ -466,7 +491,8 @@
private Boolean mIsMisoEnabled;
private Boolean mIsLinear;
private Boolean mIsShortFrames;
-
+ private Integer mIsdbtMode;
+ private Integer mIsdbtPartialReceptionFlag;
// Constructed and fields set by JNI code.
private FrontendStatus() {
@@ -941,6 +967,40 @@
}
/**
+ * Gets ISDB-T mode.
+ *
+ * <p>This query is only supported by Tuner HAL 2.0 or higher. Unsupported version or if HAL
+ * doesn't return ISDB-T mode status will throw IllegalStateException. Use
+ * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ */
+ @IsdbtFrontendSettings.Mode
+ public int getIsdbtMode() {
+ TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_2_0, "IsdbtMode status");
+ if (mIsdbtMode == null) {
+ throw new IllegalStateException("IsdbtMode status is empty");
+ }
+ return mIsdbtMode;
+ }
+
+ /**
+ * Gets ISDB-T partial reception flag.
+ *
+ * <p>This query is only supported by Tuner HAL 2.0 or higher. Unsupported version or if HAL
+ * doesn't return partial reception flag status will throw IllegalStateException. Use
+ * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ */
+ @IsdbtFrontendSettings.PartialReceptionFlag
+ public int getIsdbtPartialReceptionFlag() {
+ TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_2_0, "IsdbtPartialReceptionFlag status");
+ if (mIsdbtPartialReceptionFlag == null) {
+ throw new IllegalStateException("IsdbtPartialReceptionFlag status is empty");
+ }
+ return mIsdbtPartialReceptionFlag;
+ }
+
+ /**
* Information of each tuning Physical Layer Pipes.
*/
public static class Atsc3PlpTuningInfo {
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendCapabilities.java
index ffebc5a..768dedb 100644
--- a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendCapabilities.java
+++ b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendCapabilities.java
@@ -17,6 +17,7 @@
package android.media.tv.tuner.frontend;
import android.annotation.SystemApi;
+import android.media.tv.tuner.TunerVersionChecker;
/**
* ISDBT Capabilities.
@@ -30,14 +31,21 @@
private final int mModulationCap;
private final int mCodeRateCap;
private final int mGuardIntervalCap;
+ private final int mTimeInterleaveCap;
+ private final boolean mIsSegmentAutoSupported;
+ private final boolean mIsFullSegmentSupported;
private IsdbtFrontendCapabilities(int modeCap, int bandwidthCap, int modulationCap,
- int codeRateCap, int guardIntervalCap) {
+ int codeRateCap, int guardIntervalCap, int timeInterleaveCap,
+ boolean isSegmentAutoSupported, boolean isFullSegmentSupported) {
mModeCap = modeCap;
mBandwidthCap = bandwidthCap;
mModulationCap = modulationCap;
mCodeRateCap = codeRateCap;
mGuardIntervalCap = guardIntervalCap;
+ mTimeInterleaveCap = timeInterleaveCap;
+ mIsSegmentAutoSupported = isSegmentAutoSupported;
+ mIsFullSegmentSupported = isFullSegmentSupported;
}
/**
@@ -75,4 +83,35 @@
public int getGuardIntervalCapability() {
return mGuardIntervalCap;
}
+ /**
+ * Gets time interleave mode capability.
+ *
+ * <p>This query is only supported by Tuner HAL 2.0 or higher. Unsupported version will
+ * return {@link IsdbtFrontendSettings#TIME_INTERLEAVE_MODE_UNDEFINED}.
+ * Use {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ */
+ @IsdbtFrontendSettings.TimeInterleaveMode
+ public int getTimeInterleaveModeCapability() {
+ return mTimeInterleaveCap;
+ }
+ /**
+ * If auto segment is supported or not.
+ *
+ * <p>This query is only supported by Tuner HAL 2.0 or higher. Unsupported version will
+ * return false.
+ * Use {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ */
+ public boolean isSegmentAutoSupported() {
+ return mIsSegmentAutoSupported;
+ }
+ /**
+ * If full segment is supported or not.
+ *
+ * <p>This query is only supported by Tuner HAL 2.0 or higher. Unsupported version will
+ * return false.
+ * Use {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ */
+ public boolean isFullSegmentSupported() {
+ return mIsFullSegmentSupported;
+ }
}
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
index d34b643..1090f33 100644
--- a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
@@ -19,12 +19,16 @@
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.hardware.tv.tuner.FrontendIsdbtBandwidth;
import android.hardware.tv.tuner.FrontendIsdbtMode;
import android.hardware.tv.tuner.FrontendIsdbtModulation;
+import android.hardware.tv.tuner.FrontendIsdbtPartialReceptionFlag;
+import android.hardware.tv.tuner.FrontendIsdbtTimeInterleaveMode;
+import android.media.tv.tuner.TunerVersionChecker;
import android.media.tv.tuner.frontend.DvbtFrontendSettings.CodeRate;
-
+import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -127,30 +131,145 @@
*/
public static final int BANDWIDTH_6MHZ = FrontendIsdbtBandwidth.BANDWIDTH_6MHZ;
- private final int mModulation;
+ /** @hide */
+ @IntDef(flag = true, prefix = "PARTIAL_RECEPTION_FLAG_",
+ value = {PARTIAL_RECEPTION_FLAG_UNDEFINED, PARTIAL_RECEPTION_FLAG_FALSE,
+ PARTIAL_RECEPTION_FLAG_TRUE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PartialReceptionFlag {}
+
+ /**
+ * Partial Reception Flag undefined.
+ */
+ public static final int PARTIAL_RECEPTION_FLAG_UNDEFINED =
+ FrontendIsdbtPartialReceptionFlag.UNDEFINED;
+ /**
+ * Partial Reception Flag false.
+ */
+ public static final int PARTIAL_RECEPTION_FLAG_FALSE = FrontendIsdbtPartialReceptionFlag.FALSE;
+ /**
+ * Partial Reception Flag true.
+ */
+ public static final int PARTIAL_RECEPTION_FLAG_TRUE = FrontendIsdbtPartialReceptionFlag.TRUE;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = "TIME_INTERLEAVE_MODE_",
+ value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO,
+ TIME_INTERLEAVE_MODE_1_0, TIME_INTERLEAVE_MODE_1_4, TIME_INTERLEAVE_MODE_1_8,
+ TIME_INTERLEAVE_MODE_1_16, TIME_INTERLEAVE_MODE_2_0, TIME_INTERLEAVE_MODE_2_2,
+ TIME_INTERLEAVE_MODE_2_4, TIME_INTERLEAVE_MODE_2_8, TIME_INTERLEAVE_MODE_3_0,
+ TIME_INTERLEAVE_MODE_3_1, TIME_INTERLEAVE_MODE_3_2, TIME_INTERLEAVE_MODE_3_4})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TimeInterleaveMode {}
+
+ /**
+ * Time Interleave Mode undefined.
+ */
+ public static final int TIME_INTERLEAVE_MODE_UNDEFINED =
+ FrontendIsdbtTimeInterleaveMode.UNDEFINED;
+ /**
+ * Hardware is able to detect and set time interleave mode automatically
+ */
+ public static final int TIME_INTERLEAVE_MODE_AUTO = FrontendIsdbtTimeInterleaveMode.AUTO;
+ /**
+ * Time Interleave Mode 1: 0.
+ */
+ public static final int TIME_INTERLEAVE_MODE_1_0 =
+ FrontendIsdbtTimeInterleaveMode.INTERLEAVE_1_0;
+ /**
+ * Time Interleave Mode 1: 4.
+ */
+ public static final int TIME_INTERLEAVE_MODE_1_4 =
+ FrontendIsdbtTimeInterleaveMode.INTERLEAVE_1_4;
+ /**
+ * Time Interleave Mode 1: 8.
+ */
+ public static final int TIME_INTERLEAVE_MODE_1_8 =
+ FrontendIsdbtTimeInterleaveMode.INTERLEAVE_1_8;
+ /**
+ * Time Interleave Mode 1: 16.
+ */
+ public static final int TIME_INTERLEAVE_MODE_1_16 =
+ FrontendIsdbtTimeInterleaveMode.INTERLEAVE_1_16;
+ /**
+ * Time Interleave Mode 2: 0.
+ */
+ public static final int TIME_INTERLEAVE_MODE_2_0 =
+ FrontendIsdbtTimeInterleaveMode.INTERLEAVE_2_0;
+ /**
+ * Time Interleave Mode 2: 2.
+ */
+ public static final int TIME_INTERLEAVE_MODE_2_2 =
+ FrontendIsdbtTimeInterleaveMode.INTERLEAVE_2_2;
+ /**
+ * Time Interleave Mode 2: 4.
+ */
+ public static final int TIME_INTERLEAVE_MODE_2_4 =
+ FrontendIsdbtTimeInterleaveMode.INTERLEAVE_2_4;
+ /**
+ * Time Interleave Mode 2: 8.
+ */
+ public static final int TIME_INTERLEAVE_MODE_2_8 =
+ FrontendIsdbtTimeInterleaveMode.INTERLEAVE_2_8;
+ /**
+ * Time Interleave Mode 3: 0.
+ */
+ public static final int TIME_INTERLEAVE_MODE_3_0 =
+ FrontendIsdbtTimeInterleaveMode.INTERLEAVE_3_0;
+ /**
+ * Time Interleave Mode 3: 1.
+ */
+ public static final int TIME_INTERLEAVE_MODE_3_1 =
+ FrontendIsdbtTimeInterleaveMode.INTERLEAVE_3_1;
+ /**
+ * Time Interleave Mode 3: 2.
+ */
+ public static final int TIME_INTERLEAVE_MODE_3_2 =
+ FrontendIsdbtTimeInterleaveMode.INTERLEAVE_3_2;
+ /**
+ * Time Interleave Mode 3: 4.
+ */
+ public static final int TIME_INTERLEAVE_MODE_3_4 =
+ FrontendIsdbtTimeInterleaveMode.INTERLEAVE_3_4;
+
private final int mBandwidth;
private final int mMode;
- private final int mCodeRate;
private final int mGuardInterval;
private final int mServiceAreaId;
+ private final IsdbtLayerSettings[] mLayerSettings;
+ private final int mPartialReceptionFlag;
+ private static final String TAG = "IsdbtFrontendSettings";
- private IsdbtFrontendSettings(long frequency, int modulation, int bandwidth, int mode,
- int codeRate, int guardInterval, int serviceAreaId) {
+ private IsdbtFrontendSettings(long frequency, int bandwidth, int mode, int guardInterval,
+ int serviceAreaId, IsdbtLayerSettings[] layerSettings, int partialReceptionFlag) {
super(frequency);
- mModulation = modulation;
mBandwidth = bandwidth;
mMode = mode;
- mCodeRate = codeRate;
mGuardInterval = guardInterval;
mServiceAreaId = serviceAreaId;
+ mLayerSettings = new IsdbtLayerSettings[layerSettings.length];
+ for (int i = 0; i < layerSettings.length; i++) {
+ mLayerSettings[i] = layerSettings[i];
+ }
+ mPartialReceptionFlag = partialReceptionFlag;
}
/**
* Gets Modulation.
+ *
+ * <p>This query is only supported in Tuner 1.1 or lowner version. Unsupported version will
+ * return {@link MODULATION_UNDEFINED}.
+ * Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
+ * @deprecated Use {@link #getLayerSettings()} and {@link IsdbtLayerSettings#getModulation()}
+ * instead.
*/
+ @Deprecated
@Modulation
public int getModulation() {
- return mModulation;
+ if (TunerVersionChecker.isHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_2_0)) {
+ return MODULATION_UNDEFINED;
+ }
+ return mLayerSettings.length > 0 ? mLayerSettings[0].getModulation() : MODULATION_UNDEFINED;
}
/**
* Gets Bandwidth.
@@ -168,10 +287,21 @@
}
/**
* Gets Code rate.
+ *
+ * <p>This query is only supported in Tuner 1.1 or lowner version. Unsupported version will
+ * return {@link DvbtFrontendSettings#CODERATE_UNDEFINED}.
+ * Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
+ * @deprecated Use {@link #getLayerSettings()} and {@link IsdbtLayerSettings#getCodeRate()}
+ * instead.
*/
+ @Deprecated
@CodeRate
public int getCodeRate() {
- return mCodeRate;
+ if (TunerVersionChecker.isHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_2_0)) {
+ return DvbtFrontendSettings.CODERATE_UNDEFINED;
+ }
+ return mLayerSettings.length > 0 ? mLayerSettings[0].getCodeRate()
+ : DvbtFrontendSettings.CODERATE_UNDEFINED;
}
/**
* Gets Guard Interval.
@@ -186,6 +316,29 @@
public int getServiceAreaId() {
return mServiceAreaId;
}
+ /**
+ * Gets ISDB-T Layer Settings.
+ *
+ * <p>This query is only supported in Tuner 2.0 or higher version. Unsupported version will
+ * return an empty array.
+ * Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
+ */
+ @SuppressLint("ArrayReturn")
+ @NonNull
+ public IsdbtLayerSettings[] getLayerSettings() {
+ return mLayerSettings;
+ }
+ /**
+ * Gets ISDB-T Partial Reception Flag.
+ *
+ * <p>This query is only supported in Tuner 2.0 or higher version. Unsupported version will
+ * return {@link PARTIALRECEPTIONFLAG_UNDEFINED}.
+ * Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
+ */
+ @PartialReceptionFlag
+ public int getPartialReceptionFlag() {
+ return mPartialReceptionFlag;
+ }
/**
* Creates a builder for {@link IsdbtFrontendSettings}.
@@ -200,12 +353,12 @@
*/
public static class Builder {
private long mFrequency = 0;
- private int mModulation = MODULATION_UNDEFINED;
private int mBandwidth = BANDWIDTH_UNDEFINED;
private int mMode = MODE_UNDEFINED;
- private int mCodeRate = DvbtFrontendSettings.CODERATE_UNDEFINED;
private int mGuardInterval = DvbtFrontendSettings.GUARD_INTERVAL_UNDEFINED;
private int mServiceAreaId = 0;
+ private IsdbtLayerSettings[] mLayerSettings = {};
+ private int mPartialReceptionFlag = PARTIAL_RECEPTION_FLAG_UNDEFINED;
private Builder() {
}
@@ -238,11 +391,28 @@
/**
* Sets Modulation.
*
+ * <p>This configuration is only supported in Tuner 1.1 or lowner version. Unsupported
+ * version will cause no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the
+ * version information.
+ *
* <p>Default value is {@link #MODULATION_UNDEFINED}.
*/
+ @Deprecated
@NonNull
public Builder setModulation(@Modulation int modulation) {
- mModulation = modulation;
+ if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_2_0, "setModulation")) {
+ Log.d(TAG, "Use IsdbtLayerSettings on HAL 2.0 or higher");
+ } else {
+ IsdbtLayerSettings.Builder layerBuilder = IsdbtLayerSettings.builder();
+ layerBuilder.setModulation(modulation);
+ if (mLayerSettings.length == 0) {
+ mLayerSettings = new IsdbtLayerSettings[1];
+ } else {
+ layerBuilder.setCodeRate(mLayerSettings[0].getCodeRate());
+ }
+ mLayerSettings[0] = layerBuilder.build();
+ }
return this;
}
/**
@@ -268,11 +438,28 @@
/**
* Sets Code rate.
*
+ * <p>This configuration is only supported in Tuner 1.1 or lowner version. Unsupported
+ * version will cause no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the
+ * version information.
+ *
* <p>Default value is {@link DvbtFrontendSettings#CODERATE_UNDEFINED}.
*/
+ @Deprecated
@NonNull
public Builder setCodeRate(@DvbtFrontendSettings.CodeRate int codeRate) {
- mCodeRate = codeRate;
+ if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_2_0, "setModulation")) {
+ Log.d(TAG, "Use IsdbtLayerSettings on HAL 2.0 or higher");
+ } else {
+ IsdbtLayerSettings.Builder layerBuilder = IsdbtLayerSettings.builder();
+ layerBuilder.setCodeRate(codeRate);
+ if (mLayerSettings.length == 0) {
+ mLayerSettings = new IsdbtLayerSettings[1];
+ } else {
+ layerBuilder.setModulation(mLayerSettings[0].getModulation());
+ }
+ mLayerSettings[0] = layerBuilder.build();
+ }
return this;
}
/**
@@ -295,14 +482,52 @@
mServiceAreaId = serviceAreaId;
return this;
}
+ /**
+ * Sets ISDB-T Layer Settings.
+ *
+ * <p>This configuration is only supported in Tuner 2.0 or higher version. Unsupported
+ * version will cause no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the
+ * version information.
+ *
+ * <p>Default value is an empty array.
+ */
+ @NonNull
+ public Builder setLayerSettings(
+ @SuppressLint("ArrayReturn") @NonNull IsdbtLayerSettings[] layerSettings) {
+ if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_2_0, "setLayerSettings")) {
+ mLayerSettings = new IsdbtLayerSettings[layerSettings.length];
+ for (int i = 0; i < layerSettings.length; i++) {
+ mLayerSettings[i] = layerSettings[i];
+ }
+ }
+ return this;
+ }
+ /**
+ * Sets ISDB-T Partial Reception Flag.
+ *
+ * <p>This configuration is only supported in Tuner 2.0 or higher version. Unsupported
+ * version will cause no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the
+ * version information.
+ *
+ * <p>Default value is {@link PARTIALRECEPTIONFLAG_UNDEFINED}.
+ */
+ @NonNull
+ public Builder setPartialReceptionFlag(@PartialReceptionFlag int flag) {
+ if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_2_0, "setPartialReceptionFlag")) {
+ mPartialReceptionFlag = flag;
+ }
+ return this;
+ }
/**
* Builds a {@link IsdbtFrontendSettings} object.
*/
@NonNull
public IsdbtFrontendSettings build() {
- return new IsdbtFrontendSettings(mFrequency, mModulation, mBandwidth, mMode, mCodeRate,
- mGuardInterval, mServiceAreaId);
+ return new IsdbtFrontendSettings(mFrequency, mBandwidth, mMode, mGuardInterval,
+ mServiceAreaId, mLayerSettings, mPartialReceptionFlag);
}
}
@@ -310,4 +535,123 @@
public int getType() {
return FrontendSettings.TYPE_ISDBT;
}
+
+ /**
+ * Layer Settings for ISDB-T Frontend.
+ *
+ * <p>Layer Settings is only supported in Tuner 2.0 or higher version. Use
+ * {@link TunerVersionChecker#getTunerVersion()} to get the version information.
+ */
+ public static final class IsdbtLayerSettings {
+ private final int mModulation;
+ private final int mTimeInterleaveMode;
+ private final int mCodeRate;
+ private final int mNumOfSegment;
+
+ private IsdbtLayerSettings(
+ int modulation, int timeInterleaveMode, int codeRate, int numOfSegment) {
+ mModulation = modulation;
+ mTimeInterleaveMode = timeInterleaveMode;
+ mCodeRate = codeRate;
+ mNumOfSegment = numOfSegment;
+ }
+
+ /**
+ * Gets Modulation.
+ */
+ @Modulation
+ public int getModulation() {
+ return mModulation;
+ }
+ /**
+ * Gets Time Interleave Mode.
+ */
+ @TimeInterleaveMode
+ public int getTimeInterleaveMode() {
+ return mTimeInterleaveMode;
+ }
+ /**
+ * Gets Code rate.
+ */
+ @CodeRate
+ public int getCodeRate() {
+ return mCodeRate;
+ }
+ /**
+ * Gets Number of Segment.
+ */
+ @IntRange(from = 0, to = 0xff)
+ public int getNumOfSegment() {
+ return mNumOfSegment;
+ }
+
+ /**
+ * Creates a builder for {@link IsdbtLayerSettings}.
+ */
+ @NonNull
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder for {@link IsdbtLayerSettings}.
+ */
+ public static final class Builder {
+ private int mModulation = MODULATION_UNDEFINED;
+ private int mTimeInterleaveMode = TIME_INTERLEAVE_MODE_UNDEFINED;
+ private int mCodeRate = DvbtFrontendSettings.CODERATE_UNDEFINED;
+ private int mNumOfSegment = 0;
+
+ private Builder() {}
+
+ /**
+ * Sets modulation.
+ *
+ * <p>Default value is {@link #MODULATION_UNDEFINED}.
+ */
+ @NonNull
+ public Builder setModulation(@Modulation int modulation) {
+ mModulation = modulation;
+ return this;
+ }
+ /**
+ * Sets time interleave mode.
+ *
+ * <p>Default value is {@link #TIME_INTERLEAVE_MODE_UNDEFINED}.
+ */
+ @NonNull
+ public Builder setTimeInterleaveMode(@TimeInterleaveMode int mode) {
+ mTimeInterleaveMode = mode;
+ return this;
+ }
+ /**
+ * Sets code rate.
+ */
+ @NonNull
+ public Builder setCodeRate(@DvbtFrontendSettings.CodeRate int codeRate) {
+ mCodeRate = codeRate;
+ return this;
+ }
+ /**
+ * Sets number of segment.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ @IntRange(from = 0, to = 0xff)
+ public Builder setNumOfSegment(int numOfSegment) {
+ mNumOfSegment = numOfSegment;
+ return this;
+ }
+
+ /**
+ * Builds a {@link IsdbtLayerSettings} object.
+ */
+ @NonNull
+ public IsdbtLayerSettings build() {
+ return new IsdbtLayerSettings(
+ mModulation, mTimeInterleaveMode, mCodeRate, mNumOfSegment);
+ }
+ }
+ }
}
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 2636ab2..8dcdc98 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -953,7 +953,7 @@
Parcel* parcel = parcelForJavaObject(env, jAttributionSource);
android::content::AttributionSourceState attributionSource;
attributionSource.readFromParcel(parcel);
- sp<MediaPlayer> mp = new MediaPlayer(attributionSource);
+ sp<MediaPlayer> mp = sp<MediaPlayer>::make(attributionSource);
if (mp == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 35f18751..24eea30 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -123,9 +123,12 @@
#include <aidl/android/hardware/tv/tuner/FrontendIsdbtBandwidth.h>
#include <aidl/android/hardware/tv/tuner/FrontendIsdbtCoderate.h>
#include <aidl/android/hardware/tv/tuner/FrontendIsdbtGuardInterval.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIsdbtLayerSettings.h>
#include <aidl/android/hardware/tv/tuner/FrontendIsdbtMode.h>
#include <aidl/android/hardware/tv/tuner/FrontendIsdbtModulation.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIsdbtPartialReceptionFlag.h>
#include <aidl/android/hardware/tv/tuner/FrontendIsdbtSettings.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIsdbtTimeInterleaveMode.h>
#include <aidl/android/hardware/tv/tuner/FrontendModulation.h>
#include <aidl/android/hardware/tv/tuner/FrontendModulationStatus.h>
#include <aidl/android/hardware/tv/tuner/FrontendRollOff.h>
@@ -268,9 +271,12 @@
using ::aidl::android::hardware::tv::tuner::FrontendIsdbtBandwidth;
using ::aidl::android::hardware::tv::tuner::FrontendIsdbtCoderate;
using ::aidl::android::hardware::tv::tuner::FrontendIsdbtGuardInterval;
+using ::aidl::android::hardware::tv::tuner::FrontendIsdbtLayerSettings;
using ::aidl::android::hardware::tv::tuner::FrontendIsdbtMode;
using ::aidl::android::hardware::tv::tuner::FrontendIsdbtModulation;
+using ::aidl::android::hardware::tv::tuner::FrontendIsdbtPartialReceptionFlag;
using ::aidl::android::hardware::tv::tuner::FrontendIsdbtSettings;
+using ::aidl::android::hardware::tv::tuner::FrontendIsdbtTimeInterleaveMode;
using ::aidl::android::hardware::tv::tuner::FrontendModulation;
using ::aidl::android::hardware::tv::tuner::FrontendModulationStatus;
using ::aidl::android::hardware::tv::tuner::FrontendRollOff;
@@ -1355,16 +1361,19 @@
jobject JTuner::getIsdbtFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IsdbtFrontendCapabilities");
- jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIII)V");
+ jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIIIIZZ)V");
jint modeCap = caps.get<FrontendCapabilities::Tag::isdbtCaps>().modeCap;
jint bandwidthCap = caps.get<FrontendCapabilities::Tag::isdbtCaps>().bandwidthCap;
jint modulationCap = caps.get<FrontendCapabilities::Tag::isdbtCaps>().modulationCap;
jint coderateCap = caps.get<FrontendCapabilities::Tag::isdbtCaps>().coderateCap;
jint guardIntervalCap = caps.get<FrontendCapabilities::Tag::isdbtCaps>().guardIntervalCap;
+ jint timeInterleaveCap = caps.get<FrontendCapabilities::Tag::isdbtCaps>().timeInterleaveCap;
+ jboolean isSegmentAuto = caps.get<FrontendCapabilities::Tag::isdbtCaps>().isSegmentAuto;
+ jboolean isFullSegment = caps.get<FrontendCapabilities::Tag::isdbtCaps>().isFullSegment;
return env->NewObject(clazz, capsInit, modeCap, bandwidthCap, modulationCap, coderateCap,
- guardIntervalCap);
+ guardIntervalCap, timeInterleaveCap, isSegmentAuto, isFullSegment);
}
jobject JTuner::getDtmbFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) {
@@ -2336,6 +2345,13 @@
valid = true;
break;
}
+ case FrontendInterleaveMode::Tag::isdbt: {
+ in[0] = static_cast<jint>(
+ interleaving.get<FrontendInterleaveMode::Tag::isdbt>());
+ env->SetIntArrayRegion(valObj, i, 1, in);
+ valid = true;
+ break;
+ }
default:
break;
}
@@ -2414,6 +2430,22 @@
env->SetObjectField(statusObj, field, newBooleanObj);
break;
}
+ case FrontendStatus::Tag::isdbtMode: {
+ jfieldID field = env->GetFieldID(clazz, "mIsdbtMode", "Ljava/lang/Integer;");
+ jobject newIntegerObj =
+ env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::isdbtMode>());
+ env->SetObjectField(statusObj, field, newIntegerObj);
+ break;
+ }
+ case FrontendStatus::Tag::partialReceptionFlag: {
+ jfieldID field =
+ env->GetFieldID(clazz, "mIsdbtPartialReceptionFlag", "Ljava/lang/Integer;");
+ jobject newIntegerObj =
+ env->NewObject(intClazz, initInt,
+ s.get<FrontendStatus::Tag::partialReceptionFlag>());
+ env->SetObjectField(statusObj, field, newIntegerObj);
+ break;
+ }
default: {
break;
}
@@ -2879,35 +2911,55 @@
int64_t endFreq = getFrontendSettingsEndFreq(env, settings);
FrontendSpectralInversion inversion = getFrontendSettingsSpectralInversion(env, settings);
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IsdbtFrontendSettings");
- FrontendIsdbtModulation modulation =
- static_cast<FrontendIsdbtModulation>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mModulation", "I")));
FrontendIsdbtBandwidth bandwidth =
static_cast<FrontendIsdbtBandwidth>(
env->GetIntField(settings, env->GetFieldID(clazz, "mBandwidth", "I")));
- FrontendIsdbtMode mode =
- static_cast<FrontendIsdbtMode>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mMode", "I")));
- FrontendIsdbtCoderate coderate =
- static_cast<FrontendIsdbtCoderate>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mCodeRate", "I")));
+ FrontendIsdbtMode mode = static_cast<FrontendIsdbtMode>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mMode", "I")));
FrontendIsdbtGuardInterval guardInterval =
static_cast<FrontendIsdbtGuardInterval>(
env->GetIntField(settings, env->GetFieldID(clazz, "mGuardInterval", "I")));
int32_t serviceAreaId =
env->GetIntField(settings, env->GetFieldID(clazz, "mServiceAreaId", "I"));
+ FrontendIsdbtPartialReceptionFlag partialReceptionFlag =
+ static_cast<FrontendIsdbtPartialReceptionFlag>(
+ env->GetIntField(settings,
+ env->GetFieldID(clazz, "mPartialReceptionFlag", "I")));
FrontendIsdbtSettings frontendIsdbtSettings{
.frequency = freq,
.endFrequency = endFreq,
- .modulation = modulation,
.bandwidth = bandwidth,
.mode = mode,
- .coderate = coderate,
.guardInterval = guardInterval,
.serviceAreaId = serviceAreaId,
.inversion = inversion,
+ .partialReceptionFlag = partialReceptionFlag,
};
+
+ jobjectArray layerSettings = reinterpret_cast<jobjectArray>(
+ env->GetObjectField(settings,
+ env->GetFieldID(clazz, "mLayerSettings",
+ "[Landroid/media/tv/tuner/frontend/"
+ "IsdbtFrontendSettings$IsdbtLayerSettings;")));
+ int len = env->GetArrayLength(layerSettings);
+ jclass layerClazz = env->FindClass(
+ "android/media/tv/tuner/frontend/IsdbtFrontendSettings$IsdbtLayerSettings");
+ frontendIsdbtSettings.layerSettings.resize(len);
+ for (int i = 0; i < len; i++) {
+ jobject layer = env->GetObjectArrayElement(layerSettings, i);
+ frontendIsdbtSettings.layerSettings[i].modulation = static_cast<FrontendIsdbtModulation>(
+ env->GetIntField(layer, env->GetFieldID(layerClazz, "mModulation", "I")));
+ frontendIsdbtSettings.layerSettings[i].timeInterleave =
+ static_cast<FrontendIsdbtTimeInterleaveMode>(
+ env->GetIntField(layer,
+ env->GetFieldID(layerClazz, "mTimeInterleaveMode", "I")));
+ frontendIsdbtSettings.layerSettings[i].coderate = static_cast<FrontendIsdbtCoderate>(
+ env->GetIntField(layer, env->GetFieldID(layerClazz, "mCodeRate", "I")));
+ frontendIsdbtSettings.layerSettings[i].numOfSegment =
+ env->GetIntField(layer, env->GetFieldID(layerClazz, "mNumOfSegment", "I"));
+ }
+
frontendSettings.set<FrontendSettings::Tag::isdbt>(frontendIsdbtSettings);
return frontendSettings;
}
diff --git a/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java b/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java
index 5f64d20..414de89 100644
--- a/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java
+++ b/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java
@@ -67,6 +67,19 @@
}
@Test
+ public void testAudioChannelConversionApiOutputMask() {
+ final AudioChannelLayout aidl = AudioChannelLayout.layoutMask(
+ AudioChannelLayout.CHANNEL_FRONT_LEFT | AudioChannelLayout.CHANNEL_FRONT_RIGHT
+ | AudioChannelLayout.CHANNEL_FRONT_WIDE_LEFT
+ | AudioChannelLayout.CHANNEL_FRONT_WIDE_RIGHT);
+ final int api = AidlConversion.aidl2api_AudioChannelLayout_AudioFormatChannelMask(
+ aidl, false /*isInput*/);
+ assertEquals(AudioFormat.CHANNEL_OUT_FRONT_LEFT | AudioFormat.CHANNEL_OUT_FRONT_RIGHT
+ | AudioFormat.CHANNEL_OUT_FRONT_WIDE_LEFT
+ | AudioFormat.CHANNEL_OUT_FRONT_WIDE_RIGHT, api);
+ }
+
+ @Test
public void testAudioChannelConversionApiInput() {
final AudioChannelLayout aidl = AudioChannelLayout.layoutMask(
AudioChannelLayout.LAYOUT_MONO);
@@ -76,6 +89,20 @@
}
@Test
+ public void testAudioChannelConversionApiInputMask() {
+ final AudioChannelLayout aidl = AudioChannelLayout.layoutMask(
+ AudioChannelLayout.CHANNEL_FRONT_LEFT | AudioChannelLayout.CHANNEL_FRONT_RIGHT
+ | AudioChannelLayout.CHANNEL_TOP_SIDE_LEFT
+ | AudioChannelLayout.CHANNEL_TOP_SIDE_RIGHT);
+ final int api = AidlConversion.aidl2api_AudioChannelLayout_AudioFormatChannelMask(
+ aidl, true /*isInput*/);
+ assertEquals(AudioFormat.CHANNEL_IN_LEFT | AudioFormat.CHANNEL_IN_RIGHT
+ // | AudioFormat.CHANNEL_IN_TOP_LEFT | AudioFormat.CHANNEL_IN_TOP_RIGHT,
+ | 0x200000 | 0x400000, // TODO: Replace with names when revealed.
+ api);
+ }
+
+ @Test
public void testAudioChannelConversionApiIndex() {
final AudioChannelLayout aidl = AudioChannelLayout.indexMask(
AudioChannelLayout.INDEX_MASK_1);
diff --git a/native/android/input.cpp b/native/android/input.cpp
index 8eeb955..4de2c23 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -329,3 +329,7 @@
InputEvent* e = static_cast<InputEvent*>(event);
iq->finishEvent(e, handled != 0);
}
+
+AInputQueue* AInputQueue_fromJava(jobject inputQueue) {
+ return android::android_view_InputQueue_getNativePtr(inputQueue);
+}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 29c1d8c..f00eef2 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -91,6 +91,7 @@
AInputQueue_attachLooper;
AInputQueue_detachLooper;
AInputQueue_finishEvent;
+ AInputQueue_fromJava; # introduced=Tiramisu
AInputQueue_getEvent;
AInputQueue_hasEvents;
AInputQueue_preDispatchEvent;
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index edbfd2a..a5168cc 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -78,6 +78,7 @@
getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
sInstance = this;
+ getService().mActivity = this;
String deviceProfile = getRequest().getDeviceProfile();
String profilePrivacyDisclaimer = emptyIfNull(getRequest()
@@ -141,8 +142,6 @@
profileSummary.setVisibility(View.GONE);
}
- getService().mActivity = this;
-
mCancelButton = findViewById(R.id.button_cancel);
mCancelButton.setOnClickListener(v -> cancel());
}
@@ -194,6 +193,7 @@
@Override
protected void onDestroy() {
super.onDestroy();
+ getService().mActivity = null;
if (sInstance == this) {
sInstance = null;
}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
index 4ac217a..9f07317 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
@@ -257,12 +257,8 @@
if (!mIsScanning) return;
mIsScanning = false;
- CompanionDeviceActivity activity = mActivity;
- if (activity != null) {
- if (activity.mDeviceListView != null) {
- activity.mDeviceListView.removeFooterView(activity.mLoadingIndicator);
- }
- mActivity = null;
+ if (mActivity != null && mActivity.mDeviceListView != null) {
+ mActivity.mDeviceListView.removeFooterView(mActivity.mLoadingIndicator);
}
mBluetoothAdapter.cancelDiscovery();
@@ -334,6 +330,7 @@
void onCancel() {
if (DEBUG) Log.i(LOG_TAG, "onCancel()");
+ mActivity = null;
mServiceCallback.cancel(true);
}
diff --git a/packages/SettingsLib/ActivityEmbedding/Android.bp b/packages/SettingsLib/ActivityEmbedding/Android.bp
new file mode 100644
index 0000000..fc82b79
--- /dev/null
+++ b/packages/SettingsLib/ActivityEmbedding/Android.bp
@@ -0,0 +1,21 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+ name: "SettingsLibActivityEmbedding",
+
+ srcs: ["src/**/*.java"],
+
+ static_libs: [
+ "androidx.annotation_annotation",
+ "SettingsLibUtils",
+ ],
+ sdk_version: "system_current",
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/ActivityEmbedding/AndroidManifest.xml b/packages/SettingsLib/ActivityEmbedding/AndroidManifest.xml
new file mode 100644
index 0000000..2e6c405
--- /dev/null
+++ b/packages/SettingsLib/ActivityEmbedding/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.activityembedding">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
new file mode 100644
index 0000000..36c2bda
--- /dev/null
+++ b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
@@ -0,0 +1,51 @@
+/*
+ * 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.settingslib.activityembedding;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+
+import com.android.settingslib.utils.BuildCompatUtils;
+
+/**
+ * An util class collecting all common methods for the embedding activity features.
+ */
+public class ActivityEmbeddingUtils {
+ private static final String ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY =
+ "android.settings.SETTINGS_EMBED_DEEP_LINK_ACTIVITY";
+ private static final String PACKAGE_NAME_SETTINGS = "com.android.settings";
+
+ /**
+ * Whether to support embedding activity feature.
+ */
+ public static boolean isEmbeddingActivityEnabled(Context context) {
+ if (BuildCompatUtils.isAtLeastS()) {
+ final Intent intent = new Intent(ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY);
+ intent.setPackage(PACKAGE_NAME_SETTINGS);
+ final ResolveInfo resolveInfo =
+ context.getPackageManager().resolveActivity(intent, 0 /* flags */);
+ return resolveInfo != null
+ && resolveInfo.activityInfo != null
+ && resolveInfo.activityInfo.enabled;
+ }
+ return false;
+ }
+
+ private ActivityEmbeddingUtils() {
+ }
+}
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index e8ed88f..b266df5 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -48,6 +48,7 @@
"SettingsLibCollapsingToolbarBaseActivity",
"SettingsLibTwoTargetPreference",
"SettingsLibSettingsTransition",
+ "SettingsLibActivityEmbedding",
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java
index 6da249c..c3f845c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java
@@ -59,7 +59,7 @@
// These callbacks run on the main thread.
private final class CoordinatedSetServiceListener implements BluetoothProfile.ServiceListener {
- @RequiresApi(32)
+ @RequiresApi(33)
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (VDBG) {
Log.d(TAG, "Bluetooth service connected");
@@ -233,7 +233,7 @@
return NAME;
}
- @RequiresApi(32)
+ @RequiresApi(33)
protected void finalize() {
if (VDBG) {
Log.d(TAG, "finalize()");
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index a1fba4a..dc109ca 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -16,7 +16,8 @@
package com.android.settingslib.bluetooth;
-import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_ALL;
+import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_AUDIO;
+import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
@@ -29,6 +30,7 @@
import android.util.Log;
import com.android.settingslib.R;
+import com.android.settingslib.Utils;
import java.util.ArrayList;
import java.util.List;
@@ -162,9 +164,12 @@
if (mBluetoothAdapter == null) {
return false;
}
+ int profiles = Utils.isAudioModeOngoingCall(mContext)
+ ? ACTIVE_DEVICE_PHONE_CALL
+ : ACTIVE_DEVICE_AUDIO;
return device == null
- ? mBluetoothAdapter.removeActiveDevice(ACTIVE_DEVICE_ALL)
- : mBluetoothAdapter.setActiveDevice(device, ACTIVE_DEVICE_ALL);
+ ? mBluetoothAdapter.removeActiveDevice(profiles)
+ : mBluetoothAdapter.setActiveDevice(device, profiles);
}
public List<BluetoothDevice> getActiveDevices() {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index cb41743..1b62d1a 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -130,7 +130,7 @@
mContext = InstrumentationRegistry.getTargetContext();
mMaxSignalLevel = mContext.getSystemService(WifiManager.class).getMaxSignalLevel();
mWifiInfo = new WifiInfo();
- mWifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(TEST_SSID));
+ mWifiInfo.setSSID(WifiSsid.fromString(TEST_SSID));
mWifiInfo.setBSSID(TEST_BSSID);
mScanResults = buildScanResultCache(TEST_SSID);
mRoamingScans = buildScanResultCache(ROAMING_SSID);
@@ -318,7 +318,7 @@
new NetworkInfo(ConnectivityManager.TYPE_WIFI, 2, "WIFI", "WIFI_SUBTYPE");
AccessPoint accessPoint = new AccessPoint(mContext, configuration);
WifiInfo wifiInfo = new WifiInfo();
- wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(configuration.SSID));
+ wifiInfo.setSSID(WifiSsid.fromString(configuration.SSID));
wifiInfo.setBSSID(configuration.BSSID);
wifiInfo.setNetworkId(configuration.networkId);
accessPoint.update(configuration, wifiInfo, networkInfo);
@@ -334,7 +334,7 @@
new NetworkInfo(ConnectivityManager.TYPE_WIFI, 2, "WIFI", "WIFI_SUBTYPE");
AccessPoint accessPoint = new AccessPoint(mContext, configuration);
WifiInfo wifiInfo = new WifiInfo();
- wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(configuration.SSID));
+ wifiInfo.setSSID(WifiSsid.fromString(configuration.SSID));
wifiInfo.setBSSID(configuration.BSSID);
wifiInfo.setNetworkId(configuration.networkId);
wifiInfo.setMeteredHint(true);
@@ -367,7 +367,7 @@
new NetworkInfo(ConnectivityManager.TYPE_WIFI, 2, "WIFI", "WIFI_SUBTYPE");
AccessPoint accessPoint = new AccessPoint(mContext, configuration);
WifiInfo wifiInfo = new WifiInfo();
- wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(configuration.SSID));
+ wifiInfo.setSSID(WifiSsid.fromString(configuration.SSID));
wifiInfo.setBSSID(configuration.BSSID);
wifiInfo.setNetworkId(configuration.networkId);
accessPoint.update(configuration, wifiInfo, networkInfo);
@@ -518,7 +518,7 @@
final String connectedViaAppResourceString = "Connected via ";
WifiInfo wifiInfo = new WifiInfo();
- wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(TEST_SSID));
+ wifiInfo.setSSID(WifiSsid.fromString(TEST_SSID));
wifiInfo.setEphemeral(true);
wifiInfo.setRequestingPackageName(appPackageName);
wifiInfo.setRssi(rssi);
@@ -585,7 +585,7 @@
private WifiConfiguration createWifiConfiguration() {
WifiConfiguration configuration = new WifiConfiguration();
configuration.BSSID = "bssid";
- configuration.SSID = "ssid";
+ configuration.SSID = "\"ssid\"";
configuration.networkId = 123;
return configuration;
}
@@ -1024,7 +1024,7 @@
WifiInfo info = new WifiInfo();
info.setRssi(DEFAULT_RSSI);
- info.setSSID(WifiSsid.createFromAsciiEncoded(TEST_SSID));
+ info.setSSID(WifiSsid.fromString(TEST_SSID));
info.setBSSID(bssid);
info.setNetworkId(NETWORK_ID);
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index b0a9136..e7533bc1 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -124,7 +124,7 @@
private static final int CONNECTED_RSSI = -50;
private static final WifiInfo CONNECTED_AP_1_INFO = new WifiInfo();
static {
- CONNECTED_AP_1_INFO.setSSID(WifiSsid.createFromAsciiEncoded(SSID_1));
+ CONNECTED_AP_1_INFO.setSSID(WifiSsid.fromUtf8Text(SSID_1));
CONNECTED_AP_1_INFO.setBSSID(BSSID_1);
CONNECTED_AP_1_INFO.setNetworkId(NETWORK_ID_1);
CONNECTED_AP_1_INFO.setRssi(CONNECTED_RSSI);
@@ -246,7 +246,7 @@
private static ScanResult buildScanResult1() {
return new ScanResult(
- WifiSsid.createFromAsciiEncoded(SSID_1),
+ WifiSsid.fromUtf8Text(SSID_1),
BSSID_1,
0, // hessid
0, //anqpDomainId
@@ -259,7 +259,7 @@
private static ScanResult buildScanResult2() {
return new ScanResult(
- WifiSsid.createFromAsciiEncoded(SSID_2),
+ WifiSsid.fromUtf8Text(SSID_2),
BSSID_2,
0, // hessid
0, //anqpDomainId
@@ -272,7 +272,7 @@
private static ScanResult buildScanResultWithTimestamp(long timestampMillis) {
return new ScanResult(
- WifiSsid.createFromAsciiEncoded(SSID_3),
+ WifiSsid.fromUtf8Text(SSID_3),
BSSID_3,
0, // hessid
0, //anqpDomainId
@@ -444,7 +444,7 @@
networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTING, "connecting", "test");
WifiInfo info = new WifiInfo();
- info.setSSID(WifiSsid.createFromAsciiEncoded(SSID_2));
+ info.setSSID(WifiSsid.fromUtf8Text(SSID_2));
info.setBSSID(BSSID_2);
info.setRssi(CONNECTED_RSSI);
info.setNetworkId(NETWORK_ID_2);
@@ -671,7 +671,7 @@
String ssid = "ssid3";
String bssid = "00:00:00:00:00:00";
ScanResult newResult = new ScanResult(
- WifiSsid.createFromAsciiEncoded(ssid),
+ WifiSsid.fromUtf8Text(ssid),
bssid,
0, // hessid
0, //anqpDomainId
@@ -720,7 +720,7 @@
// Add a Passpoint AP to the scan results.
List<ScanResult> results = new ArrayList<>();
ScanResult passpointAp = new ScanResult(
- WifiSsid.createFromAsciiEncoded(SSID_1),
+ WifiSsid.fromUtf8Text(SSID_1),
BSSID_1,
0, // hessid
0, //anqpDomainId
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index ddf0289..b620654 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -162,6 +162,7 @@
<uses-permission android:name="android.permission.SET_ORIENTATION" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.INSTALL_PACKAGE_UPDATES" />
+ <uses-permission android:name="android.permission.INSTALL_DPC_PACKAGES" />
<uses-permission android:name="com.android.permission.USE_INSTALLER_V2" />
<uses-permission android:name="android.permission.INSTALL_TEST_ONLY_PACKAGE" />
<uses-permission android:name="com.android.permission.USE_SYSTEM_DATA_LOADERS" />
@@ -200,6 +201,7 @@
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.CREATE_USERS" />
+ <uses-permission android:name="android.permission.CREATE_VIRTUAL_DEVICE" />
<uses-permission android:name="android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP" />
<uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
<uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" />
@@ -654,6 +656,15 @@
</intent-filter>
</receiver>
+ <receiver
+ android:name=".ProfcollectUploadReceiver"
+ android:exported="true"
+ android:permission="android.permission.TRIGGER_SHELL_PROFCOLLECT_UPLOAD" >
+ <intent-filter>
+ <action android:name="com.android.shell.action.PROFCOLLECT_UPLOAD" />
+ </intent-filter>
+ </receiver>
+
<service
android:name=".BugreportProgressService"
android:exported="false"/>
diff --git a/packages/Shell/res/xml/file_provider_paths.xml b/packages/Shell/res/xml/file_provider_paths.xml
index 225c757..85d7dd3 100644
--- a/packages/Shell/res/xml/file_provider_paths.xml
+++ b/packages/Shell/res/xml/file_provider_paths.xml
@@ -1,3 +1,4 @@
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="bugreports" path="bugreports/" />
+ <root-path name="profcollect" path="/data/misc/profcollectd/report/" />
</paths>
diff --git a/packages/Shell/src/com/android/shell/ProfcollectUploadReceiver.java b/packages/Shell/src/com/android/shell/ProfcollectUploadReceiver.java
new file mode 100644
index 0000000..d2da724
--- /dev/null
+++ b/packages/Shell/src/com/android/shell/ProfcollectUploadReceiver.java
@@ -0,0 +1,98 @@
+/*
+ * 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.shell;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.util.Log;
+
+import androidx.core.content.FileProvider;
+
+import com.android.internal.R;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * A proxy service that relays report upload requests to the uploader app, while translating
+ * the path to the report to a content URI owned by this service.
+ */
+public final class ProfcollectUploadReceiver extends BroadcastReceiver {
+ private static final String AUTHORITY = "com.android.shell";
+ private static final String PROFCOLLECT_DATA_ROOT = "/data/misc/profcollectd/report/";
+
+ private static final String LOG_TAG = "ProfcollectUploadReceiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(LOG_TAG, "Received upload intent");
+
+ String uploaderPkg = getUploaderPackageName(context);
+ String uploaderAction = getUploaderActionName(context);
+
+ try {
+ ApplicationInfo info = context.getPackageManager().getApplicationInfo(uploaderPkg,
+ 0);
+ if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ Log.e(LOG_TAG, "The profcollect uploader app " + uploaderPkg
+ + " must be a system application");
+ return;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Cannot find profcollect uploader app " + uploaderPkg);
+ return;
+ }
+
+ String filename = intent.getStringExtra("filename");
+ File reportFile = new File(PROFCOLLECT_DATA_ROOT + filename);
+ Uri reportUri = FileProvider.getUriForFile(context, AUTHORITY, reportFile);
+ Intent uploadIntent =
+ new Intent(uploaderAction)
+ .setPackage(uploaderPkg)
+ .putExtra("EXTRA_DESTINATION", "PROFCOLLECT")
+ .putExtra("EXTRA_PACKAGE_NAME", context.getPackageName())
+ .setData(reportUri)
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+
+ List<ResolveInfo> receivers =
+ context.getPackageManager().queryBroadcastReceivers(uploadIntent, 0);
+ if (receivers == null || receivers.isEmpty()) {
+ Log.e(LOG_TAG, "No one to receive upload intent, abort upload.");
+ return;
+ }
+
+ context.grantUriPermission(uploaderPkg, reportUri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ context.sendBroadcast(uploadIntent);
+ }
+
+ private String getUploaderPackageName(Context context) {
+ return context.getResources().getString(
+ R.string.config_defaultProfcollectReportUploaderApp);
+ }
+
+ private String getUploaderActionName(Context context) {
+ return context.getResources().getString(
+ R.string.config_defaultProfcollectReportUploaderAction);
+ }
+}
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index ae8439f..6b91d64 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -50,6 +50,17 @@
srcs: ["src/com/android/systemui/EventLogTags.logtags"],
}
+java_library {
+ name: "SystemUI-flags",
+ srcs: [
+ "src/com/android/systemui/flags/Flags.java",
+ ],
+ libs: [
+ "SystemUI-flag-types",
+ ],
+ static_kotlin_stdlib: false,
+}
+
filegroup {
name: "ReleaseJavaFiles",
srcs: [
@@ -72,8 +83,7 @@
"src/**/*.kt",
"src/**/*.java",
"src/**/I*.aidl",
- "src-release/**/*.kt",
- "src-release/**/*.java",
+ ":ReleaseJavaFiles",
],
product_variables: {
debuggable: {
@@ -117,6 +127,7 @@
"iconloader_base",
"SystemUI-tags",
"SystemUI-proto",
+ "SystemUI-flags",
"monet",
"dagger2",
"jsr330",
@@ -179,8 +190,7 @@
"src/**/*.kt",
"src/**/*.java",
"src/**/I*.aidl",
- "src-release/**/*.kt",
- "src-release/**/*.java",
+ ":ReleaseJavaFiles",
],
static_libs: [
"WifiTrackerLib",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index c87ba65..26a4962 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -294,6 +294,11 @@
<uses-permission android:name="android.permission.READ_PEOPLE_DATA" />
+ <!-- Permission for dream overlay. -->
+ <uses-permission android:name="android.permission.BIND_DREAM_SERVICE" />
+
+ <uses-permission android:name="android.permission.BIND_APPWIDGET" />
+
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
@@ -673,6 +678,11 @@
android:name=".keyguard.KeyguardService"
android:exported="true" />
+ <service
+ android:name=".dreams.DreamOverlayService"
+ android:enabled="@bool/config_dreamOverlayServiceEnabled"
+ android:exported="true" />
+
<activity android:name=".keyguard.WorkLockActivity"
android:label="@string/accessibility_desc_work_lock"
android:permission="android.permission.MANAGE_USERS"
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 45e0345..f3edc0f 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -125,13 +125,18 @@
],
"auto-end-to-end-postsubmit": [
{
- "name": "AndroidAutoUiTests",
+ "name": "AndroidAutomotiveHomeTests",
"options" : [
{
- "include-filter": "android.test.functional.auto.apps.HomeHelperTest"
- },
+ "include-filter": "android.platform.tests.HomeTest"
+ }
+ ]
+ },
+ {
+ "name": "AndroidAutomotiveNotificationsTests",
+ "options" : [
{
- "include-filter": "android.test.functional.auto.apps.NotificationHelperTest"
+ "include-filter": "android.platform.tests.NotificationTest"
}
]
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index 669a054..865f96b 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -38,9 +38,6 @@
/**
* A class that allows dialogs to be started in a seamless way from a view that is transforming
* nicely into the starting dialog.
- *
- * Important: Don't forget to call [DialogLaunchAnimator.onDozeAmountChanged] when the doze amount
- * changes to gracefully handle dialogs fading out when the device is dozing.
*/
class DialogLaunchAnimator(
private val context: Context,
@@ -89,8 +86,17 @@
// host dialog.
if (dialog is ListenableDialog) {
dialog.addListener(object : DialogListener {
- override fun onDismiss() {
+ override fun onDismiss(reason: DialogListener.DismissReason) {
dialog.removeListener(this)
+
+ // We disable the exit animation if we are dismissing the dialog because the
+ // device is being locked, otherwise the animation looks bad if AOD is enabled.
+ // If AOD is disabled the screen will directly becomes black and we won't see
+ // the animation anyways.
+ if (reason == DialogListener.DismissReason.DEVICE_LOCKED) {
+ launchAnimation.exitAnimationDisabled = true
+ }
+
hostDialog.dismiss()
}
@@ -110,6 +116,10 @@
launchAnimation.ignoreNextCallToHide = true
dialog.hide()
}
+
+ override fun onSizeChanged() {
+ launchAnimation.onOriginalDialogSizeChanged()
+ }
})
}
@@ -117,13 +127,6 @@
return hostDialog
}
- /** Notify the current doze amount, to ensure that dialogs fade out when dozing. */
- // TODO(b/193634619): Replace this by some mandatory constructor parameter to make sure that we
- // don't forget to call this when the doze amount changes.
- fun onDozeAmountChanged(amount: Float) {
- currentAnimations.forEach { it.onDozeAmountChanged(amount) }
- }
-
/**
* Ensure that all dialogs currently shown won't animate into their touch surface when
* dismissed.
@@ -147,6 +150,7 @@
* 2. call [dismissOverride] instead of doing any dismissing logic. The actual dismissing
* logic should instead be done inside the lambda passed to [dismissOverride], which will
* be called after the exit animation.
+ * 3. Be full screen, i.e. have a window matching its parent size.
*
* See SystemUIHostDialogProvider for an example of implementation.
*/
@@ -168,14 +172,25 @@
}
interface DialogListener {
+ /** The reason why a dialog was dismissed. */
+ enum class DismissReason {
+ UNKNOWN,
+
+ /** The device was locked, which dismissed this dialog. */
+ DEVICE_LOCKED,
+ }
+
/** Called when this dialog dismiss() is called. */
- fun onDismiss()
+ fun onDismiss(reason: DismissReason)
/** Called when this dialog hide() is called. */
fun onHide()
/** Called when this dialog show() is called. */
fun onShow()
+
+ /** Called when this dialog size might have changed, e.g. because of configuration changes. */
+ fun onSizeChanged()
}
private class DialogLaunchAnimation(
@@ -254,10 +269,6 @@
val window = hostDialog.window
?: throw IllegalStateException("There is no window associated to the host dialog")
window.setBackgroundDrawableResource(android.R.color.transparent)
- window.setLayout(
- WindowManager.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.MATCH_PARENT
- )
// If we are using gesture navigation, then we can overlay the navigation/task bars with
// the host dialog.
@@ -425,6 +436,19 @@
})
}
+ fun onOriginalDialogSizeChanged() {
+ // The dialog is the single child of the root.
+ if (hostDialogRoot.childCount != 1) {
+ return
+ }
+
+ val dialogView = hostDialogRoot.getChildAt(0)
+ val layoutParams = dialogView.layoutParams as? FrameLayout.LayoutParams ?: return
+ layoutParams.width = originalDialog.window.attributes.width
+ layoutParams.height = originalDialog.window.attributes.height
+ dialogView.layoutParams = layoutParams
+ }
+
private fun maybeStartLaunchAnimation() {
if (!isTouchSurfaceGhostDrawn || !isOriginalDialogViewLaidOut) {
return
@@ -638,14 +662,4 @@
return (touchSurface.parent as? View)?.isShown ?: true
}
-
- internal fun onDozeAmountChanged(amount: Float) {
- val alpha = Interpolators.ALPHA_OUT.getInterpolation(1 - amount)
- val decorView = this.hostDialog.window?.decorView ?: return
- if (decorView.hasOverlappingRendering() && alpha > 0.0f &&
- alpha < 1.0f && decorView.layerType != View.LAYER_TYPE_HARDWARE) {
- decorView.setLayerType(View.LAYER_TYPE_HARDWARE, null)
- }
- decorView.alpha = alpha
- }
}
diff --git a/packages/SystemUI/docs/plugins.md b/packages/SystemUI/docs/plugins.md
index 6892005..378cba5 100644
--- a/packages/SystemUI/docs/plugins.md
+++ b/packages/SystemUI/docs/plugins.md
@@ -1,3 +1,4 @@
+
# SystemUI Plugins
Plugins provide an easy way to rapidly prototype SystemUI features. Plugins are APKs that will be installable only on Build.IS_DEBUGGABLE (dogfood) builds, that can change the behavior of SystemUI at runtime. This is done by creating a basic set of interfaces that the plugins can expect to be in SysUI, then the portion of code controlled by the interface can be iterated on faster than currently.
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/plugin/src/com/android/systemui/flags/Flag.kt
deleted file mode 100644
index 68834bc..0000000
--- a/packages/SystemUI/plugin/src/com/android/systemui/flags/Flag.kt
+++ /dev/null
@@ -1,52 +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 com.android.systemui.flags
-
-interface Flag<T> {
- val id: Int
- val default: T
-}
-
-data class BooleanFlag @JvmOverloads constructor(
- override val id: Int,
- override val default: Boolean = false
-) : Flag<Boolean>
-
-data class StringFlag @JvmOverloads constructor(
- override val id: Int,
- override val default: String = ""
-) : Flag<String>
-
-data class IntFlag @JvmOverloads constructor(
- override val id: Int,
- override val default: Int = 0
-) : Flag<Int>
-
-data class LongFlag @JvmOverloads constructor(
- override val id: Int,
- override val default: Long = 0
-) : Flag<Long>
-
-data class FloatFlag @JvmOverloads constructor(
- override val id: Int,
- override val default: Float = 0f
-) : Flag<Float>
-
-data class DoubleFlag @JvmOverloads constructor(
- override val id: Int,
- override val default: Double = 0.0
-) : Flag<Double>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/udfps_enroll_checkmark.xml b/packages/SystemUI/res/drawable/udfps_enroll_checkmark.xml
new file mode 100644
index 0000000..f8169d3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/udfps_enroll_checkmark.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="54dp"
+ android:height="54dp"
+ android:viewportWidth="54"
+ android:viewportHeight="54">
+ <path
+ android:pathData="M26.9999,3.9619C39.7029,3.9619 50.0369,14.2969 50.0369,26.9999C50.0369,39.7029 39.7029,50.0379 26.9999,50.0379C14.2969,50.0379 3.9629,39.7029 3.9629,26.9999C3.9629,14.2969 14.2969,3.9619 26.9999,3.9619Z"
+ android:fillColor="?android:colorBackground"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M27,0C12.088,0 0,12.088 0,27C0,41.912 12.088,54 27,54C41.912,54 54,41.912 54,27C54,12.088 41.912,0 27,0ZM27,3.962C39.703,3.962 50.037,14.297 50.037,27C50.037,39.703 39.703,50.038 27,50.038C14.297,50.038 3.963,39.703 3.963,27C3.963,14.297 14.297,3.962 27,3.962Z"
+ android:fillColor="@color/udfps_enroll_progress"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M23.0899,38.8534L10.4199,26.1824L13.2479,23.3544L23.0899,33.1974L41.2389,15.0474L44.0679,17.8754L23.0899,38.8534Z"
+ android:fillColor="@color/udfps_enroll_progress"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml
new file mode 100644
index 0000000..1f10e5d
--- /dev/null
+++ b/packages/SystemUI/res/layout/combined_qs_header.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<androidx.constraintlayout.motion.widget.MotionLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/split_shade_status_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/split_shade_header_min_height"
+ android:clickable="false"
+ android:focusable="true"
+ android:paddingLeft="@dimen/qs_panel_padding"
+ android:paddingRight="@dimen/qs_panel_padding"
+ android:visibility="gone"
+ android:theme="@style/Theme.SystemUI.QuickSettings.Header"
+ app:layoutDescription="@xml/combined_qs_header_scene">
+
+ <androidx.constraintlayout.widget.Guideline
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/center"
+ app:layout_constraintGuide_percent="0.5"
+ android:orientation="vertical" />
+
+ <com.android.systemui.statusbar.policy.Clock
+ android:id="@+id/clock"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:gravity="start|center_vertical"
+ android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
+ android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.QS.Status"
+ />
+
+ <com.android.systemui.statusbar.policy.DateView
+ android:id="@+id/date"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_gravity="start|center_vertical"
+ android:gravity="center_vertical"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.QS.Status"
+ app:datePattern="@string/abbrev_wday_month_day_no_year_alarm"
+ />
+
+ <include
+ android:id="@+id/carrier_group"
+ layout="@layout/qs_carrier_group"
+ app:layout_constraintHeight_min="@dimen/split_shade_header_min_height"
+ android:minHeight="@dimen/split_shade_header_min_height"
+ app:layout_constraintWidth_min="48dp"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constrainedWidth="true"
+ android:layout_gravity="end|center_vertical"
+ android:layout_marginStart="8dp"
+ app:layout_constraintStart_toEndOf="@id/date"
+ app:layout_constraintEnd_toStartOf="@id/statusIcons"
+ app:layout_constraintTop_toTopOf="@id/clock"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintHorizontal_bias="1"
+ />
+
+ <com.android.systemui.statusbar.phone.StatusIconContainer
+ android:id="@+id/statusIcons"
+ app:layout_constraintHeight_min="@dimen/split_shade_header_min_height"
+ android:paddingEnd="@dimen/signal_cluster_battery_padding"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ app:layout_constraintStart_toEndOf="@id/carrier_group"
+ app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
+ app:layout_constraintTop_toTopOf="@id/clock"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintHorizontal_bias="1"
+ />
+
+ <com.android.systemui.battery.BatteryMeterView
+ android:id="@+id/batteryRemainingIcon"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ app:layout_constraintHeight_min="@dimen/split_shade_header_min_height"
+ app:textAppearance="@style/TextAppearance.QS.Status"
+ app:layout_constraintStart_toEndOf="@id/statusIcons"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="@id/clock"
+ app:layout_constraintBottom_toBottomOf="parent"
+ />
+
+</androidx.constraintlayout.motion.widget.MotionLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index f4faa62..86e2661 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -329,7 +329,8 @@
android:layout_height="wrap_content"
android:paddingBottom="4dp"
android:clickable="false"
- android:focusable="false">
+ android:focusable="false"
+ android:visibility="gone">
<LinearLayout
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/screenshot_static.xml b/packages/SystemUI/res/layout/screenshot_static.xml
index e2d5229..f6e3f1a 100644
--- a/packages/SystemUI/res/layout/screenshot_static.xml
+++ b/packages/SystemUI/res/layout/screenshot_static.xml
@@ -94,7 +94,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:elevation="@dimen/screenshot_preview_elevation"
- android:contentDescription="@string/screenshot_edit_label"
+ android:contentDescription="@string/screenshot_edit_description"
android:scaleType="fitEnd"
android:background="@drawable/screenshot_preview_background"
android:adjustViewBounds="true"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 702a354..b4c9a93 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -79,7 +79,11 @@
android:clipToPadding="false"
android:clipChildren="false">
- <include layout="@layout/split_shade_header"/>
+ <ViewStub
+ android:id="@+id/qs_header_stub"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ />
<include
layout="@layout/keyguard_status_view"
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 5176d96..6f5d7be 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -18,7 +18,7 @@
-->
<!-- This is the status bar window. -->
-<com.android.systemui.statusbar.phone.StatusBarWindowView
+<com.android.systemui.statusbar.window.StatusBarWindowView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
@@ -35,4 +35,4 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/system_bar_background" />
-</com.android.systemui.statusbar.phone.StatusBarWindowView>
+</com.android.systemui.statusbar.window.StatusBarWindowView>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 9878e0d..fc6b99a 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -577,9 +577,6 @@
<!-- Whether to use the split 2-column notification shade -->
<bool name="config_use_split_notification_shade">false</bool>
- <!-- Determines whether the shell features all run on another thread. -->
- <bool name="config_enableShellMainThread">false</bool>
-
<!-- Default udfps icon. Same path as ic_fingerprint.xml -->
<string name="config_udfpsIcon" translatable="false">
M25.5,16.3283C28.47,14.8433 31.9167,14 35.5834,14C39.2501,14 42.6968,14.8433 45.6668,16.3283
@@ -715,4 +712,22 @@
<!-- Flag to enable privacy dot views, it shall be true for normal case -->
<bool name="config_enablePrivacyDot">true</bool>
+ <!-- The positions widgets can be in defined as View.Gravity constants -->
+ <integer-array name="config_dreamOverlayPositions">
+ </integer-array>
+
+ <!-- Widget components to show as dream overlays -->
+ <string-array name="config_dreamOverlayComponents" translatable="false">
+ </string-array>
+
+ <!-- Width percentage of dream overlay components -->
+ <item name="config_dreamOverlayComponentWidthPercent" translatable="false" format="float"
+ type="dimen">0.33</item>
+
+ <!-- Height percentage of dream overlay components -->
+ <item name="config_dreamOverlayComponentHeightPercent" translatable="false" format="float"
+ type="dimen">0.25</item>
+
+ <!-- Flag to enable dream overlay service and its registration -->
+ <bool name="config_dreamOverlayServiceEnabled">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index dee886a..6348f3a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -183,6 +183,9 @@
<!-- Label for UI element which allows editing the screenshot [CHAR LIMIT=30] -->
<string name="screenshot_edit_label">Edit</string>
<!-- Content description indicating that tapping the element will allow editing the screenshot [CHAR LIMIT=NONE] -->
+ <string name="screenshot_edit_description">Edit screenshot</string>
+ <!-- Content description indicating that tapping the element will allow sharing the screenshot [CHAR LIMIT=NONE] -->
+ <string name="screenshot_share_description">Share screenshot</string>
<!-- Label for UI element which allows the user to capture additional off-screen content in a screenshot. [CHAR LIMIT=30] -->
<string name="screenshot_scroll_label">Capture more</string>
<!-- Content description indicating that tapping a button will dismiss the screenshots UI [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index b83ac3d..fbbbf9f 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -359,6 +359,9 @@
<style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog">
<item name="android:buttonCornerRadius">28dp</item>
+ <item name="android:buttonBarPositiveButtonStyle">@style/Widget.QSDialog.Button</item>
+ <item name="android:buttonBarNegativeButtonStyle">@style/Widget.QSDialog.Button.BorderButton</item>
+ <item name="android:buttonBarNeutralButtonStyle">@style/Widget.QSDialog.Button.BorderButton</item>
</style>
<style name="Theme.SystemUI.Dialog.Alert" parent="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
@@ -911,12 +914,15 @@
<style name="InternetDialog.Network">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">88dp</item>
+ <item name="android:layout_marginStart">@dimen/internet_dialog_network_layout_margin</item>
<item name="android:layout_marginEnd">@dimen/internet_dialog_network_layout_margin</item>
+ <item name="android:layout_gravity">center_vertical|start</item>
<item name="android:paddingStart">22dp</item>
<item name="android:paddingEnd">22dp</item>
<item name="android:orientation">horizontal</item>
<item name="android:focusable">true</item>
<item name="android:clickable">true</item>
+ <item name="android:background">?android:attr/selectableItemBackground</item>
</style>
<style name="InternetDialog.NetworkTitle">
diff --git a/packages/SystemUI/res/xml/combined_qs_header_scene.xml b/packages/SystemUI/res/xml/combined_qs_header_scene.xml
new file mode 100644
index 0000000..d61e4a9
--- /dev/null
+++ b/packages/SystemUI/res/xml/combined_qs_header_scene.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<MotionScene
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <Transition
+ android:id="@+id/header_transition"
+ app:constraintSetEnd="@id/qs_header_constraint"
+ app:constraintSetStart="@id/qqs_header_constraint">
+ <KeyFrameSet>
+ <KeyPosition
+ app:keyPositionType="pathRelative"
+ app:percentX="0"
+ app:framePosition="50"
+ app:motionTarget="@id/date" />
+ </KeyFrameSet>
+ </Transition>
+
+ <Transition
+ android:id="@+id/split_header_transition"
+ app:constraintSetStart="@id/split_header_constraint"
+ app:constraintSetEnd="@id/split_header_constraint"/>
+
+ <!--
+ Placeholder ConstraintSet. They are populated in the controller for this class.
+ This is needed because there's no easy way to just refer to a `ConstraintSet` file. The
+ options are either a layout file or inline the ConstraintSets.
+ -->
+ <ConstraintSet android:id="@id/qqs_header_constraint"/>
+
+ <ConstraintSet android:id="@id/qs_header_constraint"/>
+
+ <ConstraintSet android:id="@id/split_header_constraint" />
+
+</MotionScene>
diff --git a/packages/SystemUI/res/xml/media_collapsed.xml b/packages/SystemUI/res/xml/media_collapsed.xml
index c3510b6..12e446f 100644
--- a/packages/SystemUI/res/xml/media_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_collapsed.xml
@@ -58,7 +58,7 @@
<!-- Song name -->
<Constraint
android:id="@+id/header_title"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/qs_media_info_margin"
android:layout_marginEnd="@dimen/qs_center_guideline_padding"
@@ -71,7 +71,7 @@
<!-- Artist name -->
<Constraint
android:id="@+id/header_artist"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constrainedWidth="true"
android:layout_marginTop="@dimen/qs_media_info_spacing"
diff --git a/packages/SystemUI/res/xml/qqs_header.xml b/packages/SystemUI/res/xml/qqs_header.xml
new file mode 100644
index 0000000..3d7b549
--- /dev/null
+++ b/packages/SystemUI/res/xml/qqs_header.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<ConstraintSet
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/qqs_header_constraint"
+>
+
+ <Constraint
+ android:id="@+id/clock">
+ <Layout
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/date"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintHorizontal_chainStyle="packed"
+ />
+ </Constraint>
+
+ <Constraint
+ android:id="@+id/date">
+ <Layout
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ app:layout_constraintStart_toEndOf="@id/clock"
+ app:layout_constraintEnd_toStartOf="@id/carrier_group"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintHorizontal_bias="0"
+ />
+ </Constraint>
+
+ <Constraint
+ android:id="@+id/carrier_group">
+ <CustomAttribute
+ app:attributeName="alpha"
+ app:customFloatValue="0"
+ />
+ </Constraint>
+
+
+
+</ConstraintSet>
\ No newline at end of file
diff --git a/packages/SystemUI/res/xml/qs_header.xml b/packages/SystemUI/res/xml/qs_header.xml
new file mode 100644
index 0000000..6a0ab86
--- /dev/null
+++ b/packages/SystemUI/res/xml/qs_header.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<ConstraintSet
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/qs_header_constraint"
+>
+
+ <Constraint
+ android:id="@+id/clock">
+ <Layout
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/date"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/carrier_group"
+ app:layout_constraintHorizontal_bias="0"
+ />
+ </Constraint>
+
+ <Constraint
+ android:id="@+id/date">
+ <Layout
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/clock"
+ app:layout_constraintHorizontal_bias="0"
+ />
+ <Motion
+ app:motionStagger="0.5"
+ />
+ </Constraint>
+
+ <Constraint
+ android:id="@+id/carrier_group">
+ <CustomAttribute
+ app:attributeName="alpha"
+ app:customFloatValue="1"
+ />
+ </Constraint>
+
+
+</ConstraintSet>
\ No newline at end of file
diff --git a/packages/SystemUI/res/xml/split_header.xml b/packages/SystemUI/res/xml/split_header.xml
new file mode 100644
index 0000000..44d42a0
--- /dev/null
+++ b/packages/SystemUI/res/xml/split_header.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<ConstraintSet
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/split_header_constraint">
+
+ <Constraint
+ android:id="@+id/clock">
+ <Layout
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/date"
+ />
+ </Constraint>
+
+ <Constraint
+ android:id="@+id/date">
+ <Layout
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ app:layout_constraintStart_toEndOf="@id/clock"
+ app:layout_constraintEnd_toStartOf="@id/carrier_group"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintHorizontal_bias="0"
+ />
+ </Constraint>
+
+ <Constraint
+ android:id="@+id/carrier_group">
+ <PropertySet
+ android:alpha="1"
+ app:customFloatValue="1"
+ />
+ </Constraint>
+
+
+</ConstraintSet>
\ No newline at end of file
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 23307de..2909043 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -45,9 +45,41 @@
":wm_shell-aidls",
":wm_shell_util-sources",
],
+ libs: [
+ "SystemUI-flags",
+ ],
static_libs: [
"PluginCoreLib",
"androidx.dynamicanimation_dynamicanimation",
+ "androidx.concurrent_concurrent-futures",
+ ],
+ java_version: "1.8",
+ min_sdk_version: "current",
+}
+
+java_library {
+ name: "SystemUI-flag-types",
+ srcs: [
+ "src/com/android/systemui/flags/Flag.kt",
+ ],
+ include_srcs: true,
+ static_kotlin_stdlib: false,
+ java_version: "1.8",
+ min_sdk_version: "current",
+}
+
+java_library {
+ name: "SystemUIFlagsLib",
+ srcs: [
+ "src/com/android/systemui/flags/**/*.kt",
+ ],
+ static_kotlin_stdlib: false,
+ libs: [
+ "SystemUI-flags",
+ "androidx.concurrent_concurrent-futures",
+ ],
+ static_libs: [
+ "SystemUI-flag-types",
],
java_version: "1.8",
min_sdk_version: "current",
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
new file mode 100644
index 0000000..d9b6a34
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
@@ -0,0 +1,172 @@
+/*
+ * 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.systemui.flags
+
+import android.os.Parcel
+import android.os.Parcelable
+
+interface Flag<T> : Parcelable {
+ val id: Int
+ val default: T
+
+ override fun describeContents() = 0
+}
+
+// Consider using the "parcelize" kotlin library.
+
+data class BooleanFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val default: Boolean = false
+) : Flag<Boolean> {
+
+ companion object {
+ @JvmField
+ val CREATOR = object : Parcelable.Creator<BooleanFlag> {
+ override fun createFromParcel(parcel: Parcel) = BooleanFlag(parcel)
+ override fun newArray(size: Int) = arrayOfNulls<BooleanFlag>(size)
+ }
+ }
+
+ private constructor(parcel: Parcel) : this(
+ id = parcel.readInt(),
+ default = parcel.readBoolean()
+ )
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.writeInt(id)
+ parcel.writeBoolean(default)
+ }
+}
+
+data class StringFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val default: String = ""
+) : Flag<String> {
+ companion object {
+ @JvmField
+ val CREATOR = object : Parcelable.Creator<StringFlag> {
+ override fun createFromParcel(parcel: Parcel) = StringFlag(parcel)
+ override fun newArray(size: Int) = arrayOfNulls<StringFlag>(size)
+ }
+ }
+
+ private constructor(parcel: Parcel) : this(
+ id = parcel.readInt(),
+ default = parcel.readString() ?: ""
+ )
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.writeInt(id)
+ parcel.writeString(default)
+ }
+}
+
+data class IntFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val default: Int = 0
+) : Flag<Int> {
+
+ companion object {
+ @JvmField
+ val CREATOR = object : Parcelable.Creator<IntFlag> {
+ override fun createFromParcel(parcel: Parcel) = IntFlag(parcel)
+ override fun newArray(size: Int) = arrayOfNulls<IntFlag>(size)
+ }
+ }
+
+ private constructor(parcel: Parcel) : this(
+ id = parcel.readInt(),
+ default = parcel.readInt()
+ )
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.writeInt(id)
+ parcel.writeInt(default)
+ }
+}
+
+data class LongFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val default: Long = 0
+) : Flag<Long> {
+
+ companion object {
+ @JvmField
+ val CREATOR = object : Parcelable.Creator<LongFlag> {
+ override fun createFromParcel(parcel: Parcel) = LongFlag(parcel)
+ override fun newArray(size: Int) = arrayOfNulls<LongFlag>(size)
+ }
+ }
+
+ private constructor(parcel: Parcel) : this(
+ id = parcel.readInt(),
+ default = parcel.readLong()
+ )
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.writeInt(id)
+ parcel.writeLong(default)
+ }
+}
+
+data class FloatFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val default: Float = 0f
+) : Flag<Float> {
+
+ companion object {
+ @JvmField
+ val CREATOR = object : Parcelable.Creator<FloatFlag> {
+ override fun createFromParcel(parcel: Parcel) = FloatFlag(parcel)
+ override fun newArray(size: Int) = arrayOfNulls<FloatFlag>(size)
+ }
+ }
+
+ private constructor(parcel: Parcel) : this(
+ id = parcel.readInt(),
+ default = parcel.readFloat()
+ )
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.writeInt(id)
+ parcel.writeFloat(default)
+ }
+}
+
+data class DoubleFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val default: Double = 0.0
+) : Flag<Double> {
+
+ companion object {
+ @JvmField
+ val CREATOR = object : Parcelable.Creator<DoubleFlag> {
+ override fun createFromParcel(parcel: Parcel) = DoubleFlag(parcel)
+ override fun newArray(size: Int) = arrayOfNulls<DoubleFlag>(size)
+ }
+ }
+
+ private constructor(parcel: Parcel) : this(
+ id = parcel.readInt(),
+ default = parcel.readDouble()
+ )
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.writeInt(id)
+ parcel.writeDouble(default)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
new file mode 100644
index 0000000..1dc555e
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
@@ -0,0 +1,172 @@
+/*
+ * 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.systemui.flags
+
+import android.app.Activity
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Bundle
+import android.os.Handler
+import android.provider.Settings
+import androidx.concurrent.futures.CallbackToFutureAdapter
+import com.google.common.util.concurrent.ListenableFuture
+import org.json.JSONException
+import org.json.JSONObject
+
+class FlagManager constructor(
+ private val context: Context,
+ private val handler: Handler
+) : FlagReader {
+ companion object {
+ const val RECEIVING_PACKAGE = "com.android.systemui"
+ const val ACTION_SET_FLAG = "com.android.systemui.action.SET_FLAG"
+ const val ACTION_GET_FLAGS = "com.android.systemui.action.GET_FLAGS"
+ const val FLAGS_PERMISSION = "com.android.systemui.permission.FLAGS"
+ const val FIELD_ID = "id"
+ const val FIELD_VALUE = "value"
+ const val FIELD_TYPE = "type"
+ const val FIELD_FLAGS = "flags"
+ const val TYPE_BOOLEAN = "boolean"
+ private const val SETTINGS_PREFIX = "systemui/flags"
+ }
+
+ private val listeners: MutableSet<FlagReader.Listener> = mutableSetOf()
+ private val settingsObserver: ContentObserver = SettingsObserver()
+
+ fun getFlagsFuture(): ListenableFuture<Collection<Flag<*>>> {
+ val intent = Intent(ACTION_GET_FLAGS)
+ intent.setPackage(RECEIVING_PACKAGE)
+
+ return CallbackToFutureAdapter.getFuture {
+ completer: CallbackToFutureAdapter.Completer<Any?> ->
+ context.sendOrderedBroadcast(intent, null,
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ val extras: Bundle? = getResultExtras(false)
+ val listOfFlags: java.util.ArrayList<Flag<*>>? =
+ extras?.getParcelableArrayList(FIELD_FLAGS)
+ if (listOfFlags != null) {
+ completer.set(listOfFlags)
+ } else {
+ completer.setException(NoFlagResultsException())
+ }
+ }
+ }, null, Activity.RESULT_OK, "extra data", null)
+ "QueryingFlags"
+ } as ListenableFuture<Collection<Flag<*>>>
+ }
+
+ fun setFlagValue(id: Int, enabled: Boolean) {
+ val intent = createIntent(id)
+ intent.putExtra(FIELD_VALUE, enabled)
+
+ context.sendBroadcast(intent)
+ }
+
+ fun eraseFlag(id: Int) {
+ val intent = createIntent(id)
+
+ context.sendBroadcast(intent)
+ }
+
+ override fun isEnabled(id: Int, def: Boolean): Boolean {
+ return isEnabled(id) ?: def
+ }
+
+ /** Returns the stored value or null if not set. */
+ fun isEnabled(id: Int): Boolean? {
+ val data: String = Settings.Secure.getString(
+ context.contentResolver, keyToSettingsPrefix(id))
+ if (data.isEmpty()) {
+ return null
+ }
+ val json: JSONObject
+ try {
+ json = JSONObject(data)
+ return if (!assertType(json, TYPE_BOOLEAN)) {
+ null
+ } else json.getBoolean(FIELD_VALUE)
+ } catch (e: JSONException) {
+ throw InvalidFlagStorageException()
+ }
+ }
+
+ override fun addListener(listener: FlagReader.Listener) {
+ synchronized(listeners) {
+ val registerNeeded = listeners.isEmpty()
+ listeners.add(listener)
+ if (registerNeeded) {
+ context.contentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(SETTINGS_PREFIX), true, settingsObserver)
+ }
+ }
+ }
+
+ override fun removeListener(listener: FlagReader.Listener) {
+ synchronized(listeners) {
+ val isRegistered = !listeners.isEmpty()
+ listeners.remove(listener)
+ if (isRegistered && listeners.isEmpty()) {
+ context.contentResolver.unregisterContentObserver(settingsObserver)
+ }
+ }
+ }
+
+ private fun createIntent(id: Int): Intent {
+ val intent = Intent(ACTION_SET_FLAG)
+ intent.setPackage(RECEIVING_PACKAGE)
+ intent.putExtra(FIELD_ID, id)
+
+ return intent
+ }
+
+ fun keyToSettingsPrefix(key: Int): String {
+ return SETTINGS_PREFIX + "/" + key
+ }
+
+ private fun assertType(json: JSONObject, type: String): Boolean {
+ return try {
+ json.getString(FIELD_TYPE) == TYPE_BOOLEAN
+ } catch (e: JSONException) {
+ false
+ }
+ }
+
+ inner class SettingsObserver : ContentObserver(handler) {
+ override fun onChange(selfChange: Boolean, uri: Uri?) {
+ if (uri == null) {
+ return
+ }
+ val parts = uri.pathSegments
+ val idStr = parts[parts.size - 1]
+ try {
+ val id = idStr.toInt()
+ listeners.forEach { l -> l.onFlagChanged(id) }
+ } catch (e: NumberFormatException) {
+ // no-op
+ }
+ }
+ }
+}
+
+class InvalidFlagStorageException : Exception("Data found but is invalid")
+
+class NoFlagResultsException : Exception(
+ "SystemUI failed to communicate its flags back successfully")
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
new file mode 100644
index 0000000..ee6dea5
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.systemui.flags
+
+/**
+ * Plugin for loading flag values
+ */
+interface FlagReader {
+ /** Returns a boolean value for the given flag. */
+ fun isEnabled(id: Int, def: Boolean): Boolean {
+ return def
+ }
+
+ /** Add a listener to be alerted when any flag changes. */
+ fun addListener(listener: Listener) {}
+
+ /** Remove a listener to be alerted when any flag changes. */
+ fun removeListener(listener: Listener) {}
+
+ /** A simple listener to be alerted when a flag changes. */
+ fun interface Listener {
+ /** */
+ fun onFlagChanged(id: Int)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 5b7e500..be15c70 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -150,5 +150,10 @@
*/
oneway void notifyTaskbarAutohideSuspend(boolean suspend) = 48;
- // Next id = 49
+ /**
+ * Notifies SystemUI to invoke IME Switcher.
+ */
+ void onImeSwitcherPressed() = 49;
+
+ // Next id = 50
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/GroupTask.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/GroupTask.java
new file mode 100644
index 0000000..323b20e
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/GroupTask.java
@@ -0,0 +1,45 @@
+/*
+ * 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.systemui.shared.recents.model;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * A group task in the recent tasks list.
+ * TODO: Move this into Launcher
+ */
+public class GroupTask {
+ public @NonNull Task task1;
+ public @Nullable Task task2;
+
+ public GroupTask(@NonNull Task t1, @Nullable Task t2) {
+ task1 = t1;
+ task2 = t2;
+ }
+
+ public GroupTask(@NonNull GroupTask group) {
+ task1 = new Task(group.task1);
+ task2 = group.task2 != null
+ ? new Task(group.task2)
+ : null;
+ }
+
+ public boolean containsTask(int taskId) {
+ return task1.key.id == taskId || (task2 != null && task2.key.id == taskId);
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index e9e9b24..3f2ff74 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -38,6 +38,7 @@
/**
* A task in the recent tasks list.
+ * TODO: Move this into Launcher or see if we can remove now
*/
public class Task {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
index 6594d5f..1142e05 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
@@ -22,7 +22,6 @@
import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_UNDEFINED;
-import android.window.TaskSnapshot;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Point;
@@ -30,6 +29,9 @@
import android.hardware.HardwareBuffer;
import android.util.Log;
import android.view.WindowInsetsController.Appearance;
+import android.window.TaskSnapshot;
+
+import java.util.HashMap;
/**
* Data for a single thumbnail.
@@ -40,6 +42,7 @@
public int orientation;
public int rotation;
public Rect insets;
+ public Rect letterboxInsets;
public boolean reducedResolution;
public boolean isRealSnapshot;
public boolean isTranslucent;
@@ -53,6 +56,7 @@
orientation = ORIENTATION_UNDEFINED;
rotation = ROTATION_UNDEFINED;
insets = new Rect();
+ letterboxInsets = new Rect();
reducedResolution = false;
scale = 1f;
isRealSnapshot = true;
@@ -80,9 +84,22 @@
return thumbnail;
}
+ public static HashMap<Integer, ThumbnailData> wrap(int[] taskIds, TaskSnapshot[] snapshots) {
+ HashMap<Integer, ThumbnailData> temp = new HashMap<>();
+ if (taskIds == null || snapshots == null || taskIds.length != snapshots.length) {
+ return temp;
+ }
+
+ for (int i = snapshots.length - 1; i >= 0; i--) {
+ temp.put(taskIds[i], new ThumbnailData(snapshots[i]));
+ }
+ return temp;
+ }
+
public ThumbnailData(TaskSnapshot snapshot) {
thumbnail = makeThumbnail(snapshot);
insets = new Rect(snapshot.getContentInsets());
+ letterboxInsets = new Rect(snapshot.getLetterboxInsets());
orientation = snapshot.getOrientation();
rotation = snapshot.getRotation();
reducedResolution = snapshot.isLowResolution();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 9164137..b95123d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -28,7 +28,6 @@
import android.app.ActivityManager;
import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityManager.RunningTaskInfo;
-import android.window.TaskSnapshot;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.AppGlobals;
@@ -50,6 +49,7 @@
import android.view.IRecentsAnimationController;
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationTarget;
+import android.window.TaskSnapshot;
import com.android.internal.app.IVoiceInteractionManagerService;
import com.android.systemui.shared.recents.model.Task;
@@ -189,9 +189,9 @@
}
@Override
- public void onAnimationCanceled(TaskSnapshot taskSnapshot) {
+ public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots) {
animationHandler.onAnimationCanceled(
- taskSnapshot != null ? new ThumbnailData(taskSnapshot) : null);
+ ThumbnailData.wrap(taskIds, taskSnapshots));
}
@Override
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index d447b48..d182399 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -57,6 +57,8 @@
"extra_shell_starting_window";
// See ISmartspaceTransitionController.aidl
public static final String KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER = "smartspace_transition";
+ // See IRecentTasks.aidl
+ public static final String KEY_EXTRA_RECENT_TASKS = "recent_tasks";
public static final String NAV_BAR_MODE_2BUTTON_OVERLAY =
WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index 8e65560..13f1db4a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -39,12 +39,14 @@
public ThumbnailData screenshotTask(int taskId) {
try {
- TaskSnapshot snapshot = mAnimationController.screenshotTask(taskId);
- return snapshot != null ? new ThumbnailData(snapshot) : new ThumbnailData();
+ final TaskSnapshot snapshot = mAnimationController.screenshotTask(taskId);
+ if (snapshot != null) {
+ return new ThumbnailData(snapshot);
+ }
} catch (RemoteException e) {
Log.e(TAG, "Failed to screenshot task", e);
- return new ThumbnailData();
}
+ return new ThumbnailData();
}
public void setInputConsumerEnabled(boolean enabled) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
index c4cd192..a74de2e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
@@ -20,6 +20,8 @@
import com.android.systemui.shared.recents.model.ThumbnailData;
+import java.util.HashMap;
+
public interface RecentsAnimationListener {
/**
* Called when the animation into Recents can start. This call is made on the binder thread.
@@ -31,7 +33,7 @@
/**
* Called when the animation into Recents was canceled. This call is made on the binder thread.
*/
- void onAnimationCanceled(ThumbnailData thumbnailData);
+ void onAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas);
/**
* Called when the task of an activity that has been started while the recents animation
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index dcc4ea1..7729a75 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -26,6 +26,7 @@
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import android.annotation.SuppressLint;
+import android.app.IApplicationThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
@@ -51,10 +52,10 @@
private final RemoteTransitionCompat mRemoteTransition;
public RemoteAnimationAdapterCompat(RemoteAnimationRunnerCompat runner, long duration,
- long statusBarTransitionDelay) {
+ long statusBarTransitionDelay, IApplicationThread appThread) {
mWrapped = new RemoteAnimationAdapter(wrapRemoteAnimationRunner(runner), duration,
statusBarTransitionDelay);
- mRemoteTransition = buildRemoteTransition(runner);
+ mRemoteTransition = buildRemoteTransition(runner, appThread);
}
RemoteAnimationAdapter getWrapped() {
@@ -62,9 +63,10 @@
}
/** Helper to just build a remote transition. Use this if the legacy adapter isn't needed. */
- public static RemoteTransitionCompat buildRemoteTransition(RemoteAnimationRunnerCompat runner) {
+ public static RemoteTransitionCompat buildRemoteTransition(RemoteAnimationRunnerCompat runner,
+ IApplicationThread appThread) {
return new RemoteTransitionCompat(
- new RemoteTransition(wrapRemoteTransition(runner)));
+ new RemoteTransition(wrapRemoteTransition(runner), appThread));
}
public RemoteTransitionCompat getRemoteTransition() {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 9ec95a3..99b6aed 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -30,6 +30,7 @@
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
+import android.app.IApplicationThread;
import android.content.ComponentName;
import android.graphics.Rect;
import android.os.IBinder;
@@ -72,7 +73,7 @@
}
public RemoteTransitionCompat(@NonNull RemoteTransitionRunner runner,
- @NonNull Executor executor) {
+ @NonNull Executor executor, @Nullable IApplicationThread appThread) {
IRemoteTransition remote = new IRemoteTransition.Stub() {
@Override
public void startAnimation(IBinder transition, TransitionInfo info,
@@ -103,12 +104,12 @@
finishAdapter));
}
};
- mTransition = new RemoteTransition(remote);
+ mTransition = new RemoteTransition(remote, appThread);
}
/** Constructor specifically for recents animation */
public RemoteTransitionCompat(RecentsAnimationListener recents,
- RecentsAnimationControllerCompat controller) {
+ RecentsAnimationControllerCompat controller, IApplicationThread appThread) {
IRemoteTransition remote = new IRemoteTransition.Stub() {
final RecentsControllerWrap mRecentsSession = new RecentsControllerWrap();
IBinder mToken = null;
@@ -168,7 +169,7 @@
}
}
};
- mTransition = new RemoteTransition(remote);
+ mTransition = new RemoteTransition(remote, appThread);
}
/** Adds a filter check that restricts this remote transition to home open transitions. */
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java b/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
index 1eeb516..ef04619 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
@@ -16,6 +16,12 @@
package com.android.systemui.flags;
+import static com.android.systemui.flags.FlagManager.ACTION_GET_FLAGS;
+import static com.android.systemui.flags.FlagManager.ACTION_SET_FLAG;
+import static com.android.systemui.flags.FlagManager.FIELD_FLAGS;
+import static com.android.systemui.flags.FlagManager.FIELD_ID;
+import static com.android.systemui.flags.FlagManager.FIELD_VALUE;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -28,6 +34,7 @@
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.util.settings.SecureSettings;
import org.json.JSONException;
import org.json.JSONObject;
@@ -53,24 +60,20 @@
public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable {
private static final String TAG = "SysUIFlags";
- private static final String SYSPROP_PREFIX = "persist.systemui.flag_";
- private static final String FIELD_TYPE = "type";
- private static final String FIELD_ID = "id";
- private static final String FIELD_VALUE = "value";
- private static final String TYPE_BOOLEAN = "boolean";
- private static final String ACTION_SET_FLAG = "com.android.systemui.action.SET_FLAG";
- private static final String FLAGS_PERMISSION = "com.android.systemui.permission.FLAGS";
- private final SystemPropertiesHelper mSystemPropertiesHelper;
-
+ private final FlagManager mFlagManager;
+ private final SecureSettings mSecureSettings;
private final Map<Integer, Boolean> mBooleanFlagCache = new HashMap<>();
@Inject
- public FeatureFlagManager(SystemPropertiesHelper systemPropertiesHelper, Context context,
+ public FeatureFlagManager(FlagManager flagManager,
+ SecureSettings secureSettings, Context context,
DumpManager dumpManager) {
- mSystemPropertiesHelper = systemPropertiesHelper;
-
- IntentFilter filter = new IntentFilter(ACTION_SET_FLAG);
- context.registerReceiver(mReceiver, filter, FLAGS_PERMISSION, null);
+ mFlagManager = flagManager;
+ mSecureSettings = secureSettings;
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_SET_FLAG);
+ filter.addAction(ACTION_GET_FLAGS);
+ context.registerReceiver(mReceiver, filter, null, null);
dumpManager.registerDumpable(TAG, this);
}
@@ -87,20 +90,10 @@
/** Returns the stored value or null if not set. */
private Boolean isEnabledInternal(int id) {
- String data = mSystemPropertiesHelper.get(keyToSysPropKey(id));
- if (data.isEmpty()) {
- return null;
- }
- JSONObject json;
try {
- json = new JSONObject(data);
- if (!assertType(json, TYPE_BOOLEAN)) {
- return null;
- }
-
- return json.getBoolean(FIELD_VALUE);
- } catch (JSONException e) {
- eraseInternal(id); // Don't restart SystemUI in this case.
+ return mFlagManager.isEnabled(id);
+ } catch (Exception e) {
+ eraseInternal(id);
}
return null;
}
@@ -115,9 +108,9 @@
JSONObject json = new JSONObject();
try {
- json.put(FIELD_TYPE, TYPE_BOOLEAN);
+ json.put(FlagManager.FIELD_TYPE, FlagManager.TYPE_BOOLEAN);
json.put(FIELD_VALUE, value);
- mSystemPropertiesHelper.set(keyToSysPropKey(id), json.toString());
+ mSecureSettings.putString(mFlagManager.keyToSettingsPrefix(id), json.toString());
Log.i(TAG, "Set id " + id + " to " + value);
restartSystemUI();
} catch (JSONException e) {
@@ -134,15 +127,19 @@
/** Works just like {@link #eraseFlag(int)} except that it doesn't restart SystemUI. */
private void eraseInternal(int id) {
// We can't actually "erase" things from sysprops, but we can set them to empty!
- mSystemPropertiesHelper.set(keyToSysPropKey(id), "");
+ mSecureSettings.putString(mFlagManager.keyToSettingsPrefix(id), "");
Log.i(TAG, "Erase id " + id);
}
@Override
- public void addListener(Listener run) {}
+ public void addListener(Listener run) {
+ mFlagManager.addListener(run);
+ }
@Override
- public void removeListener(Listener run) {}
+ public void removeListener(Listener run) {
+ mFlagManager.removeListener(run);
+ }
private void restartSystemUI() {
Log.i(TAG, "Restarting SystemUI");
@@ -150,18 +147,6 @@
System.exit(0);
}
- private static String keyToSysPropKey(int key) {
- return SYSPROP_PREFIX + key;
- }
-
- private static boolean assertType(JSONObject json, String type) {
- try {
- return json.getString(FIELD_TYPE).equals(TYPE_BOOLEAN);
- } catch (JSONException e) {
- return false;
- }
- }
-
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -169,9 +154,15 @@
if (action == null) {
return;
}
-
if (ACTION_SET_FLAG.equals(action)) {
handleSetFlag(intent.getExtras());
+ } else if (ACTION_GET_FLAGS.equals(action)) {
+ Map<Integer, Flag<?>> knownFlagMap = Flags.collectFlags();
+ ArrayList<Flag<?>> flags = new ArrayList<>(knownFlagMap.values());
+ Bundle extras = getResultExtras(true);
+ if (extras != null) {
+ extras.putParcelableArrayList(FIELD_FLAGS, flags);
+ }
}
}
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
new file mode 100644
index 0000000..bee4d7d
--- /dev/null
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.systemui.flags
+
+import android.content.Context
+import android.os.Handler
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.util.settings.SettingsUtilModule
+import dagger.Module
+import dagger.Provides
+
+@Module(includes = [
+ SettingsUtilModule::class
+])
+object FlagsModule {
+ @JvmStatic
+ @Provides
+ fun provideFlagManager(context: Context, @Main handler: Handler): FlagManager {
+ return FlagManager(context, handler)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src-release/com/android/systemui/flags/FeatureFlagManager.java b/packages/SystemUI/src-release/com/android/systemui/flags/FeatureFlagManager.java
index e501a07..6ff175f 100644
--- a/packages/SystemUI/src-release/com/android/systemui/flags/FeatureFlagManager.java
+++ b/packages/SystemUI/src-release/com/android/systemui/flags/FeatureFlagManager.java
@@ -24,6 +24,7 @@
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.util.settings.SecureSettings;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -40,10 +41,17 @@
public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable {
SparseBooleanArray mAccessedFlags = new SparseBooleanArray();
@Inject
- public FeatureFlagManager(SystemPropertiesHelper systemPropertiesHelper, Context context,
- DumpManager dumpManager) {
+ public FeatureFlagManager(
+ SecureSettings secureSettings, Context context, DumpManager dumpManager) {
dumpManager.registerDumpable("SysUIFlags", this);
}
+
+ @Override
+ public void addListener(Listener run) {}
+
+ @Override
+ public void removeListener(Listener run) {}
+
@Override
public boolean isEnabled(int key, boolean defaultValue) {
mAccessedFlags.append(key, defaultValue);
diff --git a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
new file mode 100644
index 0000000..7647135
--- /dev/null
+++ b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.systemui.flags
+
+import dagger.Module
+
+@Module
+object FlagsModule
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
new file mode 100644
index 0000000..30aec66
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.keyguard
+
+import android.view.ViewGroup
+import com.android.systemui.unfold.SysUIUnfoldScope
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import javax.inject.Inject
+
+/**
+ * Translates items away/towards the hinge when the device is opened/closed.
+ */
+@SysUIUnfoldScope
+class KeyguardUnfoldTransition @Inject constructor(
+ val unfoldProgressProvider: UnfoldTransitionProgressProvider
+) {
+ init {
+ unfoldProgressProvider.addCallback(
+ object : TransitionProgressListener {
+ override fun onTransitionStarted() {
+ }
+
+ override fun onTransitionProgress(progress: Float) {
+ }
+
+ override fun onTransitionFinished() {
+ }
+ }
+ )
+ }
+
+ fun setup(parent: ViewGroup) {
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
index db729da..fcf1b2c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -20,11 +20,14 @@
import android.view.View;
import android.view.ViewRootImpl;
+import androidx.annotation.Nullable;
+
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
/**
* Interface to control Keyguard View. It should be implemented by KeyguardViewManagers, which
@@ -184,14 +187,10 @@
/**
* Registers the StatusBar to which this Keyguard View is mounted.
- * @param statusBar
- * @param notificationPanelViewController
- * @param biometricUnlockController
- * @param notificationContainer
- * @param bypassController
*/
void registerStatusBar(StatusBar statusBar,
NotificationPanelViewController notificationPanelViewController,
+ @Nullable PanelExpansionStateManager panelExpansionStateManager,
BiometricUnlockController biometricUnlockController,
View notificationContainer,
KeyguardBypassController bypassController);
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 2f5a18e..ce493d0 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -105,7 +105,6 @@
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
@@ -129,6 +128,7 @@
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
@@ -511,6 +511,7 @@
mProviders.put(KeyguardEnvironment.class, mKeyguardEnvironment::get);
mProviders.put(ShadeController.class, mShadeController::get);
+
mProviders.put(NotificationRemoteInputManager.Callback.class,
mNotificationRemoteInputManagerCallback::get);
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index c4147e7..b2fae9d 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -534,6 +534,9 @@
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
| WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+ // FLAG_SLIPPERY can only be set by trusted overlays
+ lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+
if (!DEBUG_SCREENSHOT_ROUNDED_CORNERS) {
lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 3096b3d..43a38aa 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -30,7 +30,9 @@
import com.android.systemui.dagger.WMComponent;
import com.android.systemui.navigationbar.gestural.BackGestureTfClassifierProvider;
import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider;
+import com.android.wm.shell.transition.ShellTransitions;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.recents.RecentTasks;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
@@ -119,7 +121,8 @@
.setTransitions(mWMComponent.getTransitions())
.setStartingSurface(mWMComponent.getStartingSurface())
.setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper())
- .setTaskSurfaceHelper(mWMComponent.getTaskSurfaceHelper());
+ .setTaskSurfaceHelper(mWMComponent.getTaskSurfaceHelper())
+ .setRecentTasks(mWMComponent.getRecentTasks());
} else {
// TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option
// is separating this logic into newly creating SystemUITestsFactory.
@@ -133,10 +136,11 @@
.setShellCommandHandler(Optional.ofNullable(null))
.setAppPairs(Optional.ofNullable(null))
.setTaskViewFactory(Optional.ofNullable(null))
- .setTransitions(Transitions.createEmptyForTesting())
+ .setTransitions(new ShellTransitions() {})
.setDisplayAreaHelper(Optional.ofNullable(null))
.setStartingSurface(Optional.ofNullable(null))
- .setTaskSurfaceHelper(Optional.ofNullable(null));
+ .setTaskSurfaceHelper(Optional.ofNullable(null))
+ .setRecentTasks(Optional.ofNullable(null));
}
mSysUIComponent = builder.build();
if (mInitializeComponents) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index f4b446b..5472d6b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -566,8 +566,8 @@
args.arg5 = requireConfirmation;
args.argi1 = userId;
args.arg6 = opPackageName;
- args.arg7 = operationId;
- args.arg8 = requestId;
+ args.argl1 = operationId;
+ args.argl2 = requestId;
args.argi2 = multiSensorConfig;
boolean skipAnimation = false;
@@ -725,8 +725,8 @@
final boolean requireConfirmation = (boolean) args.arg5;
final int userId = args.argi1;
final String opPackageName = (String) args.arg6;
- final long operationId = (long) args.arg7;
- final long requestId = (long) args.arg8;
+ final long operationId = args.argl1;
+ final long requestId = args.argl2;
final @BiometricMultiSensorMode int multiSensorConfig = args.argi2;
// Create a new dialog but do not replace the current one yet.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
index b7398d8..7bb4708 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
@@ -15,6 +15,9 @@
*/
package com.android.systemui.biometrics
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.app.ActivityTaskManager
import android.content.Context
import android.graphics.PixelFormat
import android.graphics.PorterDuff
@@ -22,6 +25,7 @@
import android.graphics.Rect
import android.hardware.biometrics.BiometricOverlayConstants
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
+import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
import android.hardware.display.DisplayManager
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
@@ -33,6 +37,8 @@
import android.view.LayoutInflater
import android.view.Surface
import android.view.View
+import android.view.ViewPropertyAnimator
+import android.view.WindowInsets
import android.view.WindowManager
import androidx.annotation.RawRes
import com.airbnb.lottie.LottieAnimationView
@@ -42,6 +48,7 @@
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.recents.OverviewProxyService
import com.android.systemui.util.concurrency.DelayableExecutor
import javax.inject.Inject
@@ -56,9 +63,11 @@
private val layoutInflater: LayoutInflater,
fingerprintManager: FingerprintManager?,
private val windowManager: WindowManager,
- @Main mainExecutor: DelayableExecutor,
+ private val activityTaskManager: ActivityTaskManager,
+ overviewProxyService: OverviewProxyService,
displayManager: DisplayManager,
- @Main handler: Handler
+ @Main mainExecutor: DelayableExecutor,
+ @Main private val handler: Handler
) {
@VisibleForTesting
val sensorProps: FingerprintSensorPropertiesInternal = fingerprintManager
@@ -74,15 +83,33 @@
BiometricDisplayListener.SensorType.SideFingerprint(sensorProps)
) { onOrientationChanged() }
+ @VisibleForTesting
+ val overviewProxyListener = object : OverviewProxyService.OverviewProxyListener {
+ override fun onTaskbarStatusUpdated(visible: Boolean, stashed: Boolean) {
+ overlayView?.let { view ->
+ handler.postDelayed({ updateOverlayVisibility(view) }, 500)
+ }
+ }
+ }
+
+ private val animationDuration =
+ context.resources.getInteger(android.R.integer.config_mediumAnimTime).toLong()
+
+ private var overlayHideAnimator: ViewPropertyAnimator? = null
+
private var overlayView: View? = null
set(value) {
field?.let { oldView ->
windowManager.removeView(oldView)
orientationListener.disable()
}
+ overlayHideAnimator?.cancel()
+ overlayHideAnimator = null
+
field = value
field?.let { newView ->
windowManager.addView(newView, overlayViewParams)
+ updateOverlayVisibility(newView)
orientationListener.enable()
}
}
@@ -90,11 +117,8 @@
private val overlayViewParams = WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
+ WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
+ Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS,
PixelFormat.TRANSLUCENT
).apply {
title = TAG
@@ -109,7 +133,7 @@
override fun show(
sensorId: Int,
@BiometricOverlayConstants.ShowReason reason: Int
- ) = if (reason.isReasonToShow()) doShow() else hide(sensorId)
+ ) = if (reason.isReasonToShow(activityTaskManager)) doShow() else hide(sensorId)
private fun doShow() = mainExecutor.execute {
if (overlayView == null) {
@@ -121,6 +145,7 @@
override fun hide(sensorId: Int) = mainExecutor.execute { overlayView = null }
})
+ overviewProxyService.addCallback(overviewProxyListener)
}
private fun onOrientationChanged() {
@@ -176,14 +201,49 @@
overlayViewParams.x = x
overlayViewParams.y = y
}
+
+ private fun updateOverlayVisibility(view: View) {
+ if (view != overlayView) {
+ return
+ }
+
+ // hide after a few seconds if the sensor is oriented down and there are
+ // large overlapping system bars
+ if ((context.display?.rotation == Surface.ROTATION_270) &&
+ windowManager.currentWindowMetrics.windowInsets.hasBigNavigationBar()) {
+ overlayHideAnimator = view.animate()
+ .alpha(0f)
+ .setStartDelay(3_000)
+ .setDuration(animationDuration)
+ .setListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ view.visibility = View.GONE
+ overlayHideAnimator = null
+ }
+ })
+ } else {
+ overlayHideAnimator?.cancel()
+ overlayHideAnimator = null
+ view.alpha = 1f
+ view.visibility = View.VISIBLE
+ }
+ }
}
@BiometricOverlayConstants.ShowReason
-private fun Int.isReasonToShow(): Boolean = when (this) {
+private fun Int.isReasonToShow(activityTaskManager: ActivityTaskManager): Boolean = when (this) {
REASON_AUTH_KEYGUARD -> false
+ REASON_AUTH_SETTINGS -> when (activityTaskManager.topClass()) {
+ // TODO(b/186176653): exclude fingerprint overlays from this list view
+ "com.android.settings.biometrics.fingerprint.FingerprintSettings" -> false
+ else -> true
+ }
else -> true
}
+private fun ActivityTaskManager.topClass(): String =
+ getTasks(1).firstOrNull()?.topActivity?.className ?: ""
+
@RawRes
private fun Display.asSideFpsAnimation(): Int = when (rotation) {
Surface.ROTATION_0 -> R.raw.sfps_pulse
@@ -200,6 +260,9 @@
private fun Display.isPortrait(): Boolean =
rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
+private fun WindowInsets.hasBigNavigationBar(): Boolean =
+ getInsets(WindowInsets.Type.navigationBars()).bottom >= 70
+
private fun LottieAnimationView.addOverlayDynamicColor(context: Context) {
fun update() {
val c = context.getColor(R.color.biometric_dialog_accent)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
index a2e55c0..9474340 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
@@ -23,12 +23,12 @@
import com.android.systemui.Dumpable;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.util.ViewController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.Optional;
/**
* Handles:
@@ -43,7 +43,7 @@
abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView>
extends ViewController<T> implements Dumpable {
@NonNull final StatusBarStateController mStatusBarStateController;
- @NonNull final Optional<StatusBar> mStatusBarOptional;
+ @NonNull final PanelExpansionStateManager mPanelExpansionStateManager;
@NonNull final DumpManager mDumpManger;
boolean mNotificationShadeExpanded;
@@ -51,11 +51,11 @@
protected UdfpsAnimationViewController(
T view,
@NonNull StatusBarStateController statusBarStateController,
- @NonNull Optional<StatusBar> statusBarOptional,
+ @NonNull PanelExpansionStateManager panelExpansionStateManager,
@NonNull DumpManager dumpManager) {
super(view);
mStatusBarStateController = statusBarStateController;
- mStatusBarOptional = statusBarOptional;
+ mPanelExpansionStateManager = panelExpansionStateManager;
mDumpManger = dumpManager;
}
@@ -63,17 +63,13 @@
@Override
protected void onViewAttached() {
- mStatusBarOptional.ifPresent(
- statusBar -> statusBar.addExpansionChangedListener(
- mStatusBarExpansionChangedListener));
+ mPanelExpansionStateManager.addExpansionListener(mPanelExpansionListener);
mDumpManger.registerDumpable(getDumpTag(), this);
}
@Override
protected void onViewDetached() {
- mStatusBarOptional.ifPresent(
- statusBar -> statusBar.removeExpansionChangedListener(
- mStatusBarExpansionChangedListener));
+ mPanelExpansionStateManager.removeExpansionListener(mPanelExpansionListener);
mDumpManger.unregisterDumpable(getDumpTag());
}
@@ -182,13 +178,13 @@
*/
void onTouchOutsideView() { }
- private final StatusBar.ExpansionChangedListener mStatusBarExpansionChangedListener =
- new StatusBar.ExpansionChangedListener() {
- @Override
- public void onExpansionChanged(float expansion, boolean expanded) {
- mNotificationShadeExpanded = expanded;
- mView.onExpansionChanged(expansion, expanded);
- updatePauseAuth();
- }
- };
+ private final PanelExpansionListener mPanelExpansionListener = new PanelExpansionListener() {
+ @Override
+ public void onPanelExpansionChanged(
+ float fraction, boolean expanded, boolean tracking) {
+ mNotificationShadeExpanded = expanded;
+ mView.onExpansionChanged(fraction, expanded);
+ updatePauseAuth();
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
index 85955e1..894b295 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
@@ -20,9 +20,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.StatusBar;
-
-import java.util.Optional;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
/**
* Class that coordinates non-HBM animations for biometric prompt.
@@ -31,9 +29,9 @@
protected UdfpsBpViewController(
@NonNull UdfpsBpView view,
@NonNull StatusBarStateController statusBarStateController,
- @NonNull Optional<StatusBar> statusBarOptional,
+ @NonNull PanelExpansionStateManager panelExpansionStateManager,
@NonNull DumpManager dumpManager) {
- super(view, statusBarStateController, statusBarOptional, dumpManager);
+ super(view, statusBarStateController, panelExpansionStateManager, dumpManager);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index f7462b6..a517ae2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -69,9 +69,9 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -112,7 +112,7 @@
@NonNull private final LayoutInflater mInflater;
private final WindowManager mWindowManager;
private final DelayableExecutor mFgExecutor;
- @NonNull private final Optional<StatusBar> mStatusBarOptional;
+ @NonNull private final PanelExpansionStateManager mPanelExpansionStateManager;
@NonNull private final StatusBarStateController mStatusBarStateController;
@NonNull private final KeyguardStateController mKeyguardStateController;
@NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
@@ -521,7 +521,7 @@
@NonNull WindowManager windowManager,
@NonNull StatusBarStateController statusBarStateController,
@Main DelayableExecutor fgExecutor,
- @NonNull Optional<StatusBar> statusBarOptional,
+ @NonNull PanelExpansionStateManager panelExpansionStateManager,
@NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@NonNull DumpManager dumpManager,
@NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -550,7 +550,7 @@
mFingerprintManager = checkNotNull(fingerprintManager);
mWindowManager = windowManager;
mFgExecutor = fgExecutor;
- mStatusBarOptional = statusBarOptional;
+ mPanelExpansionStateManager = panelExpansionStateManager;
mStatusBarStateController = statusBarStateController;
mKeyguardStateController = keyguardStateController;
mKeyguardViewManager = statusBarKeyguardViewManager;
@@ -583,7 +583,7 @@
mCoreLayoutParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
- getCoreLayoutParamFlags(),
+ Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS,
PixelFormat.TRANSLUCENT);
mCoreLayoutParams.setTitle(TAG);
mCoreLayoutParams.setFitInsetsTypes(0);
@@ -616,13 +616,6 @@
}
}
- private int getCoreLayoutParamFlags() {
- return WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
- }
-
@Nullable
private FingerprintSensorPropertiesInternal findFirstUdfps() {
for (FingerprintSensorPropertiesInternal props :
@@ -685,7 +678,7 @@
final int paddingX = animation != null ? animation.getPaddingX() : 0;
final int paddingY = animation != null ? animation.getPaddingY() : 0;
- mCoreLayoutParams.flags = getCoreLayoutParamFlags();
+ mCoreLayoutParams.flags = Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS;
if (animation != null && animation.listenForTouchesOutsideView()) {
mCoreLayoutParams.flags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
}
@@ -767,10 +760,12 @@
mOnFingerDown = false;
mView.setSensorProperties(mSensorProps);
mView.setHbmProvider(mHbmProvider);
- UdfpsAnimationViewController animation = inflateUdfpsAnimation(reason);
+ UdfpsAnimationViewController<?> animation = inflateUdfpsAnimation(reason);
mAttemptedToDismissKeyguard = false;
- animation.init();
- mView.setAnimationViewController(animation);
+ if (animation != null) {
+ animation.init();
+ mView.setAnimationViewController(animation);
+ }
mOrientationListener.enable();
// This view overlaps the sensor area, so prevent it from being selectable
@@ -793,7 +788,8 @@
}
}
- private UdfpsAnimationViewController inflateUdfpsAnimation(int reason) {
+ @Nullable
+ private UdfpsAnimationViewController<?> inflateUdfpsAnimation(int reason) {
switch (reason) {
case BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR:
case BiometricOverlayConstants.REASON_ENROLL_ENROLLING:
@@ -805,7 +801,7 @@
enrollView,
mServerRequest.mEnrollHelper,
mStatusBarStateController,
- mStatusBarOptional,
+ mPanelExpansionStateManager,
mDumpManager
);
case BiometricOverlayConstants.REASON_AUTH_KEYGUARD:
@@ -815,7 +811,7 @@
return new UdfpsKeyguardViewController(
keyguardView,
mStatusBarStateController,
- mStatusBarOptional,
+ mPanelExpansionStateManager,
mKeyguardViewManager,
mKeyguardUpdateMonitor,
mDumpManager,
@@ -833,21 +829,22 @@
return new UdfpsBpViewController(
bpView,
mStatusBarStateController,
- mStatusBarOptional,
+ mPanelExpansionStateManager,
mDumpManager
);
case BiometricOverlayConstants.REASON_AUTH_OTHER:
+ case BiometricOverlayConstants.REASON_AUTH_SETTINGS:
UdfpsFpmOtherView authOtherView = (UdfpsFpmOtherView)
mInflater.inflate(R.layout.udfps_fpm_other_view, null);
mView.addView(authOtherView);
return new UdfpsFpmOtherViewController(
authOtherView,
mStatusBarStateController,
- mStatusBarOptional,
+ mPanelExpansionStateManager,
mDumpManager
);
default:
- Log.d(TAG, "Animation for reason " + reason + " not supported yet");
+ Log.e(TAG, "Animation for reason " + reason + " not supported yet");
return null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
index 2034ff3..1f01fc5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
@@ -115,7 +115,8 @@
mBlueFill.setColor(context.getColor(R.color.udfps_moving_target_fill));
mBlueFill.setStyle(Paint.Style.FILL);
- mMovingTargetFpIcon = context.getResources().getDrawable(R.drawable.ic_fingerprint, null);
+ mMovingTargetFpIcon = context.getResources()
+ .getDrawable(R.drawable.ic_kg_fingerprint, null);
mMovingTargetFpIcon.setTint(Color.WHITE);
mMovingTargetFpIcon.mutate();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
index 11addf0..79c7e66 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
@@ -16,17 +16,22 @@
package com.android.systemui.biometrics;
+import android.animation.ValueAnimator;
import android.content.Context;
+import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
+import android.graphics.Paint;
import android.graphics.drawable.Drawable;
-import android.util.Log;
+import android.util.TypedValue;
+import android.view.animation.Interpolator;
+import android.view.animation.OvershootInterpolator;
+import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import java.util.ArrayList;
-import java.util.List;
+import com.android.systemui.R;
/**
* UDFPS enrollment progress bar.
@@ -34,108 +39,193 @@
public class UdfpsEnrollProgressBarDrawable extends Drawable {
private static final String TAG = "UdfpsProgressBar";
- private static final float SEGMENT_GAP_ANGLE = 12f;
+ private static final long CHECKMARK_ANIMATION_DELAY_MS = 200L;
+ private static final long CHECKMARK_ANIMATION_DURATION_MS = 300L;
+ private static final long FILL_COLOR_ANIMATION_DURATION_MS = 200L;
+ private static final long PROGRESS_ANIMATION_DURATION_MS = 400L;
+ private static final float STROKE_WIDTH_DP = 12f;
- @NonNull private final Context mContext;
+ private final float mStrokeWidthPx;
+ @ColorInt private final int mProgressColor;
+ @ColorInt private final int mHelpColor;
+ @NonNull private final Drawable mCheckmarkDrawable;
+ @NonNull private final Interpolator mCheckmarkInterpolator;
+ @NonNull private final Paint mBackgroundPaint;
+ @NonNull private final Paint mFillPaint;
- @Nullable private UdfpsEnrollHelper mEnrollHelper;
- @NonNull private List<UdfpsEnrollProgressBarSegment> mSegments = new ArrayList<>();
+ private boolean mAfterFirstTouch;
+
+ private int mRemainingSteps = 0;
private int mTotalSteps = 0;
- private int mProgressSteps = 0;
- private boolean mIsShowingHelp = false;
+ private float mProgress = 0f;
+ @Nullable private ValueAnimator mProgressAnimator;
+ @NonNull private final ValueAnimator.AnimatorUpdateListener mProgressUpdateListener;
+
+ private boolean mShowingHelp = false;
+ @Nullable private ValueAnimator mFillColorAnimator;
+ @NonNull private final ValueAnimator.AnimatorUpdateListener mFillColorUpdateListener;
+
+ private boolean mComplete = false;
+ private float mCheckmarkScale = 0f;
+ @Nullable private ValueAnimator mCheckmarkAnimator;
+ @NonNull private final ValueAnimator.AnimatorUpdateListener mCheckmarkUpdateListener;
public UdfpsEnrollProgressBarDrawable(@NonNull Context context) {
- mContext = context;
- }
+ mStrokeWidthPx = Utils.dpToPixels(context, STROKE_WIDTH_DP);
+ mProgressColor = context.getColor(R.color.udfps_enroll_progress);
+ mHelpColor = context.getColor(R.color.udfps_enroll_progress_help);
+ mCheckmarkDrawable = context.getDrawable(R.drawable.udfps_enroll_checkmark);
+ mCheckmarkDrawable.mutate();
+ mCheckmarkInterpolator = new OvershootInterpolator();
- void setEnrollHelper(@Nullable UdfpsEnrollHelper enrollHelper) {
- mEnrollHelper = enrollHelper;
- if (enrollHelper != null) {
- final int stageCount = enrollHelper.getStageCount();
- mSegments = new ArrayList<>(stageCount);
- float startAngle = SEGMENT_GAP_ANGLE / 2f;
- final float sweepAngle = (360f / stageCount) - SEGMENT_GAP_ANGLE;
- final Runnable invalidateRunnable = this::invalidateSelf;
- for (int index = 0; index < stageCount; index++) {
- mSegments.add(new UdfpsEnrollProgressBarSegment(mContext, getBounds(), startAngle,
- sweepAngle, SEGMENT_GAP_ANGLE, invalidateRunnable));
- startAngle += sweepAngle + SEGMENT_GAP_ANGLE;
- }
- invalidateSelf();
+ mBackgroundPaint = new Paint();
+ mBackgroundPaint.setStrokeWidth(mStrokeWidthPx);
+ mBackgroundPaint.setColor(context.getColor(R.color.white_disabled));
+ mBackgroundPaint.setAntiAlias(true);
+ mBackgroundPaint.setStyle(Paint.Style.STROKE);
+ mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND);
+
+ // Set background paint color and alpha.
+ final int[] attrs = new int[] {android.R.attr.colorControlNormal};
+ final TypedArray typedArray = context.obtainStyledAttributes(attrs);
+ try {
+ @ColorInt final int tintColor = typedArray.getColor(0, mBackgroundPaint.getColor());
+ mBackgroundPaint.setColor(tintColor);
+ } finally {
+ typedArray.recycle();
}
+ TypedValue alpha = new TypedValue();
+ context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, alpha, true);
+ mBackgroundPaint.setAlpha((int) (alpha.getFloat() * 255f));
+
+ // Progress fill should *not* use the extracted system color.
+ mFillPaint = new Paint();
+ mFillPaint.setStrokeWidth(mStrokeWidthPx);
+ mFillPaint.setColor(mProgressColor);
+ mFillPaint.setAntiAlias(true);
+ mFillPaint.setStyle(Paint.Style.STROKE);
+ mFillPaint.setStrokeCap(Paint.Cap.ROUND);
+
+ mProgressUpdateListener = animation -> {
+ mProgress = (float) animation.getAnimatedValue();
+ invalidateSelf();
+ };
+
+ mFillColorUpdateListener = animation -> {
+ mFillPaint.setColor((int) animation.getAnimatedValue());
+ invalidateSelf();
+ };
+
+ mCheckmarkUpdateListener = animation -> {
+ mCheckmarkScale = (float) animation.getAnimatedValue();
+ invalidateSelf();
+ };
}
void onEnrollmentProgress(int remaining, int totalSteps) {
- mTotalSteps = totalSteps;
-
- // Show some progress for the initial touch.
- updateState(Math.max(1, totalSteps - remaining), false /* isShowingHelp */);
+ mAfterFirstTouch = true;
+ updateState(remaining, totalSteps, false /* showingHelp */);
}
void onEnrollmentHelp(int remaining, int totalSteps) {
- updateState(Math.max(0, totalSteps - remaining), true /* isShowingHelp */);
+ updateState(remaining, totalSteps, true /* showingHelp */);
}
void onLastStepAcquired() {
- updateState(mTotalSteps, false /* isShowingHelp */);
+ updateState(0, mTotalSteps, false /* showingHelp */);
}
- private void updateState(int progressSteps, boolean isShowingHelp) {
- updateProgress(progressSteps);
- updateFillColor(isShowingHelp);
+ private void updateState(int remainingSteps, int totalSteps, boolean showingHelp) {
+ updateProgress(remainingSteps, totalSteps);
+ updateFillColor(showingHelp);
}
- private void updateProgress(int progressSteps) {
- if (mProgressSteps == progressSteps) {
+ private void updateProgress(int remainingSteps, int totalSteps) {
+ if (mRemainingSteps == remainingSteps && mTotalSteps == totalSteps) {
return;
}
- mProgressSteps = progressSteps;
+ mRemainingSteps = remainingSteps;
+ mTotalSteps = totalSteps;
- if (mEnrollHelper == null) {
- Log.e(TAG, "updateState: UDFPS enroll helper was null");
- return;
+ final int progressSteps = Math.max(0, totalSteps - remainingSteps);
+
+ // If needed, add 1 to progress and total steps to account for initial touch.
+ final int adjustedSteps = mAfterFirstTouch ? progressSteps + 1 : progressSteps;
+ final int adjustedTotal = mAfterFirstTouch ? mTotalSteps + 1 : mTotalSteps;
+
+ final float targetProgress = Math.min(1f, (float) adjustedSteps / (float) adjustedTotal);
+
+ if (mProgressAnimator != null && mProgressAnimator.isRunning()) {
+ mProgressAnimator.cancel();
}
- int index = 0;
- int prevThreshold = 0;
- while (index < mSegments.size()) {
- final UdfpsEnrollProgressBarSegment segment = mSegments.get(index);
- final int thresholdSteps = mEnrollHelper.getStageThresholdSteps(mTotalSteps, index);
- if (progressSteps >= thresholdSteps && segment.getProgress() < 1f) {
- segment.updateProgress(1f);
- break;
- } else if (progressSteps >= prevThreshold && progressSteps < thresholdSteps) {
- final int relativeSteps = progressSteps - prevThreshold;
- final int relativeThreshold = thresholdSteps - prevThreshold;
- final float segmentProgress = (float) relativeSteps / (float) relativeThreshold;
- segment.updateProgress(segmentProgress);
- break;
- }
+ mProgressAnimator = ValueAnimator.ofFloat(mProgress, targetProgress);
+ mProgressAnimator.setDuration(PROGRESS_ANIMATION_DURATION_MS);
+ mProgressAnimator.addUpdateListener(mProgressUpdateListener);
+ mProgressAnimator.start();
- index++;
- prevThreshold = thresholdSteps;
- }
-
- if (progressSteps >= mTotalSteps) {
- for (final UdfpsEnrollProgressBarSegment segment : mSegments) {
- segment.startCompletionAnimation();
- }
- } else {
- for (final UdfpsEnrollProgressBarSegment segment : mSegments) {
- segment.cancelCompletionAnimation();
- }
+ if (remainingSteps == 0) {
+ startCompletionAnimation();
+ } else if (remainingSteps > 0) {
+ rollBackCompletionAnimation();
}
}
- private void updateFillColor(boolean isShowingHelp) {
- if (mIsShowingHelp == isShowingHelp) {
+ private void updateFillColor(boolean showingHelp) {
+ if (mShowingHelp == showingHelp) {
return;
}
- mIsShowingHelp = isShowingHelp;
+ mShowingHelp = showingHelp;
- for (final UdfpsEnrollProgressBarSegment segment : mSegments) {
- segment.updateFillColor(isShowingHelp);
+ if (mFillColorAnimator != null && mFillColorAnimator.isRunning()) {
+ mFillColorAnimator.cancel();
}
+
+ @ColorInt final int targetColor = showingHelp ? mHelpColor : mProgressColor;
+ mFillColorAnimator = ValueAnimator.ofArgb(mFillPaint.getColor(), targetColor);
+ mFillColorAnimator.setDuration(FILL_COLOR_ANIMATION_DURATION_MS);
+ mFillColorAnimator.addUpdateListener(mFillColorUpdateListener);
+ mFillColorAnimator.start();
+ }
+
+ private void startCompletionAnimation() {
+ if (mComplete) {
+ return;
+ }
+ mComplete = true;
+
+ if (mCheckmarkAnimator != null && mCheckmarkAnimator.isRunning()) {
+ mCheckmarkAnimator.cancel();
+ }
+
+ mCheckmarkAnimator = ValueAnimator.ofFloat(mCheckmarkScale, 1f);
+ mCheckmarkAnimator.setStartDelay(CHECKMARK_ANIMATION_DELAY_MS);
+ mCheckmarkAnimator.setDuration(CHECKMARK_ANIMATION_DURATION_MS);
+ mCheckmarkAnimator.setInterpolator(mCheckmarkInterpolator);
+ mCheckmarkAnimator.addUpdateListener(mCheckmarkUpdateListener);
+ mCheckmarkAnimator.start();
+ }
+
+ private void rollBackCompletionAnimation() {
+ if (!mComplete) {
+ return;
+ }
+ mComplete = false;
+
+ // Adjust duration based on how much of the completion animation has played.
+ final float animatedFraction = mCheckmarkAnimator != null
+ ? mCheckmarkAnimator.getAnimatedFraction()
+ : 0f;
+ final long durationMs = Math.round(CHECKMARK_ANIMATION_DELAY_MS * animatedFraction);
+
+ if (mCheckmarkAnimator != null && mCheckmarkAnimator.isRunning()) {
+ mCheckmarkAnimator.cancel();
+ }
+
+ mCheckmarkAnimator = ValueAnimator.ofFloat(mCheckmarkScale, 0f);
+ mCheckmarkAnimator.setDuration(durationMs);
+ mCheckmarkAnimator.addUpdateListener(mCheckmarkUpdateListener);
+ mCheckmarkAnimator.start();
}
@Override
@@ -145,12 +235,55 @@
// Progress starts from the top, instead of the right
canvas.rotate(-90f, getBounds().centerX(), getBounds().centerY());
- // Draw each of the enroll segments.
- for (final UdfpsEnrollProgressBarSegment segment : mSegments) {
- segment.draw(canvas);
+ final float halfPaddingPx = mStrokeWidthPx / 2f;
+
+ if (mProgress < 1f) {
+ // Draw the background color of the progress circle.
+ canvas.drawArc(
+ halfPaddingPx,
+ halfPaddingPx,
+ getBounds().right - halfPaddingPx,
+ getBounds().bottom - halfPaddingPx,
+ 0f /* startAngle */,
+ 360f /* sweepAngle */,
+ false /* useCenter */,
+ mBackgroundPaint);
+ }
+
+ if (mProgress > 0f) {
+ // Draw the filled portion of the progress circle.
+ canvas.drawArc(
+ halfPaddingPx,
+ halfPaddingPx,
+ getBounds().right - halfPaddingPx,
+ getBounds().bottom - halfPaddingPx,
+ 0f /* startAngle */,
+ 360f * mProgress /* sweepAngle */,
+ false /* useCenter */,
+ mFillPaint);
}
canvas.restore();
+
+ if (mCheckmarkScale > 0f) {
+ final float offsetScale = (float) Math.sqrt(2) / 2f;
+ final float centerXOffset = (getBounds().width() - mStrokeWidthPx) / 2f * offsetScale;
+ final float centerYOffset = (getBounds().height() - mStrokeWidthPx) / 2f * offsetScale;
+ final float centerX = getBounds().centerX() + centerXOffset;
+ final float centerY = getBounds().centerY() + centerYOffset;
+
+ final float boundsXOffset =
+ mCheckmarkDrawable.getIntrinsicWidth() / 2f * mCheckmarkScale;
+ final float boundsYOffset =
+ mCheckmarkDrawable.getIntrinsicHeight() / 2f * mCheckmarkScale;
+
+ final int left = Math.round(centerX - boundsXOffset);
+ final int top = Math.round(centerY - boundsYOffset);
+ final int right = Math.round(centerX + boundsXOffset);
+ final int bottom = Math.round(centerY + boundsYOffset);
+ mCheckmarkDrawable.setBounds(left, top, right, bottom);
+ mCheckmarkDrawable.draw(canvas);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarSegment.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarSegment.java
deleted file mode 100644
index bd6ab44..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarSegment.java
+++ /dev/null
@@ -1,280 +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 com.android.systemui.biometrics;
-
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.Log;
-import android.util.TypedValue;
-
-import androidx.annotation.ColorInt;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.systemui.R;
-
-/**
- * A single segment of the UDFPS enrollment progress bar.
- */
-public class UdfpsEnrollProgressBarSegment {
- private static final String TAG = "UdfpsProgressBarSegment";
-
- private static final long FILL_COLOR_ANIMATION_DURATION_MS = 200L;
- private static final long PROGRESS_ANIMATION_DURATION_MS = 400L;
- private static final long OVER_SWEEP_ANIMATION_DELAY_MS = 200L;
- private static final long OVER_SWEEP_ANIMATION_DURATION_MS = 200L;
-
- private static final float STROKE_WIDTH_DP = 12f;
-
- private final Handler mHandler = new Handler(Looper.getMainLooper());
-
- @NonNull private final Rect mBounds;
- @NonNull private final Runnable mInvalidateRunnable;
- private final float mStartAngle;
- private final float mSweepAngle;
- private final float mMaxOverSweepAngle;
- private final float mStrokeWidthPx;
- @ColorInt private final int mProgressColor;
- @ColorInt private final int mHelpColor;
-
- @NonNull private final Paint mBackgroundPaint;
- @NonNull private final Paint mProgressPaint;
-
- private float mProgress = 0f;
- private float mAnimatedProgress = 0f;
- @Nullable private ValueAnimator mProgressAnimator;
- @NonNull private final ValueAnimator.AnimatorUpdateListener mProgressUpdateListener;
-
- private boolean mIsShowingHelp = false;
- @Nullable private ValueAnimator mFillColorAnimator;
- @NonNull private final ValueAnimator.AnimatorUpdateListener mFillColorUpdateListener;
-
- private float mOverSweepAngle = 0f;
- @Nullable private ValueAnimator mOverSweepAnimator;
- @Nullable private ValueAnimator mOverSweepReverseAnimator;
- @NonNull private final ValueAnimator.AnimatorUpdateListener mOverSweepUpdateListener;
- @NonNull private final Runnable mOverSweepAnimationRunnable;
-
- public UdfpsEnrollProgressBarSegment(@NonNull Context context, @NonNull Rect bounds,
- float startAngle, float sweepAngle, float maxOverSweepAngle,
- @NonNull Runnable invalidateRunnable) {
-
- mBounds = bounds;
- mInvalidateRunnable = invalidateRunnable;
- mStartAngle = startAngle;
- mSweepAngle = sweepAngle;
- mMaxOverSweepAngle = maxOverSweepAngle;
- mStrokeWidthPx = Utils.dpToPixels(context, STROKE_WIDTH_DP);
- mProgressColor = context.getColor(R.color.udfps_enroll_progress);
- mHelpColor = context.getColor(R.color.udfps_enroll_progress_help);
-
- mBackgroundPaint = new Paint();
- mBackgroundPaint.setStrokeWidth(mStrokeWidthPx);
- mBackgroundPaint.setColor(context.getColor(R.color.white_disabled));
- mBackgroundPaint.setAntiAlias(true);
- mBackgroundPaint.setStyle(Paint.Style.STROKE);
- mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND);
-
- // Background paint color + alpha
- final int[] attrs = new int[] {android.R.attr.colorControlNormal};
- final TypedArray ta = context.obtainStyledAttributes(attrs);
- @ColorInt final int tintColor = ta.getColor(0, mBackgroundPaint.getColor());
- mBackgroundPaint.setColor(tintColor);
- ta.recycle();
- TypedValue alpha = new TypedValue();
- context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, alpha, true);
- mBackgroundPaint.setAlpha((int) (alpha.getFloat() * 255f));
-
- // Progress should not be color extracted
- mProgressPaint = new Paint();
- mProgressPaint.setStrokeWidth(mStrokeWidthPx);
- mProgressPaint.setColor(mProgressColor);
- mProgressPaint.setAntiAlias(true);
- mProgressPaint.setStyle(Paint.Style.STROKE);
- mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
-
- mProgressUpdateListener = animation -> {
- mAnimatedProgress = (float) animation.getAnimatedValue();
- mInvalidateRunnable.run();
- };
-
- mFillColorUpdateListener = animation -> {
- mProgressPaint.setColor((int) animation.getAnimatedValue());
- mInvalidateRunnable.run();
- };
-
- mOverSweepUpdateListener = animation -> {
- mOverSweepAngle = (float) animation.getAnimatedValue();
- mInvalidateRunnable.run();
- };
- mOverSweepAnimationRunnable = () -> {
- if (mOverSweepAnimator != null && mOverSweepAnimator.isRunning()) {
- mOverSweepAnimator.cancel();
- }
- mOverSweepAnimator = ValueAnimator.ofFloat(mOverSweepAngle, mMaxOverSweepAngle);
- mOverSweepAnimator.setDuration(OVER_SWEEP_ANIMATION_DURATION_MS);
- mOverSweepAnimator.addUpdateListener(mOverSweepUpdateListener);
- mOverSweepAnimator.start();
- };
- }
-
- /**
- * Draws this segment to the given canvas.
- */
- public void draw(@NonNull Canvas canvas) {
- final float halfPaddingPx = mStrokeWidthPx / 2f;
-
- if (mAnimatedProgress < 1f) {
- // Draw the unfilled background color of the segment.
- canvas.drawArc(
- halfPaddingPx,
- halfPaddingPx,
- mBounds.right - halfPaddingPx,
- mBounds.bottom - halfPaddingPx,
- mStartAngle,
- mSweepAngle,
- false /* useCenter */,
- mBackgroundPaint);
- }
-
- if (mAnimatedProgress > 0f) {
- // Draw the filled progress portion of the segment.
- canvas.drawArc(
- halfPaddingPx,
- halfPaddingPx,
- mBounds.right - halfPaddingPx,
- mBounds.bottom - halfPaddingPx,
- mStartAngle,
- mSweepAngle * mAnimatedProgress + mOverSweepAngle,
- false /* useCenter */,
- mProgressPaint);
- }
- }
-
- /**
- * @return The fill progress of this segment, in the range [0, 1]. If fill progress is being
- * animated, returns the value it is animating to.
- */
- public float getProgress() {
- return mProgress;
- }
-
- /**
- * Updates the fill progress of this segment, animating if necessary.
- *
- * @param progress The new fill progress, in the range [0, 1].
- */
- public void updateProgress(float progress) {
- updateProgress(progress, PROGRESS_ANIMATION_DURATION_MS);
- }
-
- private void updateProgress(float progress, long animationDurationMs) {
- if (mProgress == progress) {
- return;
- }
- mProgress = progress;
-
- if (mProgressAnimator != null && mProgressAnimator.isRunning()) {
- mProgressAnimator.cancel();
- }
-
- mProgressAnimator = ValueAnimator.ofFloat(mAnimatedProgress, progress);
- mProgressAnimator.setDuration(animationDurationMs);
- mProgressAnimator.addUpdateListener(mProgressUpdateListener);
- mProgressAnimator.start();
- }
-
- /**
- * Updates the fill color of this segment, animating if necessary.
- *
- * @param isShowingHelp Whether fill color should indicate that a help message is being shown.
- */
- public void updateFillColor(boolean isShowingHelp) {
- if (mIsShowingHelp == isShowingHelp) {
- return;
- }
- mIsShowingHelp = isShowingHelp;
-
- if (mFillColorAnimator != null && mFillColorAnimator.isRunning()) {
- mFillColorAnimator.cancel();
- }
-
- @ColorInt final int targetColor = isShowingHelp ? mHelpColor : mProgressColor;
- mFillColorAnimator = ValueAnimator.ofArgb(mProgressPaint.getColor(), targetColor);
- mFillColorAnimator.setDuration(FILL_COLOR_ANIMATION_DURATION_MS);
- mFillColorAnimator.addUpdateListener(mFillColorUpdateListener);
- mFillColorAnimator.start();
- }
-
- /**
- * Queues and runs the completion animation for this segment.
- */
- public void startCompletionAnimation() {
- final boolean hasCallback = mHandler.hasCallbacks(mOverSweepAnimationRunnable);
- if (hasCallback || mOverSweepAngle >= mMaxOverSweepAngle) {
- Log.d(TAG, "startCompletionAnimation skipped: hasCallback = " + hasCallback
- + ", mOverSweepAngle = " + mOverSweepAngle);
- return;
- }
-
- // Reset sweep angle back to zero if the animation is being rolled back.
- if (mOverSweepReverseAnimator != null && mOverSweepReverseAnimator.isRunning()) {
- mOverSweepReverseAnimator.cancel();
- mOverSweepAngle = 0f;
- }
-
- // Clear help color and start filling the segment if it isn't already.
- if (mAnimatedProgress < 1f) {
- updateProgress(1f, OVER_SWEEP_ANIMATION_DELAY_MS);
- updateFillColor(false /* isShowingHelp */);
- }
-
- // Queue the animation to run after fill completes.
- mHandler.postDelayed(mOverSweepAnimationRunnable, OVER_SWEEP_ANIMATION_DELAY_MS);
- }
-
- /**
- * Cancels (and reverses, if necessary) a queued or running completion animation.
- */
- public void cancelCompletionAnimation() {
- // Cancel the animation if it's queued or running.
- mHandler.removeCallbacks(mOverSweepAnimationRunnable);
- if (mOverSweepAnimator != null && mOverSweepAnimator.isRunning()) {
- mOverSweepAnimator.cancel();
- }
-
- // Roll back the animation if it has at least partially run.
- if (mOverSweepAngle > 0f) {
- if (mOverSweepReverseAnimator != null && mOverSweepReverseAnimator.isRunning()) {
- mOverSweepReverseAnimator.cancel();
- }
-
- final float completion = mOverSweepAngle / mMaxOverSweepAngle;
- final long proratedDuration = (long) (OVER_SWEEP_ANIMATION_DURATION_MS * completion);
- mOverSweepReverseAnimator = ValueAnimator.ofFloat(mOverSweepAngle, 0f);
- mOverSweepReverseAnimator.setDuration(proratedDuration);
- mOverSweepReverseAnimator.addUpdateListener(mOverSweepUpdateListener);
- mOverSweepReverseAnimator.start();
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
index 729838e..93df2cf 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
@@ -73,7 +73,6 @@
}
void setEnrollHelper(UdfpsEnrollHelper enrollHelper) {
- mFingerprintProgressDrawable.setEnrollHelper(enrollHelper);
mFingerprintDrawable.setEnrollHelper(enrollHelper);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
index af7c352..292a904 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
@@ -22,9 +22,7 @@
import com.android.systemui.R;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.StatusBar;
-
-import java.util.Optional;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
/**
* Class that coordinates non-HBM animations during enrollment.
@@ -55,9 +53,9 @@
@NonNull UdfpsEnrollView view,
@NonNull UdfpsEnrollHelper enrollHelper,
@NonNull StatusBarStateController statusBarStateController,
- @NonNull Optional<StatusBar> statusBarOptional,
+ @NonNull PanelExpansionStateManager panelExpansionStateManager,
@NonNull DumpManager dumpManager) {
- super(view, statusBarStateController, statusBarOptional, dumpManager);
+ super(view, statusBarStateController, panelExpansionStateManager, dumpManager);
mEnrollProgressBarRadius = getContext().getResources()
.getInteger(R.integer.config_udfpsEnrollProgressBar);
mEnrollHelper = enrollHelper;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
index dcb5aef..6198733 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
@@ -20,9 +20,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.StatusBar;
-
-import java.util.Optional;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
/**
* Class that coordinates non-HBM animations for non keyguard, enrollment or biometric prompt
@@ -34,9 +32,9 @@
protected UdfpsFpmOtherViewController(
@NonNull UdfpsFpmOtherView view,
@NonNull StatusBarStateController statusBarStateController,
- @NonNull Optional<StatusBar> statusBarOptional,
+ @NonNull PanelExpansionStateManager panelExpansionStateManager,
@NonNull DumpManager dumpManager) {
- super(view, statusBarStateController, statusBarOptional, dumpManager);
+ super(view, statusBarStateController, panelExpansionStateManager, dumpManager);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 7a28c9d..d1ea45c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -30,16 +30,16 @@
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.time.SystemClock;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.Optional;
/**
* Class that coordinates non-HBM animations during keyguard authentication.
@@ -77,7 +77,7 @@
protected UdfpsKeyguardViewController(
@NonNull UdfpsKeyguardView view,
@NonNull StatusBarStateController statusBarStateController,
- @NonNull Optional<StatusBar> statusBarOptional,
+ @NonNull PanelExpansionStateManager panelExpansionStateManager,
@NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
@NonNull DumpManager dumpManager,
@@ -87,7 +87,7 @@
@NonNull KeyguardStateController keyguardStateController,
@NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
@NonNull UdfpsController udfpsController) {
- super(view, statusBarStateController, statusBarOptional, dumpManager);
+ super(view, statusBarStateController, panelExpansionStateManager, dumpManager);
mKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockScreenShadeTransitionController = transitionController;
@@ -126,9 +126,7 @@
mInputBouncerHiddenAmount = KeyguardBouncer.EXPANSION_HIDDEN;
mIsBouncerVisible = mKeyguardViewManager.bouncerIsOrWillBeShowing();
mConfigurationController.addCallback(mConfigurationListener);
- mStatusBarOptional.ifPresent(
- statusBar -> statusBar.addExpansionChangedListener(
- mStatusBarExpansionChangedListener));
+ mPanelExpansionStateManager.addExpansionListener(mPanelExpansionListener);
updateAlpha();
updatePauseAuth();
@@ -147,9 +145,7 @@
mKeyguardViewManager.removeAlternateAuthInterceptor(mAlternateAuthInterceptor);
mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
mConfigurationController.removeCallback(mConfigurationListener);
- mStatusBarOptional.ifPresent(
- statusBar -> statusBar.removeExpansionChangedListener(
- mStatusBarExpansionChangedListener));
+ mPanelExpansionStateManager.removeExpansionListener(mPanelExpansionListener);
if (mLockScreenShadeTransitionController.getUdfpsKeyguardViewController() == this) {
mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(null);
}
@@ -403,14 +399,14 @@
}
};
- private final StatusBar.ExpansionChangedListener mStatusBarExpansionChangedListener =
- new StatusBar.ExpansionChangedListener() {
- @Override
- public void onExpansionChanged(float expansion, boolean expanded) {
- mStatusBarExpansion = expansion;
- updateAlpha();
- }
- };
+ private final PanelExpansionListener mPanelExpansionListener = new PanelExpansionListener() {
+ @Override
+ public void onPanelExpansionChanged(
+ float fraction, boolean expanded, boolean tracking) {
+ mStatusBarExpansion = fraction;
+ updateAlpha();
+ }
+ };
private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
new KeyguardStateController.Callback() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
index 322584c..6989547 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
@@ -31,6 +31,7 @@
import android.os.UserManager;
import android.util.DisplayMetrics;
import android.view.ViewGroup;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -46,6 +47,13 @@
public static final int CREDENTIAL_PATTERN = 2;
public static final int CREDENTIAL_PASSWORD = 3;
+ /** Base set of layout flags for fingerprint overlay widgets. */
+ public static final int FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS =
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+
@Retention(RetentionPolicy.SOURCE)
@IntDef({CREDENTIAL_PIN, CREDENTIAL_PATTERN, CREDENTIAL_PASSWORD})
@interface CredentialType {}
@@ -55,11 +63,6 @@
/ DisplayMetrics.DENSITY_DEFAULT);
}
- static float pixelsToDp(Context context, float pixels) {
- return pixels / ((float) context.getResources().getDisplayMetrics().densityDpi
- / DisplayMetrics.DENSITY_DEFAULT);
- }
-
static void notifyAccessibilityContentChanged(AccessibilityManager am, ViewGroup view) {
if (!am.isEnabled()) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestReceiver.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestReceiver.kt
index bf84d77..7e5b267 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestReceiver.kt
@@ -28,6 +28,7 @@
import android.service.controls.Control
import android.service.controls.ControlsProviderService
import android.util.Log
+import java.lang.ClassCastException
/**
* Proxy to launch in user 0
@@ -59,20 +60,29 @@
return
}
- val packageName = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)
- ?.packageName
+ val targetComponent = try {
+ intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)
+ } catch (e: ClassCastException) {
+ Log.e(TAG, "Malformed intent extra ComponentName", e)
+ return
+ }
+
+ val control = try {
+ intent.getParcelableExtra<Control>(ControlsProviderService.EXTRA_CONTROL)
+ } catch (e: ClassCastException) {
+ Log.e(TAG, "Malformed intent extra Control", e)
+ return
+ }
+
+ val packageName = targetComponent?.packageName
if (packageName == null || !isPackageInForeground(context, packageName)) {
return
}
val activityIntent = Intent(context, ControlsRequestDialog::class.java).apply {
- Intent.EXTRA_COMPONENT_NAME.let {
- putExtra(it, intent.getParcelableExtra<ComponentName>(it))
- }
- ControlsProviderService.EXTRA_CONTROL.let {
- putExtra(it, intent.getParcelableExtra<Control>(it))
- }
+ putExtra(Intent.EXTRA_COMPONENT_NAME, targetComponent)
+ putExtra(ControlsProviderService.EXTRA_CONTROL, control)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
}
activityIntent.putExtra(Intent.EXTRA_USER_ID, context.userId)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
index fe79110..33f07c7 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
@@ -21,6 +21,7 @@
import com.android.systemui.ImageWallpaper;
import com.android.systemui.SystemUIService;
import com.android.systemui.doze.DozeService;
+import com.android.systemui.dreams.DreamOverlayService;
import com.android.systemui.dump.SystemUIAuxiliaryDumpService;
import com.android.systemui.keyguard.KeyguardService;
import com.android.systemui.screenrecord.RecordingService;
@@ -56,6 +57,12 @@
/** */
@Binds
@IntoMap
+ @ClassKey(DreamOverlayService.class)
+ public abstract Service bindDreamOverlayService(DreamOverlayService service);
+
+ /** */
+ @Binds
+ @IntoMap
@ClassKey(SystemUIService.class)
public abstract Service bindSystemUIService(SystemUIService service);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 7972318..b51eb07 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -442,7 +442,11 @@
@Provides
@Singleton
static PermissionManager providePermissionManager(Context context) {
- return context.getSystemService(PermissionManager.class);
+ PermissionManager pm = context.getSystemService(PermissionManager.class);
+ if (pm != null) {
+ pm.initializeUsageHelper();
+ }
+ return pm;
}
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index a9a4da8..a9fb743 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -25,6 +25,8 @@
import com.android.systemui.keyguard.KeyguardSliceProvider;
import com.android.systemui.people.PeopleProvider;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
+import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
import com.android.wm.shell.ShellCommandHandler;
import com.android.wm.shell.TaskViewFactory;
import com.android.wm.shell.apppairs.AppPairs;
@@ -34,6 +36,7 @@
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.recents.RecentTasks;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper;
@@ -101,6 +104,9 @@
@BindsInstance
Builder setTaskSurfaceHelper(Optional<TaskSurfaceHelper> t);
+ @BindsInstance
+ Builder setRecentTasks(Optional<RecentTasks> r);
+
SysUIComponent build();
}
@@ -108,7 +114,14 @@
* Initializes all the SysUI components.
*/
default void init() {
- // Do nothing
+ // Initialize components that have no direct tie to the dagger dependency graph,
+ // but are critical to this component's operation
+ // TODO(b/205034537): I think this is a good idea?
+ getSysUIUnfoldComponent().ifPresent(c -> {
+ c.getUnfoldLightRevealOverlayAnimation().init();
+ c.getUnfoldTransitionWallpaperController().init();
+ });
+ getNaturalRotationUnfoldProgressProvider().ifPresent(o -> o.init());
}
/**
@@ -146,6 +159,16 @@
InitController getInitController();
/**
+ * For devices with a hinge: access objects within this component
+ */
+ Optional<SysUIUnfoldComponent> getSysUIUnfoldComponent();
+
+ /**
+ * For devices with a hinge: the rotation animation
+ */
+ Optional<NaturalRotationUnfoldProgressProvider> getNaturalRotationUnfoldProgressProvider();
+
+ /**
* Member injection into the supplied argument.
*/
void inject(SystemUIAppComponentFactory factory);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 30844cc..a5d4d80 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -23,6 +23,8 @@
import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.accessibility.WindowMagnification;
import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.dreams.DreamOverlayRegistrant;
+import com.android.systemui.dreams.appwidgets.AppWidgetOverlayPrimer;
import com.android.systemui.globalactions.GlobalActionsComponent;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.dagger.KeyguardModule;
@@ -188,4 +190,18 @@
@IntoMap
@ClassKey(HomeSoundEffectController.class)
public abstract SystemUI bindHomeSoundEffectController(HomeSoundEffectController sysui);
+
+ /** Inject into DreamOverlay. */
+ @Binds
+ @IntoMap
+ @ClassKey(DreamOverlayRegistrant.class)
+ public abstract SystemUI bindDreamOverlayRegistrant(
+ DreamOverlayRegistrant dreamOverlayRegistrant);
+
+ /** Inject into AppWidgetOverlayPrimer. */
+ @Binds
+ @IntoMap
+ @ClassKey(AppWidgetOverlayPrimer.class)
+ public abstract SystemUI bindAppWidgetOverlayPrimer(
+ AppWidgetOverlayPrimer appWidgetOverlayPrimer);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 50d2dd1..6c52b5e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -64,6 +64,7 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyControllerImpl;
import com.android.systemui.statusbar.policy.SensorPrivacyController;
@@ -169,12 +170,19 @@
@Provides
static HeadsUpManagerPhone provideHeadsUpManagerPhone(
Context context,
+ HeadsUpManagerLogger headsUpManagerLogger,
StatusBarStateController statusBarStateController,
KeyguardBypassController bypassController,
GroupMembershipManager groupManager,
ConfigurationController configurationController) {
- return new HeadsUpManagerPhone(context, statusBarStateController, bypassController,
- groupManager, configurationController);
+ return new HeadsUpManagerPhone(
+ context,
+ headsUpManagerLogger,
+ statusBarStateController,
+ bypassController,
+ groupManager,
+ configurationController
+ );
}
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index e845e2a..11a0f6c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -18,7 +18,6 @@
import android.app.INotificationManager;
import android.content.Context;
-import android.view.LayoutInflater;
import androidx.annotation.Nullable;
@@ -27,7 +26,6 @@
import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.BootCompleteCache;
import com.android.systemui.BootCompleteCacheImpl;
-import com.android.systemui.R;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.appops.dagger.AppOpsModule;
import com.android.systemui.assist.AssistModule;
@@ -38,11 +36,13 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.demomode.dagger.DemoModeModule;
import com.android.systemui.doze.dagger.DozeComponent;
+import com.android.systemui.dreams.dagger.DreamModule;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlagManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.FlagReader;
import com.android.systemui.flags.FlagWriter;
+import com.android.systemui.flags.FlagsModule;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.model.SysUiState;
@@ -67,14 +67,15 @@
import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarWindowView;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.dagger.SmartRepliesInflationModule;
import com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule;
+import com.android.systemui.statusbar.window.StatusBarWindowModule;
import com.android.systemui.tuner.dagger.TunerModule;
+import com.android.systemui.unfold.SysUIUnfoldModule;
import com.android.systemui.user.UserModule;
import com.android.systemui.util.concurrency.SysUIConcurrencyModule;
import com.android.systemui.util.dagger.UtilModule;
@@ -103,9 +104,11 @@
AssistModule.class,
ClockModule.class,
CommunalModule.class,
+ DreamModule.class,
ControlsModule.class,
DemoModeModule.class,
FalsingModule.class,
+ FlagsModule.class,
LogModule.class,
PeopleHubModule.class,
PluginModule.class,
@@ -115,7 +118,9 @@
SettingsUtilModule.class,
SmartRepliesInflationModule.class,
StatusBarPolicyModule.class,
+ StatusBarWindowModule.class,
SysUIConcurrencyModule.class,
+ SysUIUnfoldModule.class,
TunerModule.class,
UserModule.class,
UtilModule.class,
@@ -214,17 +219,4 @@
groupManager, entryManager, notifPipeline, sysUiState, featureFlags, dumpManager,
sysuiMainExecutor));
}
-
- @Provides
- @SysUISingleton
- static StatusBarWindowView providesStatusBarWindowView(LayoutInflater layoutInflater) {
- StatusBarWindowView view =
- (StatusBarWindowView) layoutInflater.inflate(R.layout.super_status_bar,
- /* root= */ null);
- if (view == null) {
- throw new IllegalStateException(
- "R.layout.super_status_bar could not be properly inflated");
- }
- return view;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index 618c26b..d8b7742 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -20,18 +20,20 @@
import com.android.systemui.SystemUIFactory;
import com.android.systemui.tv.TvWMComponent;
-import com.android.systemui.wmshell.TvWMShellModule;
-import com.android.systemui.wmshell.WMShellModule;
+import com.android.wm.shell.dagger.TvWMShellModule;
+import com.android.wm.shell.dagger.WMShellModule;
import com.android.wm.shell.ShellCommandHandler;
import com.android.wm.shell.ShellInit;
import com.android.wm.shell.TaskViewFactory;
import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.bubbles.Bubbles;
+import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.recents.RecentTasks;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper;
@@ -110,4 +112,7 @@
@WMSingleton
Optional<TaskSurfaceHelper> getTaskSurfaceHelper();
+
+ @WMSingleton
+ Optional<RecentTasks> getRecentTasks();
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 669965b..2511520 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -115,7 +115,7 @@
}
/**
- * Appends dozing event to the logs
+ * Appends dozing event to the logs. Logs current dozing state when entering/exiting AOD.
* @param dozing true if dozing, else false
*/
public void traceDozing(boolean dozing) {
@@ -124,6 +124,14 @@
}
/**
+ * Appends dozing event to the logs when dozing has changed in AOD.
+ * @param dozing true if we're now dozing, else false
+ */
+ public void traceDozingChanged(boolean dozing) {
+ mLogger.logDozingChanged(dozing);
+ }
+
+ /**
* Appends dozing event to the logs
* @param suppressed true if dozing is suppressed
*/
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
index d79bf22..4ba6b51 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -66,6 +66,14 @@
})
}
+ fun logDozingChanged(isDozing: Boolean) {
+ buffer.log(TAG, INFO, {
+ bool1 = isDozing
+ }, {
+ "Dozing changed dozing=$bool1"
+ })
+ }
+
fun logDozingSuppressed(isDozingSuppressed: Boolean) {
buffer.log(TAG, INFO, {
bool1 = isDozingSuppressed
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
new file mode 100644
index 0000000..20c46da
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
@@ -0,0 +1,128 @@
+/*
+ * 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.systemui.dreams;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.PatternMatcher;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
+import android.util.Log;
+
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+import com.android.systemui.dagger.qualifiers.Main;
+
+import javax.inject.Inject;
+
+/**
+ * {@link DreamOverlayRegistrant} is responsible for telling system server that SystemUI should be
+ * the designated dream overlay component.
+ */
+public class DreamOverlayRegistrant extends SystemUI {
+ private static final String TAG = "DreamOverlayRegistrant";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private final IDreamManager mDreamManager;
+ private final ComponentName mOverlayServiceComponent;
+ private final Resources mResources;
+ private boolean mCurrentRegisteredState = false;
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) {
+ Log.d(TAG, "package changed receiver - onReceive");
+ }
+
+ registerOverlayService();
+ }
+ };
+
+ private void registerOverlayService() {
+ // Check to see if the service has been disabled by the user. In this case, we should not
+ // proceed modifying the enabled setting.
+ final PackageManager packageManager = mContext.getPackageManager();
+ final int enabledState =
+ packageManager.getComponentEnabledSetting(mOverlayServiceComponent);
+
+
+ // TODO(b/204626521): We should not have to set the component enabled setting if the
+ // enabled config flag is properly applied based on the RRO.
+ if (enabledState != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ final int overlayState = mResources.getBoolean(R.bool.config_dreamOverlayServiceEnabled)
+ ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+ : PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+
+ if (overlayState != enabledState) {
+ packageManager
+ .setComponentEnabledSetting(mOverlayServiceComponent, overlayState, 0);
+ }
+ }
+
+ // The overlay service is only registered when its component setting is enabled.
+ boolean register = packageManager.getComponentEnabledSetting(mOverlayServiceComponent)
+ == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+
+ if (mCurrentRegisteredState == register) {
+ return;
+ }
+
+ mCurrentRegisteredState = register;
+
+ try {
+ if (DEBUG) {
+ Log.d(TAG, mCurrentRegisteredState
+ ? "registering dream overlay service:" + mOverlayServiceComponent
+ : "clearing dream overlay service");
+ }
+
+ mDreamManager.registerDreamOverlayService(
+ mCurrentRegisteredState ? mOverlayServiceComponent : null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "could not register dream overlay service:" + e);
+ }
+ }
+
+ @Inject
+ public DreamOverlayRegistrant(Context context, @Main Resources resources) {
+ super(context);
+ mResources = resources;
+ mDreamManager = IDreamManager.Stub.asInterface(
+ ServiceManager.getService(DreamService.DREAM_SERVICE));
+ mOverlayServiceComponent = new ComponentName(mContext, DreamOverlayService.class);
+ }
+
+ @Override
+ public void start() {
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addDataScheme("package");
+ filter.addDataSchemeSpecificPart(mOverlayServiceComponent.getPackageName(),
+ PatternMatcher.PATTERN_LITERAL);
+ // Note that we directly register the receiver here as data schemes are not supported by
+ // BroadcastDispatcher.
+ mContext.registerReceiver(mReceiver, filter);
+
+ registerOverlayService();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
new file mode 100644
index 0000000..8f0ea2f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -0,0 +1,195 @@
+/*
+ * 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.systemui.dreams;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.drawable.ColorDrawable;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.Window;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.PhoneWindow;
+import com.android.systemui.dagger.qualifiers.Main;
+
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+/**
+ * The {@link DreamOverlayService} is responsible for placing overlays on top of a dream. The
+ * dream reaches directly out to the service with a Window reference (via LayoutParams), which the
+ * service uses to insert its own child Window into the dream's parent Window.
+ */
+public class DreamOverlayService extends android.service.dreams.DreamOverlayService {
+ private static final String TAG = "DreamOverlayService";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ // The Context is used to construct the hosting constraint layout and child overlay views.
+ private final Context mContext;
+ // The Executor ensures actions and ui updates happen on the same thread.
+ private final Executor mExecutor;
+ // The state controller informs the service of updates to the overlays present.
+ private final DreamOverlayStateController mStateController;
+
+ // The window is populated once the dream informs the service it has begun dreaming.
+ private Window mWindow;
+ private ConstraintLayout mLayout;
+
+ private final DreamOverlayStateController.Callback mOverlayStateCallback =
+ new DreamOverlayStateController.Callback() {
+ @Override
+ public void onOverlayChanged() {
+ mExecutor.execute(() -> reloadOverlaysLocked());
+ }
+ };
+
+ // The service listens to view changes in order to declare that input occurring in areas outside
+ // the overlay should be passed through to the dream underneath.
+ private View.OnAttachStateChangeListener mRootViewAttachListener =
+ new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ v.getViewTreeObserver()
+ .addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ v.getViewTreeObserver()
+ .removeOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
+ }
+ };
+
+ // A hook into the internal inset calculation where we declare the overlays as the only
+ // touchable regions.
+ private ViewTreeObserver.OnComputeInternalInsetsListener mOnComputeInternalInsetsListener =
+ new ViewTreeObserver.OnComputeInternalInsetsListener() {
+ @Override
+ public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
+ if (mLayout != null) {
+ inoutInfo.setTouchableInsets(
+ ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ final Region region = new Region();
+ for (int i = 0; i < mLayout.getChildCount(); i++) {
+ View child = mLayout.getChildAt(i);
+ final Rect rect = new Rect();
+ child.getGlobalVisibleRect(rect);
+ region.op(rect, Region.Op.UNION);
+ }
+
+ inoutInfo.touchableRegion.set(region);
+ }
+ }
+ };
+
+ @Override
+ public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) {
+ mExecutor.execute(() -> addOverlayWindowLocked(layoutParams));
+ }
+
+ private void reloadOverlaysLocked() {
+ if (mLayout == null) {
+ return;
+ }
+ mLayout.removeAllViews();
+ for (OverlayProvider overlayProvider : mStateController.getOverlays()) {
+ addOverlay(overlayProvider);
+ }
+ }
+
+ /**
+ * Inserts {@link Window} to host dream overlays into the dream's parent window. Must be called
+ * from the main executing thread. The window attributes closely mirror those that are set by
+ * the {@link android.service.dreams.DreamService} on the dream Window.
+ * @param layoutParams The {@link android.view.WindowManager.LayoutParams} which allow inserting
+ * into the dream window.
+ */
+ private void addOverlayWindowLocked(WindowManager.LayoutParams layoutParams) {
+ mWindow = new PhoneWindow(mContext);
+ mWindow.setAttributes(layoutParams);
+ mWindow.setWindowManager(null, layoutParams.token, "DreamOverlay", true);
+
+ mWindow.setBackgroundDrawable(new ColorDrawable(0));
+
+ mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+ mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+ mWindow.requestFeature(Window.FEATURE_NO_TITLE);
+ // Hide all insets when the dream is showing
+ mWindow.getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars());
+ mWindow.setDecorFitsSystemWindows(false);
+
+ if (DEBUG) {
+ Log.d(TAG, "adding overlay window to dream");
+ }
+
+ mLayout = new ConstraintLayout(mContext);
+ mLayout.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ mLayout.addOnAttachStateChangeListener(mRootViewAttachListener);
+ mWindow.setContentView(mLayout);
+
+ final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+ windowManager.addView(mWindow.getDecorView(), mWindow.getAttributes());
+ mExecutor.execute(this::reloadOverlaysLocked);
+ }
+
+ @VisibleForTesting
+ protected void addOverlay(OverlayProvider provider) {
+ provider.onCreateOverlay(mContext,
+ (view, layoutParams) -> {
+ // Always move UI related work to the main thread.
+ mExecutor.execute(() -> {
+ if (mLayout == null) {
+ return;
+ }
+
+ mLayout.addView(view, layoutParams);
+ });
+ },
+ () -> {
+ // The Callback is set on the main thread.
+ mExecutor.execute(() -> {
+ requestExit();
+ });
+ });
+ }
+
+ @Inject
+ public DreamOverlayService(Context context, @Main Executor executor,
+ DreamOverlayStateController overlayStateController) {
+ mContext = context;
+ mExecutor = executor;
+ mStateController = overlayStateController;
+ mStateController.addCallback(mOverlayStateCallback);
+ }
+
+ @Override
+ public void onDestroy() {
+ mStateController.removeCallback(mOverlayStateCallback);
+ super.onDestroy();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
new file mode 100644
index 0000000..d248a9e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
@@ -0,0 +1,150 @@
+/*
+ * 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.systemui.dreams;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.policy.CallbackController;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Objects;
+
+import javax.inject.Inject;
+
+/**
+ * {@link DreamOverlayStateController} is the source of truth for Dream overlay configurations.
+ * Clients can register as listeners for changes to the overlay composition and can query for the
+ * overlays on-demand.
+ */
+@SysUISingleton
+public class DreamOverlayStateController implements
+ CallbackController<DreamOverlayStateController.Callback> {
+ // A counter for guaranteeing unique overlay tokens within the scope of this state controller.
+ private int mNextOverlayTokenId = 0;
+
+ /**
+ * {@link OverlayToken} provides a unique key for identifying {@link OverlayProvider}
+ * instances registered with {@link DreamOverlayStateController}.
+ */
+ public static class OverlayToken {
+ private final int mId;
+
+ private OverlayToken(int id) {
+ mId = id;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof OverlayToken)) return false;
+ OverlayToken that = (OverlayToken) o;
+ return mId == that.mId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mId);
+ }
+ }
+
+ /**
+ * Callback for dream overlay events.
+ */
+ public interface Callback {
+ /**
+ * Called when the visibility of the communal view changes.
+ */
+ default void onOverlayChanged() {
+ }
+ }
+
+ private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+ private final HashMap<OverlayToken, OverlayProvider> mOverlays = new HashMap<>();
+
+ @VisibleForTesting
+ @Inject
+ public DreamOverlayStateController() {
+ }
+
+ /**
+ * Adds an overlay to be presented on top of dreams.
+ * @param provider The {@link OverlayProvider} providing the dream.
+ * @return The {@link OverlayToken} tied to the supplied {@link OverlayProvider}.
+ */
+ public OverlayToken addOverlay(OverlayProvider provider) {
+ final OverlayToken token = new OverlayToken(mNextOverlayTokenId++);
+ mOverlays.put(token, provider);
+ notifyCallbacks();
+ return token;
+ }
+
+ /**
+ * Removes an overlay from being shown on dreams.
+ * @param token The {@link OverlayToken} associated with the {@link OverlayProvider} to be
+ * removed.
+ * @return The removed {@link OverlayProvider}, {@code null} if not found.
+ */
+ public OverlayProvider removeOverlay(OverlayToken token) {
+ final OverlayProvider removedOverlay = mOverlays.remove(token);
+
+ if (removedOverlay != null) {
+ notifyCallbacks();
+ }
+
+ return removedOverlay;
+ }
+
+ private void notifyCallbacks() {
+ for (Callback callback : mCallbacks) {
+ callback.onOverlayChanged();
+ }
+ }
+
+ @Override
+ public void addCallback(@NonNull Callback callback) {
+ Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
+ if (mCallbacks.contains(callback)) {
+ return;
+ }
+
+ mCallbacks.add(callback);
+
+ if (mOverlays.isEmpty()) {
+ return;
+ }
+
+ callback.onOverlayChanged();
+ }
+
+ @Override
+ public void removeCallback(@NonNull Callback callback) {
+ Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
+ mCallbacks.remove(callback);
+ }
+
+ /**
+ * Returns all registered {@link OverlayProvider} instances.
+ * @return A collection of {@link OverlayProvider}.
+ */
+ public Collection<OverlayProvider> getOverlays() {
+ return mOverlays.values();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/OverlayHost.java b/packages/SystemUI/src/com/android/systemui/dreams/OverlayHost.java
new file mode 100644
index 0000000..08f0f35
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/OverlayHost.java
@@ -0,0 +1,48 @@
+/*
+ * 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.systemui.dreams;
+
+import android.view.View;
+
+/**
+ * A collection of interfaces related to hosting an overlay.
+ */
+public abstract class OverlayHost {
+ /**
+ * An interface for the callback from the overlay provider to indicate when the overlay is
+ * ready.
+ */
+ public interface CreationCallback {
+ /**
+ * Called to inform the overlay view is ready to be placed within the visual space.
+ * @param view The view representing the overlay.
+ * @param layoutParams The parameters to create the view with.
+ */
+ void onCreated(View view, OverlayHostView.LayoutParams layoutParams);
+ }
+
+ /**
+ * An interface for the callback from the overlay provider to signal interactions in the
+ * overlay.
+ */
+ public interface InteractionCallback {
+ /**
+ * Called to signal the calling overlay would like to exit the dream.
+ */
+ void onExit();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/OverlayHostView.java b/packages/SystemUI/src/com/android/systemui/dreams/OverlayHostView.java
new file mode 100644
index 0000000..7870426
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/OverlayHostView.java
@@ -0,0 +1,43 @@
+/*
+ * 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.systemui.dreams;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+/**
+ * {@link OverlayHostView} is the container view for housing overlays ontop of a dream.
+ */
+public class OverlayHostView extends ConstraintLayout {
+ public OverlayHostView(Context context) {
+ super(context, null);
+ }
+
+ public OverlayHostView(Context context, AttributeSet attrs) {
+ super(context, attrs, 0);
+ }
+
+ public OverlayHostView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr, 0);
+ }
+
+ public OverlayHostView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/OverlayProvider.java b/packages/SystemUI/src/com/android/systemui/dreams/OverlayProvider.java
new file mode 100644
index 0000000..f208025
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/OverlayProvider.java
@@ -0,0 +1,36 @@
+/*
+ * 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.systemui.dreams;
+
+import android.content.Context;
+
+/**
+ * {@link OverlayProvider} is an interface for defining entities that can supply overlays to show
+ * over a dream. Presentation components such as the {@link DreamOverlayService} supply
+ * implementations with the necessary context for constructing such overlays.
+ */
+public interface OverlayProvider {
+ /**
+ * Called when the {@link OverlayHost} requests the associated overlay be produced.
+ *
+ * @param context The {@link Context} used to construct the view.
+ * @param creationCallback The callback to inform when the overlay has been created.
+ * @param interactionCallback The callback to inform when the overlay has been interacted with.
+ */
+ void onCreateOverlay(Context context, OverlayHost.CreationCallback creationCallback,
+ OverlayHost.InteractionCallback interactionCallback);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimer.java b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimer.java
new file mode 100644
index 0000000..a0c7c29
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimer.java
@@ -0,0 +1,119 @@
+/*
+ * 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.systemui.dreams.appwidgets;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.Gravity;
+
+import androidx.constraintlayout.widget.ConstraintSet;
+
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.dreams.OverlayHostView;
+import com.android.systemui.dreams.dagger.AppWidgetOverlayComponent;
+
+import javax.inject.Inject;
+
+/**
+ * {@link AppWidgetOverlayPrimer} reads the configured App Widget Overlay from resources on start
+ * and populates them into the {@link DreamOverlayStateController}.
+ */
+public class AppWidgetOverlayPrimer extends SystemUI {
+ private final Resources mResources;
+ private final DreamOverlayStateController mDreamOverlayStateController;
+ private final AppWidgetOverlayComponent.Factory mComponentFactory;
+
+ @Inject
+ public AppWidgetOverlayPrimer(Context context, @Main Resources resources,
+ DreamOverlayStateController overlayStateController,
+ AppWidgetOverlayComponent.Factory appWidgetOverlayFactory) {
+ super(context);
+ mResources = resources;
+ mDreamOverlayStateController = overlayStateController;
+ mComponentFactory = appWidgetOverlayFactory;
+ }
+
+ @Override
+ public void start() {
+ }
+
+ @Override
+ protected void onBootCompleted() {
+ super.onBootCompleted();
+ loadDefaultWidgets();
+ }
+
+ /**
+ * Generates the {@link OverlayHostView.LayoutParams} for a given gravity. Default dimension
+ * constraints are also included in the params.
+ * @param gravity The gravity for the layout as defined by {@link Gravity}.
+ * @param resources The resourcs from which default dimensions will be extracted from.
+ * @return {@link OverlayHostView.LayoutParams} representing the provided gravity and default
+ * parameters.
+ */
+ private static OverlayHostView.LayoutParams getLayoutParams(int gravity, Resources resources) {
+ final OverlayHostView.LayoutParams params = new OverlayHostView.LayoutParams(
+ OverlayHostView.LayoutParams.MATCH_CONSTRAINT,
+ OverlayHostView.LayoutParams.MATCH_CONSTRAINT);
+
+ if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
+ params.bottomToBottom = ConstraintSet.PARENT_ID;
+ }
+
+ if ((gravity & Gravity.TOP) == Gravity.TOP) {
+ params.topToTop = ConstraintSet.PARENT_ID;
+ }
+
+ if ((gravity & Gravity.END) == Gravity.END) {
+ params.endToEnd = ConstraintSet.PARENT_ID;
+ }
+
+ if ((gravity & Gravity.START) == Gravity.START) {
+ params.startToStart = ConstraintSet.PARENT_ID;
+ }
+
+ // For now, apply the same sizing constraints on every widget.
+ params.matchConstraintPercentHeight =
+ resources.getFloat(R.dimen.config_dreamOverlayComponentHeightPercent);
+ params.matchConstraintPercentWidth =
+ resources.getFloat(R.dimen.config_dreamOverlayComponentWidthPercent);
+
+ return params;
+ }
+
+
+ /**
+ * Helper method for loading widgets based on configuration.
+ */
+ private void loadDefaultWidgets() {
+ final int[] positions = mResources.getIntArray(R.array.config_dreamOverlayPositions);
+ final String[] components =
+ mResources.getStringArray(R.array.config_dreamOverlayComponents);
+
+ for (int i = 0; i < Math.min(positions.length, components.length); i++) {
+ final AppWidgetOverlayComponent component = mComponentFactory.build(
+ ComponentName.unflattenFromString(components[i]),
+ getLayoutParams(positions[i], mResources));
+
+ mDreamOverlayStateController.addOverlay(component.getAppWidgetOverlayProvider());
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayProvider.java b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayProvider.java
new file mode 100644
index 0000000..a635d3f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayProvider.java
@@ -0,0 +1,82 @@
+/*
+ * 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.systemui.dreams.appwidgets;
+
+import android.appwidget.AppWidgetHostView;
+import android.content.ComponentName;
+import android.content.Context;
+import android.util.Log;
+import android.widget.RemoteViews;
+
+import com.android.systemui.dreams.OverlayHost;
+import com.android.systemui.dreams.OverlayHostView;
+import com.android.systemui.dreams.OverlayProvider;
+import com.android.systemui.plugins.ActivityStarter;
+
+import javax.inject.Inject;
+
+/**
+ * {@link AppWidgetOverlayProvider} is an implementation of {@link OverlayProvider} for providing
+ * app widget-based overlays.
+ */
+public class AppWidgetOverlayProvider implements OverlayProvider {
+ private static final String TAG = "AppWdgtOverlayProvider";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final ActivityStarter mActivityStarter;
+ private final AppWidgetProvider mAppWidgetProvider;
+ private final ComponentName mComponentName;
+ private final OverlayHostView.LayoutParams mLayoutParams;
+
+ @Inject
+ public AppWidgetOverlayProvider(ActivityStarter activityStarter,
+ ComponentName componentName, AppWidgetProvider widgetProvider,
+ OverlayHostView.LayoutParams layoutParams) {
+ mActivityStarter = activityStarter;
+ mComponentName = componentName;
+ mAppWidgetProvider = widgetProvider;
+ mLayoutParams = layoutParams;
+ }
+
+ @Override
+ public void onCreateOverlay(Context context, OverlayHost.CreationCallback creationCallback,
+ OverlayHost.InteractionCallback interactionCallback) {
+ final AppWidgetHostView widget = mAppWidgetProvider.getWidget(mComponentName);
+
+ if (widget == null) {
+ Log.e(TAG, "could not create widget");
+ return;
+ }
+
+ widget.setInteractionHandler((view, pendingIntent, response) -> {
+ if (pendingIntent.isActivity()) {
+ if (DEBUG) {
+ Log.d(TAG, "launching pending intent from app widget:" + mComponentName);
+ }
+ interactionCallback.onExit();
+ mActivityStarter.startPendingIntentDismissingKeyguard(pendingIntent,
+ null /*intentSentUiThreadCallback*/, view);
+ return true;
+ } else {
+ return RemoteViews.startPendingIntent(view, pendingIntent,
+ response.getLaunchOptions(view));
+ }
+ });
+
+ creationCallback.onCreated(widget, mLayoutParams);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetProvider.java
new file mode 100644
index 0000000..d1da1e6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetProvider.java
@@ -0,0 +1,101 @@
+/*
+ * 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.systemui.dreams.appwidgets;
+
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+/**
+ * {@link AppWidgetProvider} is a singleton for accessing app widgets within SystemUI. This
+ * consolidates resources such as the App Widget Host across potentially multiple
+ * {@link AppWidgetOverlayProvider} instances and other usages.
+ */
+@SysUISingleton
+public class AppWidgetProvider {
+ private static final String TAG = "AppWidgetProvider";
+ public static final int APP_WIDGET_HOST_ID = 1025;
+
+ private final Context mContext;
+ private final AppWidgetManager mAppWidgetManager;
+ private final AppWidgetHost mAppWidgetHost;
+ private final Resources mResources;
+
+ @Inject
+ public AppWidgetProvider(Context context, @Main Resources resources) {
+ mContext = context;
+ mResources = resources;
+ mAppWidgetManager = android.appwidget.AppWidgetManager.getInstance(context);
+ mAppWidgetHost = new AppWidgetHost(context, APP_WIDGET_HOST_ID);
+ mAppWidgetHost.startListening();
+ }
+
+ /**
+ * Returns an {@link AppWidgetHostView} associated with a given {@link ComponentName}.
+ * @param component The {@link ComponentName} of the target {@link AppWidgetHostView}.
+ * @return The {@link AppWidgetHostView} or {@code null} on error.
+ */
+ public AppWidgetHostView getWidget(ComponentName component) {
+ final List<AppWidgetProviderInfo> appWidgetInfos =
+ mAppWidgetManager.getInstalledProviders();
+
+ for (AppWidgetProviderInfo widgetInfo : appWidgetInfos) {
+ if (widgetInfo.provider.equals(component)) {
+ final int widgetId = mAppWidgetHost.allocateAppWidgetId();
+
+ boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(widgetId,
+ widgetInfo.provider);
+
+ if (!success) {
+ Log.e(TAG, "could not bind to app widget:" + component);
+ break;
+ }
+
+ final AppWidgetHostView appWidgetView =
+ mAppWidgetHost.createView(mContext, widgetId, widgetInfo);
+
+ if (appWidgetView != null) {
+ // Register a layout change listener to update the widget on any sizing changes.
+ appWidgetView.addOnLayoutChangeListener(
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ final float density = mResources.getDisplayMetrics().density;
+ final int height = Math.round((bottom - top) / density);
+ final int width = Math.round((right - left) / density);
+ appWidgetView.updateAppWidgetSize(null, width, height, width,
+ height);
+ });
+ }
+
+ return appWidgetView;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/AppWidgetOverlayComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/AppWidgetOverlayComponent.java
new file mode 100644
index 0000000..3103057
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/AppWidgetOverlayComponent.java
@@ -0,0 +1,39 @@
+/*
+ * 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.systemui.dreams.dagger;
+
+import android.content.ComponentName;
+
+import com.android.systemui.dreams.OverlayHostView;
+import com.android.systemui.dreams.appwidgets.AppWidgetOverlayProvider;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/** */
+@Subcomponent
+public interface AppWidgetOverlayComponent {
+ /** */
+ @Subcomponent.Factory
+ interface Factory {
+ AppWidgetOverlayComponent build(@BindsInstance ComponentName component,
+ @BindsInstance OverlayHostView.LayoutParams layoutParams);
+ }
+
+ /** Builds a {@link AppWidgetOverlayProvider}. */
+ AppWidgetOverlayProvider getAppWidgetOverlayProvider();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
new file mode 100644
index 0000000..7bf2361
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -0,0 +1,28 @@
+/*
+ * 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.systemui.dreams.dagger;
+
+import dagger.Module;
+
+/**
+ * Dagger Module providing Communal-related functionality.
+ */
+@Module(subcomponents = {
+ AppWidgetOverlayComponent.class,
+})
+public interface DreamModule {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
index 3d44304..77e907c 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
@@ -187,6 +187,13 @@
return isEnabled(Flags.NEW_USER_SWITCHER);
}
+ /**
+ * Use the new single view QS headers
+ */
+ public boolean useCombinedQSHeaders() {
+ return isEnabled(Flags.COMBINED_QS_HEADERS);
+ }
+
/** static method for the system setting */
public static boolean isProviderModelSettingEnabled(Context context) {
return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagReader.java b/packages/SystemUI/src/com/android/systemui/flags/FlagReader.java
deleted file mode 100644
index 1ae8c1f..0000000
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagReader.java
+++ /dev/null
@@ -1,40 +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 com.android.systemui.flags;
-
-
-/**
- * Plugin for loading flag values
- */
-public interface FlagReader {
- /** Returns a boolean value for the given flag. */
- default boolean isEnabled(int id, boolean def) {
- return def;
- }
-
- /** Add a listener to be alerted when any flag changes. */
- default void addListener(Listener listener) {}
-
- /** Remove a listener to be alerted when any flag changes. */
- default void removeListener(Listener listener) {}
-
- /** A simple listener to be alerted when a flag changes. */
- interface Listener {
- /** */
- void onFlagChanged(int id);
- }
-}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
similarity index 95%
rename from packages/SystemUI/plugin/src/com/android/systemui/flags/Flags.java
rename to packages/SystemUI/src/com/android/systemui/flags/Flags.java
index fa60bc9..f09c797 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -47,6 +47,7 @@
public static final BooleanFlag NOTIFICATION_UPDATES =
new BooleanFlag(102, true);
+
/***************************************/
// 200 - keyguard/lockscreen
public static final BooleanFlag KEYGUARD_LAYOUT =
@@ -76,21 +77,24 @@
public static final BooleanFlag NEW_USER_SWITCHER =
new BooleanFlag(500, true);
+ public static final BooleanFlag COMBINED_QS_HEADERS =
+ new BooleanFlag(501, false);
+
/***************************************/
// 600- status bar
public static final BooleanFlag COMBINED_STATUS_BAR_SIGNAL_ICONS =
- new BooleanFlag(501, false);
+ new BooleanFlag(601, false);
/***************************************/
// 700 - dialer/calls
public static final BooleanFlag ONGOING_CALL_STATUS_BAR_CHIP =
- new BooleanFlag(600, true);
+ new BooleanFlag(700, true);
public static final BooleanFlag ONGOING_CALL_IN_IMMERSIVE =
- new BooleanFlag(601, true);
+ new BooleanFlag(701, true);
public static final BooleanFlag ONGOING_CALL_IN_IMMERSIVE_CHIP_TAP =
- new BooleanFlag(602, true);
+ new BooleanFlag(702, true);
// Pay no attention to the reflection behind the curtain.
// ========================== Curtain ==========================
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 01a0f27..22a69d4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -118,6 +118,7 @@
private final KeyguardViewMediator mKeyguardViewMediator;
private final KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher;
+ private final ShellTransitions mShellTransitions;
private static int newModeToLegacyMode(int newMode) {
switch (newMode) {
@@ -231,54 +232,10 @@
super();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
+ mShellTransitions = shellTransitions;
if (shellTransitions != null && Transitions.ENABLE_SHELL_TRANSITIONS) {
- if (sEnableRemoteKeyguardGoingAwayAnimation) {
- Slog.d(TAG, "KeyguardService registerRemote: TRANSIT_KEYGUARD_GOING_AWAY");
- TransitionFilter f = new TransitionFilter();
- f.mFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
- shellTransitions.registerRemote(f,
- new RemoteTransition(wrap(mExitAnimationRunner)));
- }
- if (sEnableRemoteKeyguardOccludeAnimation) {
- Slog.d(TAG, "KeyguardService registerRemote: TRANSIT_KEYGUARD_(UN)OCCLUDE");
- // Register for occluding
- TransitionFilter f = new TransitionFilter();
- f.mFlags = TRANSIT_FLAG_KEYGUARD_LOCKED;
- f.mRequirements = new TransitionFilter.Requirement[]{
- new TransitionFilter.Requirement(), new TransitionFilter.Requirement()};
- // First require at-least one app showing that occludes.
- f.mRequirements[0].mMustBeIndependent = false;
- f.mRequirements[0].mFlags = FLAG_OCCLUDES_KEYGUARD;
- f.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
- // Then require that we aren't closing any occludes (because this would mean a
- // regular task->task or activity->activity animation not involving keyguard).
- f.mRequirements[1].mNot = true;
- f.mRequirements[1].mMustBeIndependent = false;
- f.mRequirements[1].mFlags = FLAG_OCCLUDES_KEYGUARD;
- f.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
- shellTransitions.registerRemote(f, new RemoteTransition(mOccludeAnimation));
-
- // Now register for un-occlude.
- f = new TransitionFilter();
- f.mFlags = TRANSIT_FLAG_KEYGUARD_LOCKED;
- f.mRequirements = new TransitionFilter.Requirement[]{
- new TransitionFilter.Requirement(), new TransitionFilter.Requirement()};
- // First require at-least one app going-away (doesn't need occlude flag
- // as that is implicit by it having been visible and we don't want to exclude
- // cases where we are un-occluding because the app removed its showWhenLocked
- // capability at runtime).
- f.mRequirements[1].mMustBeIndependent = false;
- f.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
- f.mRequirements[1].mMustBeTask = true;
- // Then require that we aren't opening any occludes (otherwise we'd remain
- // occluded).
- f.mRequirements[0].mNot = true;
- f.mRequirements[0].mMustBeIndependent = false;
- f.mRequirements[0].mFlags = FLAG_OCCLUDES_KEYGUARD;
- f.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
- shellTransitions.registerRemote(f, new RemoteTransition(mUnoccludeAnimation));
- }
+ // Nothing here. Initialization for this happens in onCreate.
} else {
RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
if (sEnableRemoteKeyguardGoingAwayAnimation) {
@@ -305,6 +262,58 @@
@Override
public void onCreate() {
((SystemUIApplication) getApplication()).startServicesIfNeeded();
+
+ if (mShellTransitions == null || !Transitions.ENABLE_SHELL_TRANSITIONS) {
+ return;
+ }
+ if (sEnableRemoteKeyguardGoingAwayAnimation) {
+ Slog.d(TAG, "KeyguardService registerRemote: TRANSIT_KEYGUARD_GOING_AWAY");
+ TransitionFilter f = new TransitionFilter();
+ f.mFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
+ mShellTransitions.registerRemote(f,
+ new RemoteTransition(wrap(mExitAnimationRunner), getIApplicationThread()));
+ }
+ if (sEnableRemoteKeyguardOccludeAnimation) {
+ Slog.d(TAG, "KeyguardService registerRemote: TRANSIT_KEYGUARD_(UN)OCCLUDE");
+ // Register for occluding
+ TransitionFilter f = new TransitionFilter();
+ f.mFlags = TRANSIT_FLAG_KEYGUARD_LOCKED;
+ f.mRequirements = new TransitionFilter.Requirement[]{
+ new TransitionFilter.Requirement(), new TransitionFilter.Requirement()};
+ // First require at-least one app showing that occludes.
+ f.mRequirements[0].mMustBeIndependent = false;
+ f.mRequirements[0].mFlags = FLAG_OCCLUDES_KEYGUARD;
+ f.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+ // Then require that we aren't closing any occludes (because this would mean a
+ // regular task->task or activity->activity animation not involving keyguard).
+ f.mRequirements[1].mNot = true;
+ f.mRequirements[1].mMustBeIndependent = false;
+ f.mRequirements[1].mFlags = FLAG_OCCLUDES_KEYGUARD;
+ f.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
+ mShellTransitions.registerRemote(f,
+ new RemoteTransition(mOccludeAnimation, getIApplicationThread()));
+
+ // Now register for un-occlude.
+ f = new TransitionFilter();
+ f.mFlags = TRANSIT_FLAG_KEYGUARD_LOCKED;
+ f.mRequirements = new TransitionFilter.Requirement[]{
+ new TransitionFilter.Requirement(), new TransitionFilter.Requirement()};
+ // First require at-least one app going-away (doesn't need occlude flag
+ // as that is implicit by it having been visible and we don't want to exclude
+ // cases where we are un-occluding because the app removed its showWhenLocked
+ // capability at runtime).
+ f.mRequirements[1].mMustBeIndependent = false;
+ f.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
+ f.mRequirements[1].mMustBeTask = true;
+ // Then require that we aren't opening any occludes (otherwise we'd remain
+ // occluded).
+ f.mRequirements[0].mNot = true;
+ f.mRequirements[0].mMustBeIndependent = false;
+ f.mRequirements[0].mFlags = FLAG_OCCLUDES_KEYGUARD;
+ f.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+ mShellTransitions.registerRemote(f,
+ new RemoteTransition(mUnoccludeAnimation, getIApplicationThread()));
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 1547cbc..7bb7cc4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -82,6 +82,8 @@
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
+import androidx.annotation.Nullable;
+
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -117,15 +119,17 @@
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
-import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import com.android.systemui.util.DeviceConfigProxy;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
@@ -811,8 +815,7 @@
private DeviceConfigProxy mDeviceConfig;
private DozeParameters mDozeParameters;
- private final UnfoldTransitionConfig mUnfoldTransitionConfig;
- private final Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealAnimation;
+ private final Optional<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealAnimation;
private final AtomicInteger mPendingDrawnTasks = new AtomicInteger();
private final KeyguardStateController mKeyguardStateController;
@@ -837,8 +840,7 @@
NavigationModeController navigationModeController,
KeyguardDisplayManager keyguardDisplayManager,
DozeParameters dozeParameters,
- UnfoldTransitionConfig unfoldTransitionConfig,
- Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation,
+ Optional<SysUIUnfoldComponent> unfoldComponent,
SysuiStatusBarStateController statusBarStateController,
KeyguardStateController keyguardStateController,
Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationControllerLazy,
@@ -872,8 +874,8 @@
mInGestureNavigationMode = QuickStepContract.isGesturalMode(mode);
}));
mDozeParameters = dozeParameters;
- mUnfoldTransitionConfig = unfoldTransitionConfig;
- mUnfoldLightRevealAnimation = unfoldLightRevealOverlayAnimation;
+ mUnfoldLightRevealAnimation = unfoldComponent.map(
+ c -> c.getUnfoldLightRevealOverlayAnimation());
mStatusBarStateController = statusBarStateController;
statusBarStateController.addCallback(this);
@@ -2563,7 +2565,7 @@
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleNotifyScreenTurningOn");
- if (mUnfoldTransitionConfig.isEnabled()) {
+ if (mUnfoldLightRevealAnimation.isPresent()) {
mPendingDrawnTasks.set(2); // unfold overlay and keyguard drawn
mUnfoldLightRevealAnimation.get()
@@ -2660,10 +2662,16 @@
*/
public KeyguardViewController registerStatusBar(StatusBar statusBar,
NotificationPanelViewController panelView,
+ @Nullable PanelExpansionStateManager panelExpansionStateManager,
BiometricUnlockController biometricUnlockController,
View notificationContainer, KeyguardBypassController bypassController) {
- mKeyguardViewControllerLazy.get().registerStatusBar(statusBar, panelView,
- biometricUnlockController, notificationContainer, bypassController);
+ mKeyguardViewControllerLazy.get().registerStatusBar(
+ statusBar,
+ panelView,
+ panelExpansionStateManager,
+ biometricUnlockController,
+ notificationContainer,
+ bypassController);
return mKeyguardViewControllerLazy.get();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 9b0d69b..cae9fee 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -49,11 +49,11 @@
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
-import com.android.systemui.unfold.config.UnfoldTransitionConfig;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.sensors.AsyncSensorManager;
+import java.util.Optional;
import java.util.concurrent.Executor;
import dagger.Lazy;
@@ -92,8 +92,7 @@
NavigationModeController navigationModeController,
KeyguardDisplayManager keyguardDisplayManager,
DozeParameters dozeParameters,
- UnfoldTransitionConfig unfoldTransitionConfig,
- Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation,
+ Optional<SysUIUnfoldComponent> unfoldComponent,
SysuiStatusBarStateController statusBarStateController,
KeyguardStateController keyguardStateController,
Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationController,
@@ -116,8 +115,7 @@
navigationModeController,
keyguardDisplayManager,
dozeParameters,
- unfoldTransitionConfig,
- unfoldLightRevealOverlayAnimation,
+ unfoldComponent,
statusBarStateController,
keyguardStateController,
keyguardUnlockAnimationController,
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
index c1db8ed..9e00381 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
@@ -61,7 +61,7 @@
*
* @param name The name of this buffer
* @param maxLogs The maximum number of messages to keep in memory at any one time, including the
- * unused pool.
+ * unused pool. Must be >= [poolSize].
* @param poolSize The maximum amount that the size of the buffer is allowed to flex in response to
* sequential calls to [document] that aren't immediately followed by a matching call to [push].
*/
@@ -71,6 +71,13 @@
private val poolSize: Int,
private val logcatEchoTracker: LogcatEchoTracker
) {
+ init {
+ if (maxLogs < poolSize) {
+ throw IllegalArgumentException("maxLogs must be greater than or equal to poolSize, " +
+ "but maxLogs=$maxLogs < $poolSize=poolSize")
+ }
+ }
+
private val buffer: ArrayDeque<LogMessageImpl> = ArrayDeque()
var frozen = false
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 72601e9..df950d7 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -52,6 +52,14 @@
return factory.create("NotifLog", 1000);
}
+ /** Provides a logging buffer for all logs related to the data layer of notifications. */
+ @Provides
+ @SysUISingleton
+ @NotificationHeadsUpLog
+ public static LogBuffer provideNotificationHeadsUpLogBuffer(LogBufferFactory factory) {
+ return factory.create("NotifHeadsUpLog", 1000);
+ }
+
/** Provides a logging buffer for all logs related to managing notification sections. */
@Provides
@SysUISingleton
@@ -112,6 +120,17 @@
}
/**
+ * Provides a logging buffer for logs related to {@link com.android.systemui.qs.QSFragment}'s
+ * disable flag adjustments.
+ */
+ @Provides
+ @SysUISingleton
+ @QSFragmentDisableLog
+ public static LogBuffer provideQSFragmentDisableLogBuffer(LogBufferFactory factory) {
+ return factory.create("QSFragmentDisableFlagsLog", 10);
+ }
+
+ /**
* Provides a logging buffer for logs related to swiping away the status bar while in immersive
* mode. See {@link com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureLogger}.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationHeadsUpLog.java
similarity index 76%
copy from packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java
copy to packages/SystemUI/src/com/android/systemui/log/dagger/NotificationHeadsUpLog.java
index 7292b9e..fcc184a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationHeadsUpLog.java
@@ -14,20 +14,20 @@
* limitations under the License.
*/
-package com.android.systemui.dagger;
+package com.android.systemui.log.dagger;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import com.android.systemui.log.LogBuffer;
+
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
-import javax.inject.Scope;
+import javax.inject.Qualifier;
-/**
- * Scope annotation for singleton items within the WMComponent.
- */
+/** A {@link LogBuffer} for heads up notification-related messages. */
+@Qualifier
@Documented
@Retention(RUNTIME)
-@Scope
-public @interface WMSingleton {
+public @interface NotificationHeadsUpLog {
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java b/packages/SystemUI/src/com/android/systemui/log/dagger/QSFragmentDisableLog.java
similarity index 69%
copy from packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java
copy to packages/SystemUI/src/com/android/systemui/log/dagger/QSFragmentDisableLog.java
index 7292b9e..557a254 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMSingleton.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/QSFragmentDisableLog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,20 +14,23 @@
* limitations under the License.
*/
-package com.android.systemui.dagger;
+package com.android.systemui.log.dagger;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import com.android.systemui.log.LogBuffer;
+
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
-import javax.inject.Scope;
+import javax.inject.Qualifier;
/**
- * Scope annotation for singleton items within the WMComponent.
+ * A {@link LogBuffer} for disable flag adjustments made in
+ * {@link com.android.systemui.qs.QSFragment}.
*/
+@Qualifier
@Documented
@Retention(RUNTIME)
-@Scope
-public @interface WMSingleton {
+public @interface QSFragmentDisableLog {
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index 292b0e2..fbfb919 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -109,11 +109,9 @@
}
@MainThread
- private fun processDevice(key: String, oldKey: String?, device: MediaDevice?) {
- val enabled = device != null
- val data = MediaDeviceData(enabled, device?.iconWithoutBackground, device?.name)
+ private fun processDevice(key: String, oldKey: String?, device: MediaDeviceData?) {
listeners.forEach {
- it.onMediaDeviceChanged(key, oldKey, data)
+ it.onMediaDeviceChanged(key, oldKey, device)
}
}
@@ -135,7 +133,7 @@
get() = controller?.sessionToken
private var started = false
private var playbackType = PLAYBACK_TYPE_UNKNOWN
- private var current: MediaDevice? = null
+ private var current: MediaDeviceData? = null
set(value) {
if (!started || value != field) {
field = value
@@ -201,15 +199,13 @@
@WorkerThread
private fun updateCurrent() {
- val device = localMediaManager.getCurrentConnectedDevice()
- controller?.let {
- val route = mr2manager.getRoutingSessionForMediaController(it)
- // If we get a null route, then don't trust the device. Just set to null to disable the
- // output switcher chip.
- current = if (route != null) device else null
- } ?: run {
- current = device
- }
+ val device = localMediaManager.currentConnectedDevice
+ val route = controller?.let { mr2manager.getRoutingSessionForMediaController(it)}
+
+ // If we have a controller but get a null route, then don't trust the device
+ val enabled = device != null && (controller == null || route != null)
+ val name = route?.name?.toString() ?: device?.name
+ current = MediaDeviceData(enabled, device?.iconWithoutBackground, name)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 6a1eae7..c351d13 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -30,7 +30,6 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.containsType;
-import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
@@ -131,10 +130,10 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
-import com.android.systemui.shared.rotation.RotationButton;
-import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.shared.rotation.RotationButton;
+import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.AutoHideUiElement;
@@ -1606,40 +1605,38 @@
int height = WindowManager.LayoutParams.MATCH_PARENT;
int insetsHeight = -1;
int gravity = Gravity.BOTTOM;
- if (INSETS_LAYOUT_GENERALIZATION) {
- boolean navBarCanMove = true;
- if (mWindowManager != null && mWindowManager.getCurrentWindowMetrics() != null) {
- Rect displaySize = mWindowManager.getCurrentWindowMetrics().getBounds();
- navBarCanMove = displaySize.width() != displaySize.height()
- && mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_navBarCanMove);
- }
- if (!navBarCanMove) {
- height = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_frame_height);
- insetsHeight = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_height);
- } else {
- switch (rotation) {
- case ROTATION_UNDEFINED:
- case Surface.ROTATION_0:
- case Surface.ROTATION_180:
- height = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_frame_height);
- insetsHeight = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_height);
- break;
- case Surface.ROTATION_90:
- gravity = Gravity.RIGHT;
- width = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_width);
- break;
- case Surface.ROTATION_270:
- gravity = Gravity.LEFT;
- width = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_width);
- break;
- }
+ boolean navBarCanMove = true;
+ if (mWindowManager != null && mWindowManager.getCurrentWindowMetrics() != null) {
+ Rect displaySize = mWindowManager.getCurrentWindowMetrics().getBounds();
+ navBarCanMove = displaySize.width() != displaySize.height()
+ && mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_navBarCanMove);
+ }
+ if (!navBarCanMove) {
+ height = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_frame_height);
+ insetsHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_height);
+ } else {
+ switch (rotation) {
+ case ROTATION_UNDEFINED:
+ case Surface.ROTATION_0:
+ case Surface.ROTATION_180:
+ height = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_frame_height);
+ insetsHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_height);
+ break;
+ case Surface.ROTATION_90:
+ gravity = Gravity.RIGHT;
+ width = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_width);
+ break;
+ case Surface.ROTATION_270:
+ gravity = Gravity.LEFT;
+ width = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_width);
+ break;
}
}
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
@@ -1653,13 +1650,11 @@
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
| WindowManager.LayoutParams.FLAG_SLIPPERY,
PixelFormat.TRANSLUCENT);
- if (INSETS_LAYOUT_GENERALIZATION) {
- lp.gravity = gravity;
- if (insetsHeight != -1) {
- lp.providedInternalInsets = Insets.of(0, height - insetsHeight, 0, 0);
- } else {
- lp.providedInternalInsets = Insets.NONE;
- }
+ lp.gravity = gravity;
+ if (insetsHeight != -1) {
+ lp.providedInternalInsets = Insets.of(0, height - insetsHeight, 0, 0);
+ } else {
+ lp.providedInternalInsets = Insets.NONE;
}
lp.token = new Binder();
lp.accessibilityTitle = mContext.getString(R.string.nav_bar);
@@ -1825,4 +1820,4 @@
mInputMethodManager);
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 97bcb00..4959c7d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -222,13 +222,12 @@
*/
public void createNavigationBars(final boolean includeDefaultDisplay,
RegisterStatusBarResult result) {
- if (initializeTaskbarIfNecessary()) {
- return;
- }
-
+ // Don't need to create nav bar on the default display if we initialize TaskBar.
+ final boolean shouldCreateDefaultNavbar = includeDefaultDisplay
+ && !initializeTaskbarIfNecessary();
Display[] displays = mDisplayManager.getDisplays();
for (Display display : displays) {
- if (includeDefaultDisplay || display.getDisplayId() != DEFAULT_DISPLAY) {
+ if (shouldCreateDefaultNavbar || display.getDisplayId() != DEFAULT_DISPLAY) {
createNavigationBar(display, null /* savedState */, result);
}
}
@@ -246,12 +245,15 @@
return;
}
- if (mIsTablet) {
+ final int displayId = display.getDisplayId();
+ final boolean isOnDefaultDisplay = displayId == DEFAULT_DISPLAY;
+
+ // We may show TaskBar on the default display for large screen device. Don't need to create
+ // navigation bar for this case.
+ if (mIsTablet && isOnDefaultDisplay) {
return;
}
- final int displayId = display.getDisplayId();
- final boolean isOnDefaultDisplay = displayId == DEFAULT_DISPLAY;
final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
try {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 1784f73..cdf770f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -178,7 +178,10 @@
@Override
public void setSquishinessFraction(float squishinessFraction) {
- // No-op, paged layouts are not squishy.
+ int nPages = mPages.size();
+ for (int i = 0; i < nPages; i++) {
+ mPages.get(i).setSquishinessFraction(squishinessFraction);
+ }
}
private void updateListening() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index eeca239..dd876b7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -15,6 +15,7 @@
package com.android.systemui.qs;
import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
+import static com.android.systemui.statusbar.DisableFlagsLogger.DisableState;
import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
@@ -101,6 +102,7 @@
private final MediaHost mQsMediaHost;
private final MediaHost mQqsMediaHost;
private final QSFragmentComponent.Factory mQsComponentFactory;
+ private final QSFragmentDisableFlagsLogger mQsFragmentDisableFlagsLogger;
private final QSTileHost mHost;
private boolean mShowCollapsedOnKeyguard;
private boolean mLastKeyguardAndExpanded;
@@ -151,6 +153,7 @@
@Named(QUICK_QS_PANEL) MediaHost qqsMediaHost,
KeyguardBypassController keyguardBypassController,
QSFragmentComponent.Factory qsComponentFactory,
+ QSFragmentDisableFlagsLogger qsFragmentDisableFlagsLogger,
FalsingManager falsingManager, DumpManager dumpManager) {
mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
mCommandQueue = commandQueue;
@@ -158,6 +161,7 @@
mQsMediaHost = qsMediaHost;
mQqsMediaHost = qqsMediaHost;
mQsComponentFactory = qsComponentFactory;
+ mQsFragmentDisableFlagsLogger = qsFragmentDisableFlagsLogger;
commandQueue.observe(getLifecycle(), this);
mHost = qsTileHost;
mFalsingManager = falsingManager;
@@ -363,8 +367,14 @@
if (displayId != getContext().getDisplayId()) {
return;
}
+ int state2BeforeAdjustment = state2;
state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
+ mQsFragmentDisableFlagsLogger.logDisableFlagChange(
+ /* new= */ new DisableState(state1, state2BeforeAdjustment),
+ /* newAfterLocalModification= */ new DisableState(state1, state2)
+ );
+
final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
if (disabled == mQsDisabled) return;
mQsDisabled = disabled;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt
new file mode 100644
index 0000000..17a815e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt
@@ -0,0 +1,48 @@
+package com.android.systemui.qs
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.QSFragmentDisableLog
+import com.android.systemui.statusbar.DisableFlagsLogger
+import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment
+import javax.inject.Inject
+
+/** A helper class for logging disable flag changes made in [QSFragment]. */
+class QSFragmentDisableFlagsLogger @Inject constructor(
+ @QSFragmentDisableLog private val buffer: LogBuffer,
+ private val disableFlagsLogger: DisableFlagsLogger
+) {
+
+ /**
+ * Logs a string representing the new state received by [QSFragment] and any modifications that
+ * were made to the flags locally.
+ *
+ * @param new see [DisableFlagsLogger.getDisableFlagsString]
+ * @param newAfterLocalModification see [DisableFlagsLogger.getDisableFlagsString]
+ */
+ fun logDisableFlagChange(
+ new: DisableFlagsLogger.DisableState,
+ newAfterLocalModification: DisableFlagsLogger.DisableState
+ ) {
+ buffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ int1 = new.disable1
+ int2 = new.disable2
+ long1 = newAfterLocalModification.disable1.toLong()
+ long2 = newAfterLocalModification.disable2.toLong()
+ },
+ {
+ disableFlagsLogger.getDisableFlagsString(
+ old = null,
+ new = DisableFlagsLogger.DisableState(int1, int2),
+ newAfterLocalModification =
+ DisableFlagsLogger.DisableState(long1.toInt(), long2.toInt())
+ )
+ }
+ )
+ }
+}
+
+private const val TAG = "QSFragmentDisableFlagsLog"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt b/packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt
index 6de8370..c1c146d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt
@@ -3,15 +3,14 @@
import android.view.ViewGroup
import com.android.systemui.qs.dagger.QSFragmentModule.QQS_FOOTER
import com.android.systemui.qs.dagger.QSScope
-import com.android.systemui.qs.tileimpl.HeightOverrideable
import javax.inject.Inject
import javax.inject.Named
@QSScope
class QSSquishinessController @Inject constructor(
- private val qsTileHost: QSTileHost,
@Named(QQS_FOOTER) private val qqsFooterActionsView: FooterActionsView,
private val qsAnimator: QSAnimator,
+ private val qsPanelController: QSPanelController,
private val quickQSPanelController: QuickQSPanelController
) {
@@ -34,15 +33,7 @@
* Change the height of all tiles and repositions their siblings.
*/
private fun updateSquishiness() {
- // Start by updating the height of all tiles
- for (tile in qsTileHost.tiles) {
- val tileView = quickQSPanelController.getTileView(tile)
- (tileView as? HeightOverrideable)?.let {
- it.squishinessFraction = squishiness
- }
- }
-
- // Update tile positions in the layout
+ (qsPanelController.tileLayout as QSPanel.QSTileLayout).setSquishinessFraction(squishiness)
val tileLayout = quickQSPanelController.tileLayout as TileLayout
tileLayout.setSquishinessFraction(squishiness)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 071e053..7770d8e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -39,11 +39,12 @@
import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.qs.QSDetail.Callback;
+import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
-import com.android.systemui.statusbar.phone.StatusBarWindowView;
import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.policy.VariableDateView;
+import com.android.systemui.statusbar.window.StatusBarWindowView;
import java.util.List;
@@ -86,6 +87,7 @@
private TintedIconManager mTintedIconManager;
private QSExpansionPathInterpolator mQSExpansionPathInterpolator;
+ private StatusBarContentInsetsProvider mInsetsProvider;
private int mRoundedCornerPadding = 0;
private int mWaterfallTopInset;
@@ -102,6 +104,8 @@
private boolean mHasCenterCutout;
private boolean mConfigShowBatteryEstimate;
+ private boolean mUseCombinedQSHeader;
+
public QuickStatusBarHeader(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -158,9 +162,13 @@
void onAttach(TintedIconManager iconManager,
QSExpansionPathInterpolator qsExpansionPathInterpolator,
- List<String> rssiIgnoredSlots) {
+ List<String> rssiIgnoredSlots,
+ StatusBarContentInsetsProvider insetsProvider,
+ boolean useCombinedQSHeader) {
+ mUseCombinedQSHeader = useCombinedQSHeader;
mTintedIconManager = iconManager;
mRssiIgnoredSlots = rssiIgnoredSlots;
+ mInsetsProvider = insetsProvider;
int fillColor = Utils.getColorAttrDefaultColor(getContext(),
android.R.attr.textColorPrimary);
@@ -233,8 +241,11 @@
// status bar is already displayed out of QS in split shade
boolean shouldUseSplitShade =
resources.getBoolean(R.bool.config_use_split_notification_shade);
- mStatusIconsView.setVisibility(shouldUseSplitShade ? View.GONE : View.VISIBLE);
- mDatePrivacyView.setVisibility(shouldUseSplitShade ? View.GONE : View.VISIBLE);
+
+ mStatusIconsView.setVisibility(
+ shouldUseSplitShade || mUseCombinedQSHeader ? View.GONE : View.VISIBLE);
+ mDatePrivacyView.setVisibility(
+ shouldUseSplitShade || mUseCombinedQSHeader ? View.GONE : View.VISIBLE);
mConfigShowBatteryEstimate = resources.getBoolean(R.bool.config_showBatteryEstimateQSBH);
@@ -273,8 +284,8 @@
}
MarginLayoutParams qqsLP = (MarginLayoutParams) mHeaderQsPanel.getLayoutParams();
- qqsLP.topMargin = mContext.getResources()
- .getDimensionPixelSize(R.dimen.qqs_layout_margin_top);
+ qqsLP.topMargin = shouldUseSplitShade || !mUseCombinedQSHeader ? mContext.getResources()
+ .getDimensionPixelSize(R.dimen.qqs_layout_margin_top) : qsOffsetHeight;
mHeaderQsPanel.setLayoutParams(qqsLP);
updateBatteryMode();
@@ -302,6 +313,10 @@
}
private void updateAnimators() {
+ if (mUseCombinedQSHeader) {
+ mTranslationAnimator = null;
+ return;
+ }
updateAlphaAnimator();
int offset = mTopViewMeasureHeight;
@@ -314,6 +329,10 @@
}
private void updateAlphaAnimator() {
+ if (mUseCombinedQSHeader) {
+ mAlphaAnimator = null;
+ return;
+ }
TouchAnimator.Builder builder = new TouchAnimator.Builder()
.addFloat(mSecurityHeaderView, "alpha", 0, 1)
// These views appear on expanding down
@@ -421,22 +440,20 @@
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
// Handle padding of the views
DisplayCutout cutout = insets.getDisplayCutout();
- Pair<Integer, Integer> cornerCutoutPadding = StatusBarWindowView.cornerCutoutMargins(
- cutout, getDisplay());
- Pair<Integer, Integer> padding =
- StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
- cutout, cornerCutoutPadding, -1);
- mDatePrivacyView.setPadding(padding.first, 0, padding.second, 0);
- mStatusIconsView.setPadding(padding.first, 0, padding.second, 0);
+
+ Pair<Integer, Integer> sbInsets = mInsetsProvider
+ .getStatusBarContentInsetsForCurrentRotation();
+ boolean hasCornerCutout = mInsetsProvider.currentRotationHasCornerCutout();
+
+ mDatePrivacyView.setPadding(sbInsets.first, 0, sbInsets.second, 0);
+ mStatusIconsView.setPadding(sbInsets.first, 0, sbInsets.second, 0);
LinearLayout.LayoutParams datePrivacySeparatorLayoutParams =
(LinearLayout.LayoutParams) mDatePrivacySeparator.getLayoutParams();
LinearLayout.LayoutParams mClockIconsSeparatorLayoutParams =
(LinearLayout.LayoutParams) mClockIconsSeparator.getLayoutParams();
- boolean cornerCutout = cornerCutoutPadding != null
- && (cornerCutoutPadding.first == 0 || cornerCutoutPadding.second == 0);
if (cutout != null) {
Rect topCutout = cutout.getBoundingRectTop();
- if (topCutout.isEmpty() || cornerCutout) {
+ if (topCutout.isEmpty() || hasCornerCutout) {
datePrivacySeparatorLayoutParams.width = 0;
mDatePrivacySeparator.setVisibility(View.GONE);
mClockIconsSeparatorLayoutParams.width = 0;
@@ -454,8 +471,8 @@
}
mDatePrivacySeparator.setLayoutParams(datePrivacySeparatorLayoutParams);
mClockIconsSeparator.setLayoutParams(mClockIconsSeparatorLayoutParams);
- mCutOutPaddingLeft = padding.first;
- mCutOutPaddingRight = padding.second;
+ mCutOutPaddingLeft = sbInsets.first;
+ mCutOutPaddingRight = sbInsets.second;
mWaterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
updateBatteryMode();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index 9835d17..6a57e45 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -39,6 +39,7 @@
import com.android.systemui.privacy.logging.PrivacyLogger;
import com.android.systemui.qs.carrier.QSCarrierGroupController;
import com.android.systemui.qs.dagger.QSScope;
+import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.policy.Clock;
@@ -73,6 +74,7 @@
private final QSExpansionPathInterpolator mQSExpansionPathInterpolator;
private final FeatureFlags mFeatureFlags;
private final BatteryMeterViewController mBatteryMeterViewController;
+ private final StatusBarContentInsetsProvider mInsetsProvider;
private final VariableDateViewController mVariableDateViewControllerDateView;
private final VariableDateViewController mVariableDateViewControllerClockDateView;
@@ -142,7 +144,8 @@
QSExpansionPathInterpolator qsExpansionPathInterpolator,
FeatureFlags featureFlags,
VariableDateViewController.Factory variableDateViewControllerFactory,
- BatteryMeterViewController batteryMeterViewController) {
+ BatteryMeterViewController batteryMeterViewController,
+ StatusBarContentInsetsProvider statusBarContentInsetsProvider) {
super(view);
mPrivacyItemController = privacyItemController;
mActivityStarter = activityStarter;
@@ -155,6 +158,7 @@
mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
mFeatureFlags = featureFlags;
mBatteryMeterViewController = batteryMeterViewController;
+ mInsetsProvider = statusBarContentInsetsProvider;
mQSCarrierGroupController = qsCarrierGroupControllerBuilder
.setQSCarrierGroup(mView.findViewById(R.id.carrier_group))
@@ -225,7 +229,8 @@
);
}
- mView.onAttach(mIconManager, mQSExpansionPathInterpolator, rssiIgnoredSlots);
+ mView.onAttach(mIconManager, mQSExpansionPathInterpolator, rssiIgnoredSlots,
+ mInsetsProvider, mFeatureFlags.useCombinedQSHeaders());
mDemoModeController.addCallback(mDemoModeReceiver);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index ee5d5ff..7f08e5b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -13,6 +13,7 @@
import com.android.systemui.R;
import com.android.systemui.qs.QSPanel.QSTileLayout;
import com.android.systemui.qs.QSPanelControllerBase.TileRecord;
+import com.android.systemui.qs.tileimpl.HeightOverrideable;
import java.util.ArrayList;
@@ -212,7 +213,7 @@
return mMaxCellHeight;
}
- private void layoutTileRecords(int numRecords) {
+ private void layoutTileRecords(int numRecords, boolean forLayout) {
final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
int row = 0;
int column = 0;
@@ -232,14 +233,18 @@
final int left = getColumnStart(isRtl ? mColumns - column - 1 : column);
final int right = left + mCellWidth;
final int bottom = top + record.tileView.getMeasuredHeight();
- record.tileView.layout(left, top, right, bottom);
+ if (forLayout) {
+ record.tileView.layout(left, top, right, bottom);
+ } else {
+ record.tileView.setLeftTopRightBottom(left, top, right, bottom);
+ }
mLastTileBottom = bottom;
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
- layoutTileRecords(mRecords.size());
+ layoutTileRecords(mRecords.size(), true /* forLayout */);
}
protected int getRowTop(int row) {
@@ -280,6 +285,12 @@
return;
}
mSquishinessFraction = squishinessFraction;
- layoutTileRecords(mRecords.size());
+ layoutTileRecords(mRecords.size(), false /* forLayout */);
+
+ for (TileRecord record : mRecords) {
+ if (record.tileView instanceof HeightOverrideable) {
+ ((HeightOverrideable) record.tileView).setSquishinessFraction(mSquishinessFraction);
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index b496e6b..403eb7e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -242,12 +242,12 @@
}
private fun updateHeight() {
- val actualHeight = (if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
+ val actualHeight = if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
heightOverride
} else {
measuredHeight
- } * squishinessFraction).toInt()
- bottom = top + actualHeight
+ }
+ bottom = top + (actualHeight * squishinessFraction).toInt()
scrollY = (actualHeight - height) / 2
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 1dab263..563c4cd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -19,6 +19,7 @@
import android.app.AlertDialog;
import android.content.Context;
+import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.net.Network;
import android.net.NetworkCapabilities;
@@ -122,11 +123,14 @@
private Switch mWiFiToggle;
private FrameLayout mDoneLayout;
private Drawable mBackgroundOn;
+ private Drawable mBackgroundOff = null;
private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private boolean mCanConfigMobileData;
// Wi-Fi entries
+ @VisibleForTesting
protected WifiEntry mConnectedWifiEntry;
+ @VisibleForTesting
protected int mWifiEntriesCount;
// Wi-Fi scanning progress bar
@@ -209,6 +213,14 @@
mInternetDialogTitle.setText(getDialogTitleText());
mInternetDialogTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
+ TypedArray typedArray = mContext.obtainStyledAttributes(
+ new int[]{android.R.attr.selectableItemBackground});
+ try {
+ mBackgroundOff = typedArray.getDrawable(0 /* index */);
+ } finally {
+ typedArray.recycle();
+ }
+
setOnClickListener();
mTurnWifiOnLayout.setBackground(null);
mWifiRecyclerView.setLayoutManager(new LinearLayoutManager(mContext));
@@ -324,6 +336,9 @@
mSeeAllLayout.setOnClickListener(v -> onClickSeeMoreButton());
mWiFiToggle.setOnCheckedChangeListener(
(buttonView, isChecked) -> {
+ if (isChecked) {
+ mWifiScanNotifyLayout.setVisibility(View.GONE);
+ }
buttonView.setChecked(isChecked);
mWifiManager.setWifiEnabled(isChecked);
});
@@ -364,7 +379,8 @@
mMobileSummaryText.setTextAppearance(isCarrierNetworkConnected
? R.style.TextAppearance_InternetDialog_Secondary_Active
: R.style.TextAppearance_InternetDialog_Secondary);
- mMobileNetworkLayout.setBackground(isCarrierNetworkConnected ? mBackgroundOn : null);
+ mMobileNetworkLayout.setBackground(
+ isCarrierNetworkConnected ? mBackgroundOn : mBackgroundOff);
mMobileDataToggle.setVisibility(mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
}
@@ -565,12 +581,12 @@
@WorkerThread
public void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries,
@Nullable WifiEntry connectedEntry) {
- mConnectedWifiEntry = connectedEntry;
- mWifiEntriesCount = wifiEntries == null ? 0 : wifiEntries.size();
- mAdapter.setWifiEntries(wifiEntries, mWifiEntriesCount);
mHandler.post(() -> {
- mAdapter.notifyDataSetChanged();
+ mConnectedWifiEntry = connectedEntry;
+ mWifiEntriesCount = wifiEntries == null ? 0 : wifiEntries.size();
updateDialog(false /* shouldUpdateMobileNetwork */);
+ mAdapter.setWifiEntries(wifiEntries, mWifiEntriesCount);
+ mAdapter.notifyDataSetChanged();
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 721a6af..fa874b1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -17,6 +17,7 @@
package com.android.systemui.recents;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
@@ -24,6 +25,7 @@
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
@@ -68,6 +70,7 @@
import android.view.MotionEvent;
import android.view.Surface;
import android.view.accessibility.AccessibilityManager;
+import android.view.inputmethod.InputMethodManager;
import androidx.annotation.NonNull;
@@ -103,6 +106,7 @@
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
+import com.android.wm.shell.recents.RecentTasks;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.transition.ShellTransitions;
@@ -156,6 +160,7 @@
private final ShellTransitions mShellTransitions;
private final Optional<StartingSurface> mStartingSurface;
private final SmartspaceTransitionController mSmartspaceTransitionController;
+ private final Optional<RecentTasks> mRecentTasks;
private Region mActiveNavBarRegion;
@@ -237,6 +242,15 @@
}
@Override
+ public void onImeSwitcherPressed() throws RemoteException {
+ // TODO(b/204901476) We're intentionally using DEFAULT_DISPLAY for now since
+ // Launcher/Taskbar isn't display aware.
+ mContext.getSystemService(InputMethodManager.class)
+ .showInputMethodPickerFromSystem(true /* showAuxiliarySubtypes */,
+ DEFAULT_DISPLAY);
+ }
+
+ @Override
public void setHomeRotationEnabled(boolean enabled) {
verifyCallerAndClearCallingIdentityPostMain("setHomeRotationEnabled", () ->
mHandler.post(() -> notifyHomeRotationEnabled(enabled)));
@@ -480,6 +494,9 @@
params.putBinder(
KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER,
mSmartspaceTransitionController.createExternalInterface().asBinder());
+ mRecentTasks.ifPresent(recentTasks -> params.putBinder(
+ KEY_EXTRA_RECENT_TASKS,
+ recentTasks.createExternalInterface().asBinder()));
try {
mOverviewProxy.onInitialize(params);
@@ -530,17 +547,18 @@
@Inject
public OverviewProxyService(Context context, CommandQueue commandQueue,
Lazy<NavigationBarController> navBarControllerLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
NavigationModeController navModeController,
NotificationShadeWindowController statusBarWinController, SysUiState sysUiState,
Optional<Pip> pipOptional,
Optional<LegacySplitScreen> legacySplitScreenOptional,
Optional<SplitScreen> splitScreenOptional,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
Optional<OneHanded> oneHandedOptional,
+ Optional<RecentTasks> recentTasks,
+ Optional<StartingSurface> startingSurface,
BroadcastDispatcher broadcastDispatcher,
ShellTransitions shellTransitions,
ScreenLifecycle screenLifecycle,
- Optional<StartingSurface> startingSurface,
SmartspaceTransitionController smartspaceTransitionController,
DumpManager dumpManager) {
super(broadcastDispatcher);
@@ -562,6 +580,7 @@
mSysUiState.addCallback(this::notifySystemUiStateFlags);
mOneHandedOptional = oneHandedOptional;
mShellTransitions = shellTransitions;
+ mRecentTasks = recentTasks;
// Assumes device always starts with back button until launcher tells it that it does not
mNavBarButtonAlpha = 1.0f;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index a0118ab..6d78b9f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -266,6 +266,7 @@
private Animator mScreenshotAnimation;
private RequestCallback mCurrentRequestCallback;
+ private String mPackageName = "";
private final Handler mScreenshotHandler = new Handler(Looper.getMainLooper()) {
@Override
@@ -275,7 +276,8 @@
if (DEBUG_UI) {
Log.d(TAG, "Corner timeout hit");
}
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT);
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT, 0,
+ mPackageName);
ScreenshotController.this.dismissScreenshot(false);
break;
default:
@@ -354,12 +356,13 @@
mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
}
- void takeScreenshotFullscreen(Consumer<Uri> finisher, RequestCallback requestCallback) {
+ void takeScreenshotFullscreen(ComponentName topComponent, Consumer<Uri> finisher,
+ RequestCallback requestCallback) {
mCurrentRequestCallback = requestCallback;
DisplayMetrics displayMetrics = new DisplayMetrics();
getDefaultDisplay().getRealMetrics(displayMetrics);
takeScreenshotInternal(
- finisher,
+ topComponent, finisher,
new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels));
}
@@ -383,13 +386,15 @@
screenshotScreenBounds.set(0, 0, screenshot.getWidth(), screenshot.getHeight());
}
mCurrentRequestCallback = requestCallback;
- saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, showFlash);
+ saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, topComponent,
+ showFlash);
}
/**
* Displays a screenshot selector
*/
- void takeScreenshotPartial(final Consumer<Uri> finisher, RequestCallback requestCallback) {
+ void takeScreenshotPartial(ComponentName topComponent,
+ final Consumer<Uri> finisher, RequestCallback requestCallback) {
mScreenshotView.reset();
mCurrentRequestCallback = requestCallback;
@@ -398,7 +403,7 @@
mScreenshotView.requestApplyInsets();
mScreenshotView.takePartialScreenshot(
- rect -> takeScreenshotInternal(finisher, rect));
+ rect -> takeScreenshotInternal(topComponent, finisher, rect));
}
/**
@@ -489,7 +494,8 @@
/**
* Takes a screenshot of the current display and shows an animation.
*/
- private void takeScreenshotInternal(Consumer<Uri> finisher, Rect crop) {
+ private void takeScreenshotInternal(ComponentName topComponent, Consumer<Uri> finisher,
+ Rect crop) {
mScreenshotTakenInPortrait =
mContext.getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;
@@ -507,7 +513,7 @@
return;
}
- saveScreenshot(screenshot, finisher, screenRect, Insets.NONE, true);
+ saveScreenshot(screenshot, finisher, screenRect, Insets.NONE, topComponent, true);
}
private Bitmap captureScreenshot(Rect crop) {
@@ -537,7 +543,7 @@
}
private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
- Insets screenInsets, boolean showFlash) {
+ Insets screenInsets, ComponentName topComponent, boolean showFlash) {
if (mAccessibilityManager.isEnabled()) {
AccessibilityEvent event =
new AccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
@@ -550,7 +556,7 @@
if (mScreenshotView.isAttachedToWindow()) {
// if we didn't already dismiss for another reason
if (!mScreenshotView.isDismissing()) {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED);
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED, 0, mPackageName);
}
if (DEBUG_WINDOW) {
Log.d(TAG, "saveScreenshot: screenshotView is already attached, resetting. "
@@ -558,6 +564,8 @@
}
mScreenshotView.reset();
}
+ mPackageName = topComponent == null ? "" : topComponent.getPackageName();
+ mScreenshotView.setPackageName(mPackageName);
mScreenshotView.updateOrientation(
mWindowManager.getCurrentWindowMetrics().getWindowInsets());
@@ -776,6 +784,10 @@
}
mWindowManager.removeViewImmediate(decorView);
}
+ // Ensure that we remove the input monitor
+ if (mScreenshotView != null) {
+ mScreenshotView.stopInputListening();
+ }
}
/**
@@ -794,11 +806,11 @@
}
finisher.accept(imageData.uri);
if (imageData.uri == null) {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED, 0, mPackageName);
mNotificationsController.notifyScreenshotError(
R.string.screenshot_failed_to_save_text);
} else {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED, 0, mPackageName);
mScreenshotHandler.post(() -> Toast.makeText(mContext,
R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show());
}
@@ -977,11 +989,11 @@
*/
private void logSuccessOnActionsReady(ScreenshotController.SavedImageData imageData) {
if (imageData.uri == null) {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED, 0, mPackageName);
mNotificationsController.notifyScreenshotError(
R.string.screenshot_failed_to_save_text);
} else {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED, 0, mPackageName);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 3314c75..a7f8bca 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -163,6 +163,7 @@
private SwipeDismissHandler mSwipeDismissHandler;
private InputMonitorCompat mInputMonitor;
private boolean mShowScrollablePreview;
+ private String mPackageName = "";
private final ArrayList<ScreenshotActionChip> mSmartChips = new ArrayList<>();
private PendingInteraction mPendingInteraction;
@@ -314,7 +315,7 @@
});
}
- private void stopInputListening() {
+ void stopInputListening() {
if (mInputMonitor != null) {
mInputMonitor.dispose();
mInputMonitor = null;
@@ -410,6 +411,10 @@
mScreenshotPreview.setImageDrawable(createScreenDrawable(mResources, bitmap, screenInsets));
}
+ void setPackageName(String packageName) {
+ mPackageName = packageName;
+ }
+
void updateInsets(WindowInsets insets) {
int orientation = mContext.getResources().getConfiguration().orientation;
mOrientationPortrait = (orientation == ORIENTATION_PORTRAIT);
@@ -586,7 +591,8 @@
if (DEBUG_INPUT) {
Log.d(TAG, "dismiss button clicked");
}
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL);
+ mUiEventLogger.log(
+ ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL, 0, mPackageName);
animateDismissal();
});
mDismissButton.setAlpha(1);
@@ -622,7 +628,7 @@
ArrayList<ScreenshotActionChip> chips = new ArrayList<>();
- mShareChip.setContentDescription(mContext.getString(com.android.internal.R.string.share));
+ mShareChip.setContentDescription(mContext.getString(R.string.screenshot_share_description));
mShareChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_share), true);
mShareChip.setOnClickListener(v -> {
mShareChip.setIsPending(true);
@@ -634,7 +640,7 @@
});
chips.add(mShareChip);
- mEditChip.setContentDescription(mContext.getString(R.string.screenshot_edit_label));
+ mEditChip.setContentDescription(mContext.getString(R.string.screenshot_edit_description));
mEditChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_edit), true);
mEditChip.setOnClickListener(v -> {
mEditChip.setIsPending(true);
@@ -699,24 +705,25 @@
void setChipIntents(ScreenshotController.SavedImageData imageData) {
mShareChip.setOnClickListener(v -> {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED);
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED, 0, mPackageName);
startSharedTransition(
imageData.shareTransition.get());
});
mEditChip.setOnClickListener(v -> {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED);
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED, 0, mPackageName);
startSharedTransition(
imageData.editTransition.get());
});
mScreenshotPreview.setOnClickListener(v -> {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED);
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED, 0, mPackageName);
startSharedTransition(
imageData.editTransition.get());
});
if (mQuickShareChip != null) {
mQuickShareChip.setPendingIntent(imageData.quickShareAction.actionIntent,
() -> {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED);
+ mUiEventLogger.log(
+ ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED, 0, mPackageName);
animateDismissal();
});
}
@@ -746,7 +753,8 @@
actionChip.setIcon(smartAction.getIcon(), false);
actionChip.setPendingIntent(smartAction.actionIntent,
() -> {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED);
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED,
+ 0, mPackageName);
animateDismissal();
});
actionChip.setAlpha(1);
@@ -1124,7 +1132,7 @@
if (DEBUG_DISMISS) {
Log.d(TAG, "dismiss triggered via swipe gesture");
}
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED);
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED, 0, mPackageName);
animateDismissal(createSwipeDismissAnimation());
} else {
// if we've moved, but not past the threshold, start the return animation
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index daa9d09..f380911 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -186,20 +186,22 @@
ScreenshotHelper.ScreenshotRequest screenshotRequest =
(ScreenshotHelper.ScreenshotRequest) msg.obj;
- mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource()));
+ ComponentName topComponent = screenshotRequest.getTopComponent();
+ mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource()), 0,
+ topComponent == null ? "" : topComponent.getPackageName());
switch (msg.what) {
case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
if (DEBUG_SERVICE) {
Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_FULLSCREEN");
}
- mScreenshot.takeScreenshotFullscreen(uriConsumer, requestCallback);
+ mScreenshot.takeScreenshotFullscreen(topComponent, uriConsumer, requestCallback);
break;
case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
if (DEBUG_SERVICE) {
Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_SELECTED_REGION");
}
- mScreenshot.takeScreenshotPartial(uriConsumer, requestCallback);
+ mScreenshot.takeScreenshotPartial(topComponent, uriConsumer, requestCallback);
break;
case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE:
if (DEBUG_SERVICE) {
@@ -211,7 +213,6 @@
Insets insets = screenshotRequest.getInsets();
int taskId = screenshotRequest.getTaskId();
int userId = screenshotRequest.getUserId();
- ComponentName topComponent = screenshotRequest.getTopComponent();
if (screenshot == null) {
Log.e(TAG, "Got null bitmap from screenshot message");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index 9fa4609..43b3fb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -23,12 +23,12 @@
import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
import java.util.stream.Stream;
@@ -41,6 +41,11 @@
private static final String TAG = "AlertNotifManager";
protected final Clock mClock = new Clock();
protected final ArrayMap<String, AlertEntry> mAlertEntries = new ArrayMap<>();
+ protected final HeadsUpManagerLogger mLogger;
+
+ public AlertingNotificationManager(HeadsUpManagerLogger logger) {
+ mLogger = logger;
+ }
/**
* This is the list of entries that have already been removed from the
@@ -61,9 +66,7 @@
* @param entry entry to show
*/
public void showNotification(@NonNull NotificationEntry entry) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "showNotification");
- }
+ mLogger.logShowNotification(entry.getKey());
addAlertEntry(entry);
updateNotification(entry.getKey(), true /* alert */);
entry.setInterruption();
@@ -77,9 +80,7 @@
* @return true if notification is removed, false otherwise
*/
public boolean removeNotification(@NonNull String key, boolean releaseImmediately) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "removeNotification");
- }
+ mLogger.logRemoveNotification(key, releaseImmediately);
AlertEntry alertEntry = mAlertEntries.get(key);
if (alertEntry == null) {
return true;
@@ -100,11 +101,8 @@
* removal time
*/
public void updateNotification(@NonNull String key, boolean alert) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "updateNotification");
- }
-
AlertEntry alertEntry = mAlertEntries.get(key);
+ mLogger.logUpdateNotification(key, alert, alertEntry != null);
if (alertEntry == null) {
// the entry was released before this update (i.e by a listener) This can happen
// with the groupmanager
@@ -121,9 +119,7 @@
* Clears all managed notifications.
*/
public void releaseAllImmediately() {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "releaseAllImmediately");
- }
+ mLogger.logReleaseAllImmediately();
// A copy is necessary here as we are changing the underlying map. This would cause
// undefined behavior if we iterated over the key set directly.
ArraySet<String> keysToRemove = new ArraySet<>(mAlertEntries.keySet());
@@ -300,9 +296,7 @@
* @param updatePostTime whether or not to refresh the post time
*/
public void updateEntry(boolean updatePostTime) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "updateEntry");
- }
+ mLogger.logUpdateEntry(updatePostTime);
long currentTime = mClock.currentTimeMillis();
mEarliestRemovaltime = currentTime + mMinimumDisplayTime;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index c25bc84..e189b26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -877,8 +877,8 @@
args.arg5 = requireConfirmation;
args.argi1 = userId;
args.arg6 = opPackageName;
- args.arg7 = operationId;
- args.arg8 = requestId;
+ args.argl1 = operationId;
+ args.argl2 = requestId;
args.argi2 = multiSensorConfig;
mHandler.obtainMessage(MSG_BIOMETRIC_SHOW, args)
.sendToTarget();
@@ -1359,9 +1359,9 @@
(boolean) someArgs.arg4 /* credentialAllowed */,
(boolean) someArgs.arg5 /* requireConfirmation */,
someArgs.argi1 /* userId */,
- (long) someArgs.arg7 /* operationId */,
+ someArgs.argl1 /* operationId */,
(String) someArgs.arg6 /* opPackageName */,
- (long) someArgs.arg8 /* requestId */,
+ someArgs.argl2 /* requestId */,
someArgs.argi2 /* multiSensorConfig */);
}
someArgs.recycle();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt
index cf34db2..4272bb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt
@@ -85,24 +85,30 @@
* is no difference. the new-after-modification state also won't be included if there's no
* difference from the new state.
*
- * @param old the disable state that had been previously sent.
+ * @param old the disable state that had been previously sent. Null if we don't need to log the
+ * previously sent state.
* @param new the new disable state that has just been sent.
* @param newAfterLocalModification the new disable states after a class has locally modified
* them. Null if the class does not locally modify.
*/
fun getDisableFlagsString(
- old: DisableState,
+ old: DisableState? = null,
new: DisableState,
newAfterLocalModification: DisableState? = null
): String {
val builder = StringBuilder("Received new disable state. ")
- builder.append("Old: ")
- builder.append(getFlagsString(old))
- builder.append(" | New: ")
- if (old != new) {
+
+ old?.let {
+ builder.append("Old: ")
+ builder.append(getFlagsString(old))
+ builder.append(" | ")
+ }
+
+ builder.append("New: ")
+ if (old != null && old != new) {
builder.append(getFlagsStringWithDiff(old, new))
} else {
- builder.append(getFlagsString(old))
+ builder.append(getFlagsString(new))
}
if (newAfterLocalModification != null && new != newAfterLocalModification) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 77e329f..03d8e7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -208,11 +208,6 @@
lateinit var isScrimOpaqueChangedListener: Consumer<Boolean>
/**
- * A runnable to call when the scrim has been fully revealed. This is only invoked once
- */
- var fullyRevealedRunnable: Runnable? = null
-
- /**
* How much of the underlying views are revealed, in percent. 0 means they will be completely
* obscured and 1 means they'll be fully visible.
*/
@@ -223,20 +218,10 @@
revealEffect.setRevealAmountOnScrim(value, this)
updateScrimOpaque()
- maybeTriggerFullyRevealedRunnable()
invalidate()
}
}
- private fun maybeTriggerFullyRevealedRunnable() {
- if (revealAmount == 1.0f) {
- fullyRevealedRunnable?.let {
- it.run()
- fullyRevealedRunnable = null
- }
- }
- }
-
/**
* The [LightRevealEffect] used to manipulate the radial gradient whenever [revealAmount]
* changes.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 5648741e..46004db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -37,9 +37,10 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.BiometricUnlockController
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
import com.android.systemui.statusbar.phone.DozeParameters
-import com.android.systemui.statusbar.phone.PanelExpansionListener
import com.android.systemui.statusbar.phone.ScrimController
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.WallpaperController
import java.io.FileDescriptor
@@ -73,10 +74,6 @@
private const val TAG = "DepthController"
}
- /**
- * Did we already unblur while dozing?
- */
- private var alreadyUnblurredWhileDozing = false
lateinit var root: View
private var blurRoot: View? = null
private var keyguardAnimator: Animator? = null
@@ -233,11 +230,9 @@
private val keyguardStateCallback = object : KeyguardStateController.Callback {
override fun onKeyguardFadingAwayChanged() {
if (!keyguardStateController.isKeyguardFadingAway ||
- !biometricUnlockController.isWakeAndUnlock) {
+ biometricUnlockController.mode != MODE_WAKE_AND_UNLOCK) {
return
}
- // When wakeAndUnlocking the screen remains dozing, so we have to manually trigger
- // the unblur earlier
keyguardAnimator?.cancel()
keyguardAnimator = ValueAnimator.ofFloat(1f, 0f).apply {
@@ -259,7 +254,6 @@
})
start()
}
- alreadyUnblurredWhileDozing = statusBarStateController.dozeAmount != 0.0f
}
override fun onKeyguardShowingChanged() {
@@ -281,24 +275,10 @@
if (isDozing) {
shadeAnimation.finishIfRunning()
brightnessMirrorSpring.finishIfRunning()
-
- // unset this for safety, to be ready for the next wakeup
- alreadyUnblurredWhileDozing = false
}
}
override fun onDozeAmountChanged(linear: Float, eased: Float) {
- if (alreadyUnblurredWhileDozing) {
- if (linear == 0.0f) {
- // We finished waking up, let's reset
- alreadyUnblurredWhileDozing = false
- } else {
- // We've already handled the unbluring from the keyguardAnimator above.
- // if we would continue, we'd play another unzoom / blur animation from the
- // dozing changing.
- return
- }
- }
wakeAndUnlockBlurRadius = blurUtils.blurRadiusOfRatio(eased)
scheduleUpdate()
}
@@ -329,10 +309,12 @@
/**
* Update blurs when pulling down the shade
*/
- override fun onPanelExpansionChanged(rawExpansion: Float, tracking: Boolean) {
+ override fun onPanelExpansionChanged(
+ rawFraction: Float, expanded: Boolean, tracking: Boolean
+ ) {
val timestamp = SystemClock.elapsedRealtimeNanos()
val expansion = MathUtils.saturate(
- (rawExpansion - panelPullDownMinFraction) / (1f - panelPullDownMinFraction))
+ (rawFraction - panelPullDownMinFraction) / (1f - panelPullDownMinFraction))
if (shadeExpansion == expansion && prevTracking == tracking) {
prevTimestamp = timestamp
@@ -456,7 +438,6 @@
it.println("blursDisabledForAppLaunch: $blursDisabledForAppLaunch")
it.println("qsPanelExpansion: $qsPanelExpansion")
it.println("transitionToFullShadeProgress: $transitionToFullShadeProgress")
- it.println("alreadyUnblurredWhileDozing: $alreadyUnblurredWhileDozing")
it.println("lastAppliedBlur: $lastAppliedBlur")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index bb697c3..74ea19f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -67,11 +67,13 @@
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
-import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.statusbar.phone.SystemUIHostDialogProvider;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowModule;
+import com.android.systemui.statusbar.window.StatusBarWindowView;
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.time.SystemClock;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index 6f8a5a8..ce86953 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -394,11 +394,11 @@
animationScheduler.addCallback(systemStatusAnimationCallback)
}
- val left = contentInsetsProvider.getStatusBarContentInsetsForRotation(ROTATION_SEASCAPE)
- val top = contentInsetsProvider.getStatusBarContentInsetsForRotation(ROTATION_NONE)
- val right = contentInsetsProvider.getStatusBarContentInsetsForRotation(ROTATION_LANDSCAPE)
+ val left = contentInsetsProvider.getStatusBarContentAreaForRotation(ROTATION_SEASCAPE)
+ val top = contentInsetsProvider.getStatusBarContentAreaForRotation(ROTATION_NONE)
+ val right = contentInsetsProvider.getStatusBarContentAreaForRotation(ROTATION_LANDSCAPE)
val bottom = contentInsetsProvider
- .getStatusBarContentInsetsForRotation(ROTATION_UPSIDE_DOWN)
+ .getStatusBarContentAreaForRotation(ROTATION_UPSIDE_DOWN)
val paddingTop = contentInsetsProvider.getStatusBarPaddingTop()
synchronized(lock) {
@@ -529,11 +529,11 @@
// Returns [left, top, right, bottom] aka [seascape, none, landscape, upside-down]
private fun getLayoutRects(): List<Rect> {
- val left = contentInsetsProvider.getStatusBarContentInsetsForRotation(ROTATION_SEASCAPE)
- val top = contentInsetsProvider.getStatusBarContentInsetsForRotation(ROTATION_NONE)
- val right = contentInsetsProvider.getStatusBarContentInsetsForRotation(ROTATION_LANDSCAPE)
+ val left = contentInsetsProvider.getStatusBarContentAreaForRotation(ROTATION_SEASCAPE)
+ val top = contentInsetsProvider.getStatusBarContentAreaForRotation(ROTATION_NONE)
+ val right = contentInsetsProvider.getStatusBarContentAreaForRotation(ROTATION_LANDSCAPE)
val bottom = contentInsetsProvider
- .getStatusBarContentInsetsForRotation(ROTATION_UPSIDE_DOWN)
+ .getStatusBarContentAreaForRotation(ROTATION_UPSIDE_DOWN)
return listOf(left, top, right, bottom)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
index 589446f..d5a0467 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
@@ -26,8 +26,7 @@
import android.widget.FrameLayout
import com.android.systemui.R
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher
-import com.android.systemui.statusbar.phone.StatusBarWindowController
-import com.android.systemui.statusbar.phone.StatusBarWindowView
+import com.android.systemui.statusbar.window.StatusBarWindowController
import javax.inject.Inject
/**
@@ -35,7 +34,6 @@
*/
class SystemEventChipAnimationController @Inject constructor(
private val context: Context,
- private val statusBarWindowView: StatusBarWindowView,
private val statusBarWindowController: StatusBarWindowController,
private val locationPublisher: StatusBarLocationPublisher
) : SystemStatusChipAnimationCallback {
@@ -126,7 +124,7 @@
animationDotView = animationWindowView.findViewById(R.id.dot_view)
val lp = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
lp.gravity = Gravity.END or Gravity.CENTER_VERTICAL
- statusBarWindowView.addView(animationWindowView, lp)
+ statusBarWindowController.addViewToWindow(animationWindowView, lp)
}
private fun start() = if (animationWindowView.isLayoutRtl) right() else left()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
index dcf8e73..5a27329 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
@@ -31,8 +31,8 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.phone.StatusBarWindowController
import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.Assert
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.time.SystemClock
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
index 80577ee..7cdf69d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
@@ -26,7 +26,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.shared.system.InputChannelCompat
import com.android.systemui.shared.system.InputMonitorCompat
-import com.android.systemui.statusbar.phone.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowController
import javax.inject.Inject
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index a2c9ffc..38b5ee8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -27,8 +27,8 @@
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.statusbar.phone.PanelExpansionListener
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
import javax.inject.Inject
@@ -294,8 +294,8 @@
this.state = newState
}
- override fun onPanelExpansionChanged(expansion: Float, tracking: Boolean) {
- val collapsedEnough = expansion <= 0.9f
+ override fun onPanelExpansionChanged(fraction: Float, expanded: Boolean, tracking: Boolean) {
+ val collapsedEnough = fraction <= 0.9f
if (collapsedEnough != this.collapsedEnoughToHide) {
val couldShowPulsingHuns = canShowPulsingHuns
this.collapsedEnoughToHide = collapsedEnough
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
index 3bfdf5c..8b0252b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
@@ -31,6 +31,11 @@
var parent: GroupEntry?,
/**
+ * Identifies the notification order in the entire notification list
+ */
+ var stableIndex: Int = -1,
+
+ /**
* The section that this ListEntry was sorted into. If the child of the group, this will be the
* parent's section. Null if not attached to the list.
*/
@@ -58,6 +63,7 @@
/** Copies the state of another instance. */
fun clone(other: ListAttachState) {
parent = other.parent
+ stableIndex = other.stableIndex
section = other.section
excludingFilter = other.excludingFilter
promoter = other.promoter
@@ -70,6 +76,7 @@
section = null
excludingFilter = null
promoter = null
+ stableIndex = -1
suppressedChanges.reset()
}
@@ -78,6 +85,7 @@
fun create(): ListAttachState {
return ListAttachState(
null,
+ -1,
null,
null,
null,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 6d38389..15872da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -775,18 +775,44 @@
for (NotificationEntry child : parent.getChildren()) {
setEntrySection(child, section);
}
- parent.sortChildren(sChildComparator);
+ parent.sortChildren(mGroupChildrenComparator);
}
}
-
- // Finally, sort all top-level elements
mNotifList.sort(mTopLevelComparator);
+ assignIndexes(mNotifList);
- // notify sections since the list is sorted now
notifySectionEntriesUpdated();
Trace.endSection();
}
+ /**
+ * Assign the index of each notification relative to the total order
+ * @param notifList
+ */
+ private void assignIndexes(List<ListEntry> notifList) {
+ if (notifList.size() == 0) return;
+ NotifSection currentSection = notifList.get(0).getSection();
+ int sectionMemberIndex = 0;
+ for (int i = 0; i < notifList.size(); i++) {
+ ListEntry entry = notifList.get(i);
+ NotifSection section = entry.getSection();
+ if (section.getIndex() != currentSection.getIndex()) {
+ sectionMemberIndex = 0;
+ currentSection = section;
+ }
+ entry.getAttachState().setStableIndex(sectionMemberIndex);
+ if (entry instanceof GroupEntry) {
+ GroupEntry parent = (GroupEntry) entry;
+ for (int j = 0; j < parent.getChildren().size(); j++) {
+ entry = parent.getChildren().get(j);
+ entry.getAttachState().setStableIndex(sectionMemberIndex);
+ sectionMemberIndex++;
+ }
+ }
+ sectionMemberIndex++;
+ }
+ }
+
private void freeEmptyGroups() {
mGroups.values().removeIf(ge -> ge.getSummary() == null && ge.getChildren().isEmpty());
}
@@ -891,48 +917,53 @@
}
private final Comparator<ListEntry> mTopLevelComparator = (o1, o2) -> {
-
int cmp = Integer.compare(
o1.getSectionIndex(),
o2.getSectionIndex());
+ if (cmp != 0) return cmp;
- if (cmp == 0) {
- for (int i = 0; i < mNotifComparators.size(); i++) {
- cmp = mNotifComparators.get(i).compare(o1, o2);
- if (cmp != 0) {
- break;
- }
- }
+ int index1 = canReorder(o1) ? -1 : o1.getPreviousAttachState().getStableIndex();
+ int index2 = canReorder(o2) ? -1 : o2.getPreviousAttachState().getStableIndex();
+ cmp = Integer.compare(index1, index2);
+ if (cmp != 0) return cmp;
+
+ for (int i = 0; i < mNotifComparators.size(); i++) {
+ cmp = mNotifComparators.get(i).compare(o1, o2);
+ if (cmp != 0) return cmp;
}
final NotificationEntry rep1 = o1.getRepresentativeEntry();
final NotificationEntry rep2 = o2.getRepresentativeEntry();
-
- if (cmp == 0) {
cmp = rep1.getRanking().getRank() - rep2.getRanking().getRank();
- }
+ if (cmp != 0) return cmp;
- if (cmp == 0) {
- cmp = Long.compare(
- rep2.getSbn().getNotification().when,
- rep1.getSbn().getNotification().when);
- }
-
+ cmp = Long.compare(
+ rep2.getSbn().getNotification().when,
+ rep1.getSbn().getNotification().when);
return cmp;
};
- private static final Comparator<NotificationEntry> sChildComparator = (o1, o2) -> {
- int cmp = o1.getRanking().getRank() - o2.getRanking().getRank();
- if (cmp == 0) {
- cmp = Long.compare(
- o2.getSbn().getNotification().when,
- o1.getSbn().getNotification().when);
- }
+ private final Comparator<ListEntry> mGroupChildrenComparator = (o1, o2) -> {
+ int index1 = canReorder(o1) ? -1 : o1.getPreviousAttachState().getStableIndex();
+ int index2 = canReorder(o2) ? -1 : o2.getPreviousAttachState().getStableIndex();
+ int cmp = Integer.compare(index1, index2);
+ if (cmp != 0) return cmp;
+ cmp = o1.getRepresentativeEntry().getRanking().getRank()
+ - o2.getRepresentativeEntry().getRanking().getRank();
+ if (cmp != 0) return cmp;
+
+ cmp = Long.compare(
+ o2.getRepresentativeEntry().getSbn().getNotification().when,
+ o1.getRepresentativeEntry().getSbn().getNotification().when);
return cmp;
};
+ private boolean canReorder(ListEntry entry) {
+ return mNotifStabilityManager.isEntryReorderingAllowed(entry);
+ }
+
private boolean applyFilters(NotificationEntry entry, long now, List<NotifFilter> filters) {
final NotifFilter filter = findRejectingFilter(entry, now, filters);
entry.getAttachState().setExcludingFilter(filter);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index bf3e712..7cbda25d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -98,6 +98,7 @@
mOrderedSections.add(conversationCoordinator.sectioner) // People
mOrderedSections.add(rankingCoordinator.alertingSectioner) // Alerting
mOrderedSections.add(rankingCoordinator.silentSectioner) // Silent
+ mOrderedSections.add(rankingCoordinator.minimizedSectioner) // Minimized
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index 2ab2dd0..79eb089 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -52,6 +53,8 @@
private final NodeController mSilentNodeController;
private final SectionHeaderController mSilentHeaderController;
private final NodeController mAlertingHeaderController;
+ private boolean mHasSilentEntries;
+ private boolean mHasMinimizedEntries;
@Inject
public RankingCoordinator(
@@ -73,6 +76,7 @@
pipeline.addPreGroupFilter(mSuspendedFilter);
pipeline.addPreGroupFilter(mDndVisualEffectsFilter);
+ pipeline.addOnBeforeSortListener(entries -> resetClearAllFlags());
}
public NotifSectioner getAlertingSectioner() {
@@ -83,6 +87,10 @@
return mSilentNotifSectioner;
}
+ public NotifSectioner getMinimizedSectioner() {
+ return mMinimizedNotifSectioner;
+ }
+
private final NotifSectioner mAlertingNotifSectioner = new NotifSectioner("Alerting",
NotificationPriorityBucketKt.BUCKET_ALERTING) {
@Override
@@ -105,7 +113,8 @@
NotificationPriorityBucketKt.BUCKET_SILENT) {
@Override
public boolean isInSection(ListEntry entry) {
- return !mHighPriorityProvider.isHighPriority(entry);
+ return !mHighPriorityProvider.isHighPriority(entry)
+ && !entry.getRepresentativeEntry().isAmbient();
}
@Nullable
@@ -119,11 +128,40 @@
public void onEntriesUpdated(@NonNull List<ListEntry> entries) {
for (int i = 0; i < entries.size(); i++) {
if (entries.get(i).getRepresentativeEntry().getSbn().isClearable()) {
- mSilentHeaderController.setClearSectionEnabled(true);
- return;
+ mHasSilentEntries = true;
+ break;
}
}
- mSilentHeaderController.setClearSectionEnabled(false);
+ mSilentHeaderController.setClearSectionEnabled(
+ mHasSilentEntries | mHasMinimizedEntries);
+ }
+ };
+
+ private final NotifSectioner mMinimizedNotifSectioner = new NotifSectioner("Minimized",
+ NotificationPriorityBucketKt.BUCKET_SILENT) {
+ @Override
+ public boolean isInSection(ListEntry entry) {
+ return !mHighPriorityProvider.isHighPriority(entry)
+ && entry.getRepresentativeEntry().isAmbient();
+ }
+
+ @Nullable
+ @Override
+ public NodeController getHeaderNodeController() {
+ return mSilentNodeController;
+ }
+
+ @Nullable
+ @Override
+ public void onEntriesUpdated(@NonNull List<ListEntry> entries) {
+ for (int i = 0; i < entries.size(); i++) {
+ if (entries.get(i).getRepresentativeEntry().getSbn().isClearable()) {
+ mHasMinimizedEntries = true;
+ break;
+ }
+ }
+ mSilentHeaderController.setClearSectionEnabled(
+ mHasSilentEntries | mHasMinimizedEntries);
}
};
@@ -151,6 +189,12 @@
}
};
+ @VisibleForTesting
+ protected void resetClearAllFlags() {
+ mHasSilentEntries = false;
+ mHasMinimizedEntries = false;
+ }
+
private final StatusBarStateController.StateListener mStatusBarStateCallback =
new StatusBarStateController.StateListener() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 5ba4c2f..32b1cf6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -27,6 +27,7 @@
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
@@ -98,7 +99,9 @@
pipeline.setVisualStabilityManager(mNotifStabilityManager);
}
-
+ // TODO(b/203828145): Ensure stability manager handles minimized state changes
+ // TODO(b/203826051): Ensure stability manager can allow reordering off-screen
+ // HUNs to the top of the shade
private final NotifStabilityManager mNotifStabilityManager =
new NotifStabilityManager("VisualStabilityCoordinator") {
@Override
@@ -126,6 +129,11 @@
}
return isSectionChangeAllowedForEntry;
}
+
+ @Override
+ public boolean isEntryReorderingAllowed(ListEntry section) {
+ return mReorderingAllowed;
+ }
};
private void updateAllowedStates() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java
index 58d4b97..520791c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable;
+import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
/**
@@ -52,4 +53,11 @@
* being suppressed.
*/
public abstract boolean isSectionChangeAllowed(NotificationEntry entry);
+
+ /**
+ * Is a notification entry is allowed be reordered
+ * @param entry
+ * @return if can re-order
+ */
+ public abstract boolean isEntryReorderingAllowed(ListEntry entry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
index 1cc1dcf..6b79680 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
@@ -156,6 +156,7 @@
groupList: Sequence<NotificationChannelGroup>
): Sequence<NotificationChannel> {
+ // TODO (b/194833441): remove channel level settings when we move to a permission
val channels = groupList
.flatMap { group ->
group.channels.asSequence().filterNot { channel ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index ccd4843..b0ee37b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -17,17 +17,20 @@
package com.android.systemui.statusbar.notification.row;
import static android.app.Notification.Action.SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY;
+import static android.os.UserHandle.USER_SYSTEM;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
+import android.Manifest;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.content.Context;
@@ -45,6 +48,10 @@
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.permission.PermissionManager;
+import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.AttributeSet;
@@ -107,6 +114,7 @@
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
+import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
import com.android.systemui.util.DumpUtilsKt;
import com.android.systemui.wmshell.BubblesManager;
@@ -363,23 +371,35 @@
* <b>Note</b>, this should be run in the background thread if possible as it makes multiple IPC
* calls.
*/
- private static Boolean isSystemNotification(
- Context context, StatusBarNotification statusBarNotification) {
- PackageManager packageManager = StatusBar.getPackageManagerForUser(
- context, statusBarNotification.getUser().getIdentifier());
- Boolean isSystemNotification = null;
+ private static Boolean isSystemNotification(Context context, StatusBarNotification sbn) {
+ // TODO (b/194833441): clean up before launch
+ if (Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, 0, USER_SYSTEM) == 1) {
+ INotificationManager iNm = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ try {
+ return iNm.isPermissionFixed(sbn.getPackageName(), sbn.getUserId());
+ } catch (RemoteException e) {
+ Log.e(TAG, "cannot reach NMS");
+ }
+ return false;
+ } else {
+ PackageManager packageManager = StatusBar.getPackageManagerForUser(
+ context, sbn.getUser().getIdentifier());
+ Boolean isSystemNotification = null;
- try {
- PackageInfo packageInfo = packageManager.getPackageInfo(
- statusBarNotification.getPackageName(), PackageManager.GET_SIGNATURES);
+ try {
+ PackageInfo packageInfo = packageManager.getPackageInfo(
+ sbn.getPackageName(), PackageManager.GET_SIGNATURES);
- isSystemNotification =
- com.android.settingslib.Utils.isSystemPackage(
- context.getResources(), packageManager, packageInfo);
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "cacheIsSystemNotification: Could not find package info");
+ isSystemNotification =
+ com.android.settingslib.Utils.isSystemPackage(
+ context.getResources(), packageManager, packageInfo);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "cacheIsSystemNotification: Could not find package info");
+ }
+ return isSystemNotification;
}
- return isSystemNotification;
}
public NotificationContentView[] getLayouts() {
@@ -533,6 +553,7 @@
mEntry.mIsSystemNotification = isSystemNotification(mContext, mEntry.getSbn());
}
+ // TODO (b/194833441): remove when we've migrated to permission
boolean isNonblockable = mEntry.getChannel().isImportanceLockedByOEM()
|| mEntry.getChannel().isImportanceLockedByCriticalDeviceFunction();
@@ -1548,6 +1569,7 @@
*/
public void initialize(
NotificationEntry entry,
+ RemoteInputViewSubcomponent.Factory rivSubcomponentFactory,
String appName,
String notificationKey,
ExpansionLogger logger,
@@ -1592,6 +1614,7 @@
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
for (NotificationContentView l : mLayouts) {
l.setPeopleNotificationIdentifier(mPeopleNotificationIdentifier);
+ l.setRemoteInputViewSubcomponentFactory(rivSubcomponentFactory);
}
mOnUserInteractionCallback = onUserInteractionCallback;
mBubblesManagerOptional = bubblesManagerOptional;
@@ -1760,7 +1783,9 @@
* Called when a notification is dropped on proper target window.
*/
public void dragAndDropSuccess() {
- mOnDragSuccessListener.onDragSuccess(getEntry());
+ if (mOnDragSuccessListener != null) {
+ mOnDragSuccessListener.onDragSuccess(getEntry());
+ }
}
private void doLongClickCallback() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 0662a1e..0b29ae5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -44,6 +44,7 @@
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
import com.android.systemui.util.time.SystemClock;
import com.android.systemui.wmshell.BubblesManager;
@@ -60,6 +61,7 @@
public class ExpandableNotificationRowController implements NodeController {
private final ExpandableNotificationRow mView;
private final NotificationListContainer mListContainer;
+ private final RemoteInputViewSubcomponent.Factory mRemoteInputViewSubcomponentFactory;
private final ActivatableNotificationViewController mActivatableNotificationViewController;
private final NotificationMediaManager mMediaManager;
private final PluginManager mPluginManager;
@@ -92,6 +94,7 @@
public ExpandableNotificationRowController(
ExpandableNotificationRow view,
NotificationListContainer listContainer,
+ RemoteInputViewSubcomponent.Factory rivSubcomponentFactory,
ActivatableNotificationViewController activatableNotificationViewController,
NotificationMediaManager mediaManager,
PluginManager pluginManager,
@@ -116,6 +119,7 @@
ExpandableNotificationRowDragController dragController) {
mView = view;
mListContainer = listContainer;
+ mRemoteInputViewSubcomponentFactory = rivSubcomponentFactory;
mActivatableNotificationViewController = activatableNotificationViewController;
mMediaManager = mediaManager;
mPluginManager = pluginManager;
@@ -148,6 +152,7 @@
mActivatableNotificationViewController.init();
mView.initialize(
entry,
+ mRemoteInputViewSubcomponentFactory,
mAppName,
mNotificationKey,
mExpansionLogger,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index df484dd..81b3635 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -54,9 +54,11 @@
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
import com.android.systemui.statusbar.policy.RemoteInputView;
+import com.android.systemui.statusbar.policy.RemoteInputViewController;
import com.android.systemui.statusbar.policy.SmartReplyConstants;
import com.android.systemui.statusbar.policy.SmartReplyStateInflaterKt;
import com.android.systemui.statusbar.policy.SmartReplyView;
+import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
import com.android.systemui.wmshell.BubblesManager;
import java.io.FileDescriptor;
@@ -101,6 +103,8 @@
private SmartReplyConstants mSmartReplyConstants;
private SmartReplyView mExpandedSmartReplyView;
private SmartReplyView mHeadsUpSmartReplyView;
+ @Nullable private RemoteInputViewController mExpandedRemoteInputController;
+ @Nullable private RemoteInputViewController mHeadsUpRemoteInputController;
private SmartReplyController mSmartReplyController;
private InflatedSmartReplyViewHolder mExpandedInflatedSmartReplies;
private InflatedSmartReplyViewHolder mHeadsUpInflatedSmartReplies;
@@ -125,6 +129,8 @@
private RemoteInputController mRemoteInputController;
private Runnable mExpandedVisibleListener;
private PeopleNotificationIdentifier mPeopleIdentifier;
+ private RemoteInputViewSubcomponent.Factory mRemoteInputSubcomponentFactory;
+
/**
* List of listeners for when content views become inactive (i.e. not the showing view).
*/
@@ -159,10 +165,17 @@
private boolean mUserExpanding;
private int mSingleLineWidthIndention;
private boolean mForceSelectNextLayout = true;
- private PendingIntent mPreviousExpandedRemoteInputIntent;
- private PendingIntent mPreviousHeadsUpRemoteInputIntent;
+
+ // Cache for storing the RemoteInputView during a notification update. Needed because
+ // setExpandedChild sets the actual field to null, but then onNotificationUpdated will restore
+ // it from the cache, if present, otherwise inflate a new one.
+ // ONLY USED WHEN THE ORIGINAL WAS isActive() WHEN REPLACED
private RemoteInputView mCachedExpandedRemoteInput;
private RemoteInputView mCachedHeadsUpRemoteInput;
+ private RemoteInputViewController mCachedExpandedRemoteInputViewController;
+ private RemoteInputViewController mCachedHeadsUpRemoteInputViewController;
+ private PendingIntent mPreviousExpandedRemoteInputIntent;
+ private PendingIntent mPreviousHeadsUpRemoteInputIntent;
private int mContentHeightAtAnimationStart = UNDEFINED;
private boolean mFocusOnVisibilityChange;
@@ -399,6 +412,7 @@
if (mExpandedRemoteInput.isActive()) {
mPreviousExpandedRemoteInputIntent = mExpandedRemoteInput.getPendingIntent();
mCachedExpandedRemoteInput = mExpandedRemoteInput;
+ mCachedExpandedRemoteInputViewController = mExpandedRemoteInputController;
mExpandedRemoteInput.dispatchStartTemporaryDetach();
((ViewGroup)mExpandedRemoteInput.getParent()).removeView(mExpandedRemoteInput);
}
@@ -407,6 +421,7 @@
mExpandedChild.animate().cancel();
removeView(mExpandedChild);
mExpandedRemoteInput = null;
+ mExpandedRemoteInputController = null;
}
if (child == null) {
mExpandedChild = null;
@@ -441,6 +456,7 @@
if (mHeadsUpRemoteInput.isActive()) {
mPreviousHeadsUpRemoteInputIntent = mHeadsUpRemoteInput.getPendingIntent();
mCachedHeadsUpRemoteInput = mHeadsUpRemoteInput;
+ mCachedHeadsUpRemoteInputViewController = mHeadsUpRemoteInputController;
mHeadsUpRemoteInput.dispatchStartTemporaryDetach();
((ViewGroup)mHeadsUpRemoteInput.getParent()).removeView(mHeadsUpRemoteInput);
}
@@ -449,6 +465,7 @@
mHeadsUpChild.animate().cancel();
removeView(mHeadsUpChild);
mHeadsUpRemoteInput = null;
+ mHeadsUpRemoteInputController = null;
}
if (child == null) {
mHeadsUpChild = null;
@@ -1127,7 +1144,7 @@
if (mHeadsUpChild != null) {
mHeadsUpWrapper.onContentUpdated(row);
}
- applyRemoteInputAndSmartReply(entry);
+ applyRemoteInputAndSmartReply();
updateLegacy();
mForceSelectNextLayout = true;
mPreviousExpandedRemoteInputIntent = null;
@@ -1165,13 +1182,11 @@
return null != notification.findRemoteInputActionPair(true /* freeform */);
}
- private void applyRemoteInputAndSmartReply(final NotificationEntry entry) {
- if (mRemoteInputController == null) {
- return;
+ private void applyRemoteInputAndSmartReply() {
+ if (mRemoteInputController != null) {
+ applyRemoteInput();
}
- applyRemoteInput(entry, hasFreeformRemoteInput(entry));
-
if (mCurrentSmartReplyState == null) {
if (DEBUG) {
Log.d(TAG, "InflatedSmartReplies are null, don't add smart replies.");
@@ -1180,21 +1195,25 @@
}
if (DEBUG) {
Log.d(TAG, String.format("Adding suggestions for %s, %d actions, and %d replies.",
- entry.getSbn().getKey(),
+ mNotificationEntry.getSbn().getKey(),
mCurrentSmartReplyState.getSmartActionsList().size(),
mCurrentSmartReplyState.getSmartRepliesList().size()));
}
- applySmartReplyView(mCurrentSmartReplyState, entry);
+ applySmartReplyView();
}
- private void applyRemoteInput(NotificationEntry entry, boolean hasFreeformRemoteInput) {
- View bigContentView = mExpandedChild;
- if (bigContentView != null) {
- mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasFreeformRemoteInput,
- mPreviousExpandedRemoteInputIntent, mCachedExpandedRemoteInput,
+ private void applyRemoteInput() {
+ boolean hasFreeformRemoteInput = hasFreeformRemoteInput(mNotificationEntry);
+ if (mExpandedChild != null) {
+ RemoteInputViewData expandedData = applyRemoteInput(mExpandedChild, mNotificationEntry,
+ hasFreeformRemoteInput, mPreviousExpandedRemoteInputIntent,
+ mCachedExpandedRemoteInput, mCachedExpandedRemoteInputViewController,
mExpandedWrapper);
+ mExpandedRemoteInput = expandedData.mView;
+ mExpandedRemoteInputController = expandedData.mController;
} else {
mExpandedRemoteInput = null;
+ mExpandedRemoteInputController = null;
}
if (mCachedExpandedRemoteInput != null
&& mCachedExpandedRemoteInput != mExpandedRemoteInput) {
@@ -1202,14 +1221,18 @@
mCachedExpandedRemoteInput.dispatchFinishTemporaryDetach();
}
mCachedExpandedRemoteInput = null;
+ mCachedExpandedRemoteInputViewController = null;
- View headsUpContentView = mHeadsUpChild;
- if (headsUpContentView != null) {
- mHeadsUpRemoteInput = applyRemoteInput(
- headsUpContentView, entry, hasFreeformRemoteInput,
- mPreviousHeadsUpRemoteInputIntent, mCachedHeadsUpRemoteInput, mHeadsUpWrapper);
+ if (mHeadsUpChild != null) {
+ RemoteInputViewData headsUpData = applyRemoteInput(mHeadsUpChild, mNotificationEntry,
+ hasFreeformRemoteInput, mPreviousHeadsUpRemoteInputIntent,
+ mCachedHeadsUpRemoteInput, mCachedHeadsUpRemoteInputViewController,
+ mHeadsUpWrapper);
+ mHeadsUpRemoteInput = headsUpData.mView;
+ mHeadsUpRemoteInputController = headsUpData.mController;
} else {
mHeadsUpRemoteInput = null;
+ mHeadsUpRemoteInputController = null;
}
if (mCachedHeadsUpRemoteInput != null
&& mCachedHeadsUpRemoteInput != mHeadsUpRemoteInput) {
@@ -1217,22 +1240,24 @@
mCachedHeadsUpRemoteInput.dispatchFinishTemporaryDetach();
}
mCachedHeadsUpRemoteInput = null;
+ mCachedHeadsUpRemoteInputViewController = null;
}
-
- private RemoteInputView applyRemoteInput(View view, NotificationEntry entry,
- boolean hasRemoteInput, PendingIntent existingPendingIntent,
- RemoteInputView cachedView, NotificationViewWrapper wrapper) {
+ private RemoteInputViewData applyRemoteInput(View view, NotificationEntry entry,
+ boolean hasRemoteInput, PendingIntent existingPendingIntent, RemoteInputView cachedView,
+ RemoteInputViewController cachedController, NotificationViewWrapper wrapper) {
+ RemoteInputViewData result = new RemoteInputViewData();
View actionContainerCandidate = view.findViewById(
com.android.internal.R.id.actions_container);
if (actionContainerCandidate instanceof FrameLayout) {
- RemoteInputView existing = view.findViewWithTag(RemoteInputView.VIEW_TAG);
+ result.mView = view.findViewWithTag(RemoteInputView.VIEW_TAG);
- if (existing != null) {
- existing.onNotificationUpdateOrReset();
+ if (result.mView != null) {
+ result.mView.onNotificationUpdateOrReset();
+ result.mController = result.mView.getController();
}
- if (existing == null && hasRemoteInput) {
+ if (result.mView == null && hasRemoteInput) {
ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
if (cachedView == null) {
RemoteInputView riv = RemoteInputView.inflate(
@@ -1243,44 +1268,50 @@
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT)
);
- existing = riv;
+ result.mView = riv;
+ // Create a new controller for the view. The lifetime of the controller is 1:1
+ // with that of the view.
+ RemoteInputViewSubcomponent subcomponent =
+ mRemoteInputSubcomponentFactory.create();
+ result.mController = subcomponent.getController();
+ result.mView.setController(result.mController);
} else {
actionContainer.addView(cachedView);
cachedView.dispatchFinishTemporaryDetach();
cachedView.requestFocus();
- existing = cachedView;
+ result.mView = cachedView;
+ result.mController = cachedController;
}
}
if (hasRemoteInput) {
- existing.setWrapper(wrapper);
- existing.addOnVisibilityChangedListener(this::setRemoteInputVisible);
+ result.mView.setWrapper(wrapper);
+ result.mView.addOnVisibilityChangedListener(this::setRemoteInputVisible);
- if (existingPendingIntent != null || existing.isActive()) {
+ if (existingPendingIntent != null || result.mView.isActive()) {
// The current action could be gone, or the pending intent no longer valid.
// If we find a matching action in the new notification, focus, otherwise close.
Notification.Action[] actions = entry.getSbn().getNotification().actions;
if (existingPendingIntent != null) {
- existing.setPendingIntent(existingPendingIntent);
+ result.mView.setPendingIntent(existingPendingIntent);
}
- if (existing.updatePendingIntentFromActions(actions)) {
- if (!existing.isActive()) {
- existing.focus();
+ if (result.mView.updatePendingIntentFromActions(actions)) {
+ if (!result.mView.isActive()) {
+ result.mView.focus();
}
} else {
- if (existing.isActive()) {
- existing.close();
+ if (result.mView.isActive()) {
+ result.mView.close();
}
}
}
}
- if (existing != null) {
+ if (result.mView != null) {
int backgroundColor = entry.getRow().getCurrentBackgroundTint();
- boolean colorized = mNotificationEntry.getSbn().getNotification().isColorized();
- existing.setBackgroundTintColor(backgroundColor, colorized);
+ boolean colorized = entry.getSbn().getNotification().isColorized();
+ result.mView.setBackgroundTintColor(backgroundColor, colorized);
}
- return existing;
}
- return null;
+ return result;
}
/**
@@ -1373,19 +1404,19 @@
actionContainer.setVisibility(VISIBLE);
}
- private void applySmartReplyView(
- InflatedSmartReplyState state,
- NotificationEntry entry) {
+ private void applySmartReplyView() {
if (mContractedChild != null) {
- applyExternalSmartReplyState(mContractedChild, state);
+ applyExternalSmartReplyState(mContractedChild, mCurrentSmartReplyState);
}
if (mExpandedChild != null) {
- applyExternalSmartReplyState(mExpandedChild, state);
- mExpandedSmartReplyView = applySmartReplyView(mExpandedChild, state,
- entry, mExpandedInflatedSmartReplies);
+ applyExternalSmartReplyState(mExpandedChild, mCurrentSmartReplyState);
+ mExpandedSmartReplyView = applySmartReplyView(mExpandedChild, mCurrentSmartReplyState,
+ mNotificationEntry, mExpandedInflatedSmartReplies);
if (mExpandedSmartReplyView != null) {
- SmartReplyView.SmartReplies smartReplies = state.getSmartReplies();
- SmartReplyView.SmartActions smartActions = state.getSmartActions();
+ SmartReplyView.SmartReplies smartReplies =
+ mCurrentSmartReplyState.getSmartReplies();
+ SmartReplyView.SmartActions smartActions =
+ mCurrentSmartReplyState.getSmartActions();
if (smartReplies != null || smartActions != null) {
int numSmartReplies = smartReplies == null ? 0 : smartReplies.choices.size();
int numSmartActions = smartActions == null ? 0 : smartActions.actions.size();
@@ -1396,16 +1427,16 @@
&& mSmartReplyConstants.getEffectiveEditChoicesBeforeSending(
smartReplies.remoteInput.getEditChoicesBeforeSending());
- mSmartReplyController.smartSuggestionsAdded(entry, numSmartReplies,
+ mSmartReplyController.smartSuggestionsAdded(mNotificationEntry, numSmartReplies,
numSmartActions, fromAssistant, editBeforeSending);
}
}
}
if (mHeadsUpChild != null) {
- applyExternalSmartReplyState(mHeadsUpChild, state);
+ applyExternalSmartReplyState(mHeadsUpChild, mCurrentSmartReplyState);
if (mSmartReplyConstants.getShowInHeadsUp()) {
- mHeadsUpSmartReplyView = applySmartReplyView(mHeadsUpChild, state,
- entry, mHeadsUpInflatedSmartReplies);
+ mHeadsUpSmartReplyView = applySmartReplyView(mHeadsUpChild, mCurrentSmartReplyState,
+ mNotificationEntry, mHeadsUpInflatedSmartReplies);
}
}
}
@@ -1439,7 +1470,7 @@
}
@Nullable
- private SmartReplyView applySmartReplyView(View view,
+ private static SmartReplyView applySmartReplyView(View view,
InflatedSmartReplyState smartReplyState,
NotificationEntry entry, InflatedSmartReplyViewHolder inflatedSmartReplyViewHolder) {
View smartReplyContainerCandidate = view.findViewById(
@@ -1481,7 +1512,7 @@
inflatedSmartReplyViewHolder.getSmartSuggestionButtons());
// Ensure the colors of the smart suggestion buttons are up-to-date.
int backgroundColor = entry.getRow().getCurrentBackgroundTint();
- boolean colorized = mNotificationEntry.getSbn().getNotification().isColorized();
+ boolean colorized = entry.getSbn().getNotification().isColorized();
smartReplyView.setBackgroundTintColor(backgroundColor, colorized);
smartReplyContainer.setVisibility(View.VISIBLE);
}
@@ -1977,4 +2008,13 @@
}
return Notification.COLOR_INVALID;
}
+
+ public void setRemoteInputViewSubcomponentFactory(RemoteInputViewSubcomponent.Factory factory) {
+ mRemoteInputSubcomponentFactory = factory;
+ }
+
+ private static class RemoteInputViewData {
+ @Nullable RemoteInputView mView;
+ @Nullable RemoteInputViewController mController;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
index becc9a7..a12d0073 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
@@ -37,8 +37,11 @@
/**
* Dagger Component for a {@link ExpandableNotificationRow}.
*/
-@Subcomponent(modules = {ExpandableNotificationRowComponent.ExpandableNotificationRowModule.class,
- ActivatableNotificationViewModule.class})
+@Subcomponent(modules = {
+ ActivatableNotificationViewModule.class,
+ ExpandableNotificationRowComponent.ExpandableNotificationRowModule.class,
+ RemoteInputViewModule.class
+})
@NotificationRowScope
public interface ExpandableNotificationRowComponent {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/RemoteInputViewModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/RemoteInputViewModule.kt
new file mode 100644
index 0000000..feb7ab5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/RemoteInputViewModule.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.systemui.statusbar.notification.row.dagger
+
+import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent
+import dagger.Module
+
+@Module(subcomponents = [RemoteInputViewSubcomponent::class])
+interface RemoteInputViewModule
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt
index 32d41a8..75ca337 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt
@@ -17,8 +17,6 @@
package com.android.systemui.statusbar.notification.stack
import android.content.Context
-import android.service.notification.NotificationListenerService.REASON_APP_CANCEL
-import android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL
import android.service.notification.NotificationListenerService.REASON_CANCEL
import android.service.notification.NotificationListenerService.REASON_CANCEL_ALL
import android.service.notification.NotificationListenerService.REASON_CLICK
@@ -76,7 +74,8 @@
Assert.isMainThread()
val isClearAll = reason == REASON_CANCEL_ALL
val isUserDismiss = reason == REASON_CANCEL || reason == REASON_CLICK
- val isAppCancel = reason == REASON_APP_CANCEL || reason == REASON_APP_CANCEL_ALL
+ // REASON_APP_CANCEL and REASON_APP_CANCEL_ALL are ignored, because the
+ // foreground service associated with it is gone.
val isSummaryCancel = reason == REASON_GROUP_SUMMARY_CANCELED
if (entry == null) return false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index cfe044a..2098a1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -53,6 +53,7 @@
import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.EncryptionHelper;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -101,6 +102,7 @@
private final StatusBarLocationPublisher mLocationPublisher;
private final FeatureFlags mFeatureFlags;
private final NotificationIconAreaController mNotificationIconAreaController;
+ private final PanelExpansionStateManager mPanelExpansionStateManager;
private final StatusBarIconController mStatusBarIconController;
private List<String> mBlockedIcons = new ArrayList<>();
@@ -126,6 +128,7 @@
SystemStatusAnimationScheduler animationScheduler,
StatusBarLocationPublisher locationPublisher,
NotificationIconAreaController notificationIconAreaController,
+ PanelExpansionStateManager panelExpansionStateManager,
FeatureFlags featureFlags,
StatusBarIconController statusBarIconController,
KeyguardStateController keyguardStateController,
@@ -140,6 +143,7 @@
mAnimationScheduler = animationScheduler;
mLocationPublisher = locationPublisher;
mNotificationIconAreaController = notificationIconAreaController;
+ mPanelExpansionStateManager = panelExpansionStateManager;
mFeatureFlags = featureFlags;
mStatusBarIconController = statusBarIconController;
mKeyguardStateController = keyguardStateController;
@@ -253,8 +257,8 @@
state1 = adjustDisableFlags(state1);
mCollapsedStatusBarFragmentLogger.logDisableFlagChange(
- new DisableState(state1BeforeAdjustment, state2),
- new DisableState(state1, state2));
+ /* new= */ new DisableState(state1BeforeAdjustment, state2),
+ /* newAfterLocalModification= */ new DisableState(state1, state2));
final int old1 = mDisabled1;
final int diff1 = state1 ^ old1;
@@ -363,7 +367,7 @@
private boolean shouldHideNotificationIcons() {
final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
- if (!mStatusBar.isClosed()
+ if (!mPanelExpansionStateManager.isClosed()
&& statusBarOptional.map(
StatusBar::hideStatusBarIconsWhenExpanded).orElse(false)) {
return true;
@@ -409,7 +413,7 @@
* don't set the clock GONE otherwise it'll mess up the animation.
*/
private int clockHiddenMode() {
- if (!mStatusBar.isClosed() && !mKeyguardStateController.isShowing()
+ if (!mPanelExpansionStateManager.isClosed() && !mKeyguardStateController.isShowing()
&& !mStatusBarStateController.isDozing()) {
return View.INVISIBLE;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLogger.kt
index 3c2b555..4d472e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLogger.kt
@@ -28,22 +28,31 @@
private val disableFlagsLogger: DisableFlagsLogger,
) {
- /** Logs a string representing the old and new disable flag states to [buffer]. */
+ /**
+ * Logs a string representing the new state received by [CollapsedStatusBarFragment] and any
+ * modifications that were made to the flags locally.
+ *
+ * @param new see [DisableFlagsLogger.getDisableFlagsString]
+ * @param newAfterLocalModification see [DisableFlagsLogger.getDisableFlagsString]
+ */
fun logDisableFlagChange(
- oldState: DisableFlagsLogger.DisableState,
- newState: DisableFlagsLogger.DisableState) {
+ new: DisableFlagsLogger.DisableState,
+ newAfterLocalModification: DisableFlagsLogger.DisableState
+ ) {
buffer.log(
TAG,
LogLevel.INFO,
{
- int1 = oldState.disable1
- int2 = oldState.disable2
- long1 = newState.disable1.toLong()
- long2 = newState.disable2.toLong()
+ int1 = new.disable1
+ int2 = new.disable2
+ long1 = newAfterLocalModification.disable1.toLong()
+ long2 = newAfterLocalModification.disable2.toLong()
},
{
disableFlagsLogger.getDisableFlagsString(
- DisableFlagsLogger.DisableState(int1, int2),
+ old = null,
+ new = DisableFlagsLogger.DisableState(int1, int2),
+ newAfterLocalModification =
DisableFlagsLogger.DisableState(long1.toInt(), long2.toInt())
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 21c3e5e..7de4668 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -21,7 +21,6 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
@@ -92,10 +91,14 @@
};
@Inject
- public DozeScrimController(DozeParameters dozeParameters, DozeLog dozeLog) {
+ public DozeScrimController(
+ DozeParameters dozeParameters,
+ DozeLog dozeLog,
+ StatusBarStateController statusBarStateController
+ ) {
mDozeParameters = dozeParameters;
- //Never expected to be destroyed
- Dependency.get(StatusBarStateController.class).addCallback(this);
+ // Never expected to be destroyed
+ statusBarStateController.addCallback(this);
mDozeLog = dozeLog;
}
@@ -219,6 +222,10 @@
@Override
public void onDozingChanged(boolean isDozing) {
+ if (mDozing != isDozing) {
+ mDozeLog.traceDozingChanged(isDozing);
+ }
+
setDozing(isDozing);
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index c81196d..01acce3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -38,6 +38,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import java.io.FileDescriptor;
@@ -101,11 +102,12 @@
// Constructor:
public HeadsUpManagerPhone(@NonNull final Context context,
+ HeadsUpManagerLogger logger,
StatusBarStateController statusBarStateController,
KeyguardBypassController bypassController,
GroupMembershipManager groupMembershipManager,
ConfigurationController configurationController) {
- super(context);
+ super(context, logger);
Resources resources = mContext.getResources();
mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
mAutoHeadsUpNotificationDecay = resources.getInteger(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 03f3b0c..00b54e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -47,6 +47,7 @@
import com.android.systemui.animation.Interpolators;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.statusbar.window.StatusBarWindowView;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -238,35 +239,32 @@
}
}
- @Override
- public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ /** Should only be called from {@link KeyguardStatusBarViewController}. */
+ WindowInsets updateWindowInsets(
+ WindowInsets insets,
+ StatusBarContentInsetsProvider insetsProvider) {
mLayoutState = LAYOUT_NONE;
- if (updateLayoutConsideringCutout()) {
+ if (updateLayoutConsideringCutout(insetsProvider)) {
requestLayout();
}
return super.onApplyWindowInsets(insets);
}
- private boolean updateLayoutConsideringCutout() {
+ private boolean updateLayoutConsideringCutout(StatusBarContentInsetsProvider insetsProvider) {
mDisplayCutout = getRootWindowInsets().getDisplayCutout();
updateKeyguardStatusBarHeight();
-
- Pair<Integer, Integer> cornerCutoutMargins =
- StatusBarWindowView.cornerCutoutMargins(mDisplayCutout, getDisplay());
- updatePadding(cornerCutoutMargins);
- if (mDisplayCutout == null || cornerCutoutMargins != null) {
+ updatePadding(insetsProvider);
+ if (mDisplayCutout == null || insetsProvider.currentRotationHasCornerCutout()) {
return updateLayoutParamsNoCutout();
} else {
return updateLayoutParamsForCutout();
}
}
- private void updatePadding(Pair<Integer, Integer> cornerCutoutMargins) {
+ private void updatePadding(StatusBarContentInsetsProvider insetsProvider) {
final int waterfallTop =
mDisplayCutout == null ? 0 : mDisplayCutout.getWaterfallInsets().top;
- mPadding =
- StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
- mDisplayCutout, cornerCutoutMargins, mRoundedCornerPadding);
+ mPadding = insetsProvider.getStatusBarContentInsetsForCurrentRotation();
// consider privacy dot space
final int minLeft = (isLayoutRtl() && mIsPrivacyDotEnabled)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 9055081..e7d5724 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -91,6 +91,7 @@
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final BiometricUnlockController mBiometricUnlockController;
private final SysuiStatusBarStateController mStatusBarStateController;
+ private final StatusBarContentInsetsProvider mInsetsProvider;
private final ConfigurationController.ConfigurationListener mConfigurationListener =
new ConfigurationController.ConfigurationListener() {
@@ -228,7 +229,9 @@
KeyguardBypassController bypassController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
BiometricUnlockController biometricUnlockController,
- SysuiStatusBarStateController statusBarStateController) {
+ SysuiStatusBarStateController statusBarStateController,
+ StatusBarContentInsetsProvider statusBarContentInsetsProvider
+ ) {
super(view);
mCarrierTextController = carrierTextController;
mConfigurationController = configurationController;
@@ -244,6 +247,7 @@
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mBiometricUnlockController = biometricUnlockController;
mStatusBarStateController = statusBarStateController;
+ mInsetsProvider = statusBarContentInsetsProvider;
mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
mKeyguardStateController.addCallback(
@@ -287,6 +291,9 @@
mTintedIconManager.setBlockList(mBlockedIcons);
mStatusBarIconController.addIconGroup(mTintedIconManager);
}
+ mView.setOnApplyWindowInsetsListener(
+ (view, windowInsets) -> mView.updateWindowInsets(windowInsets, mInsetsProvider));
+
onThemeChanged();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index fbe59a2..e66ad61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -39,6 +39,7 @@
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.wm.shell.bubbles.Bubbles;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 4ce33b3..f90bfe4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -34,9 +34,9 @@
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
-import static com.android.systemui.statusbar.phone.PanelBar.STATE_CLOSED;
-import static com.android.systemui.statusbar.phone.PanelBar.STATE_OPEN;
-import static com.android.systemui.statusbar.phone.PanelBar.STATE_OPENING;
+import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_CLOSED;
+import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_OPEN;
+import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_OPENING;
import static java.lang.Float.isNaN;
@@ -99,6 +99,7 @@
import com.android.internal.util.LatencyTracker;
import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardStatusViewController;
+import com.android.keyguard.KeyguardUnfoldTransition;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.LockIconViewController;
import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent;
@@ -181,12 +182,15 @@
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
+import com.android.systemui.statusbar.phone.panelstate.PanelState;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.util.Utils;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.wallet.controller.QuickAccessWalletController;
@@ -198,6 +202,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -345,6 +350,7 @@
private VelocityTracker mQsVelocityTracker;
private boolean mQsTracking;
+ private IdleHostView mIdleHostView;
private CommunalHostView mCommunalView;
/**
@@ -647,6 +653,8 @@
private boolean mStatusViewCentered = true;
+ private Optional<KeyguardUnfoldTransition> mKeyguardUnfoldTransition;
+
private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
@@ -765,7 +773,9 @@
SplitShadeHeaderController splitShadeHeaderController,
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
LockscreenGestureLogger lockscreenGestureLogger,
+ PanelExpansionStateManager panelExpansionStateManager,
NotificationRemoteInputManager remoteInputManager,
+ Optional<SysUIUnfoldComponent> unfoldComponent,
ControlsComponent controlsComponent) {
super(view,
falsingManager,
@@ -778,6 +788,7 @@
flingAnimationUtilsBuilder.get(),
statusBarTouchableRegionManager,
lockscreenGestureLogger,
+ panelExpansionStateManager,
ambientState);
mView = view;
mVibratorHelper = vibratorHelper;
@@ -851,6 +862,8 @@
new DynamicPrivacyControlListener();
dynamicPrivacyController.addListener(dynamicPrivacyControlListener);
+ panelExpansionStateManager.addStateListener(this::onPanelStateChanged);
+
mBottomAreaShadeAlphaAnimator = ValueAnimator.ofFloat(1f, 0);
mBottomAreaShadeAlphaAnimator.addUpdateListener(animation -> {
mBottomAreaShadeAlpha = (float) animation.getAnimatedValue();
@@ -884,6 +897,7 @@
}
mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count);
+ mKeyguardUnfoldTransition = unfoldComponent.map(c -> c.getKeyguardUnfoldTransition());
updateUserSwitcherFlags();
onFinishInflate();
}
@@ -891,6 +905,7 @@
private void onFinishInflate() {
loadDimens();
mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
+ mIdleHostView = mView.findViewById(R.id.idle_host_view);
mCommunalView = mView.findViewById(R.id.communal_host);
FrameLayout userAvatarContainer = null;
@@ -913,10 +928,11 @@
.getKeyguardStatusBarViewController();
mKeyguardStatusBarViewController.init();
- IdleViewComponent idleViewComponent = mIdleViewComponentFactory
- .build(mView.findViewById(R.id.idle_host_view));
- mIdleHostViewController = idleViewComponent.getIdleHostViewController();
- mIdleHostViewController.init();
+ if (mIdleHostView != null) {
+ IdleViewComponent idleViewComponent = mIdleViewComponentFactory.build(mIdleHostView);
+ mIdleHostViewController = idleViewComponent.getIdleHostViewController();
+ mIdleHostViewController.init();
+ }
if (mCommunalView != null) {
CommunalViewComponent communalViewComponent =
@@ -926,14 +942,14 @@
mCommunalViewController.init();
}
-
+ mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
updateViewControllers(
mView.findViewById(R.id.keyguard_status_view),
userAvatarContainer,
keyguardUserSwitcherView,
- mView.findViewById(R.id.idle_host_view),
+ mIdleHostView,
mCommunalView);
- mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
+
NotificationStackScrollLayout stackScrollLayout = mView.findViewById(
R.id.notification_stack_scroller);
mNotificationStackScrollLayoutController.attach(stackScrollLayout);
@@ -984,6 +1000,7 @@
}
mTapAgainViewController.init();
+ mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mNotificationContainerParent));
}
@Override
@@ -1029,9 +1046,12 @@
mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
mKeyguardStatusViewController.init();
- IdleViewComponent idleViewComponent = mIdleViewComponentFactory.build(idleHostView);
- mIdleHostViewController = idleViewComponent.getIdleHostViewController();
- mIdleHostViewController.init();
+ if (idleHostView != null && idleHostView != mIdleHostView) {
+ mIdleHostView = idleHostView;
+ IdleViewComponent idleViewComponent = mIdleViewComponentFactory.build(idleHostView);
+ mIdleHostViewController = idleViewComponent.getIdleHostViewController();
+ mIdleHostViewController.init();
+ }
if (mKeyguardUserSwitcherController != null) {
// Try to close the switcher so that callbacks are triggered if necessary.
@@ -1231,6 +1251,8 @@
mBarState);
}
setKeyguardBottomAreaVisibility(mBarState, false);
+
+ mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mNotificationContainerParent));
}
private void attachSplitShadeMediaPlayerContainer(FrameLayout container) {
@@ -1678,7 +1700,7 @@
// it's possible that nothing animated, so we replicate the termination
// conditions of panelExpansionChanged here
// TODO(b/200063118): This can likely go away in a future refactor CL.
- mBar.updateState(STATE_CLOSED);
+ getPanelExpansionStateManager().updateState(STATE_CLOSED);
}
}
@@ -1763,7 +1785,7 @@
@Override
public void fling(float vel, boolean expand) {
- GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
+ GestureRecorder gr = mStatusBar.getGestureRecorder();
if (gr != null) {
gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel);
}
@@ -2347,10 +2369,6 @@
mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
}
- for (int i = 0; i < mExpansionListeners.size(); i++) {
- mExpansionListeners.get(i).onQsExpansionChanged(
- mQsMaxExpansionHeight != 0 ? mQsExpansionHeight / mQsMaxExpansionHeight : 0);
- }
if (DEBUG) {
mView.invalidate();
}
@@ -2359,8 +2377,11 @@
private void updateQsExpansion() {
if (mQs == null) return;
float qsExpansionFraction = computeQsExpansionFraction();
+ float squishiness = mNotificationStackScrollLayoutController
+ .getNotificationSquishinessFraction();
mQs.setQsExpansion(qsExpansionFraction, getExpandedFraction(), getHeaderTranslation(),
- mNotificationStackScrollLayoutController.getNotificationSquishinessFraction());
+ mQsExpandImmediate || mQsExpanded ? 1f : squishiness);
+ mSplitShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
mMediaHierarchyManager.setQsExpansion(qsExpansionFraction);
int qsPanelBottomY = calculateQsBottomPosition(qsExpansionFraction);
mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
@@ -4836,37 +4857,27 @@
mView.removeCallbacks(mMaybeHideExpandedRunnable);
}
- private final PanelBar.PanelStateChangeListener mPanelStateChangeListener =
- new PanelBar.PanelStateChangeListener() {
+ @PanelState
+ private int mCurrentPanelState = STATE_CLOSED;
- @PanelBar.PanelState
- private int mCurrentState = STATE_CLOSED;
+ private void onPanelStateChanged(@PanelState int state) {
+ mAmbientState.setIsShadeOpening(state == STATE_OPENING);
+ updateQSExpansionEnabledAmbient();
- @Override
- public void onStateChanged(@PanelBar.PanelState int state) {
- mAmbientState.setIsShadeOpening(state == STATE_OPENING);
- updateQSExpansionEnabledAmbient();
-
- if (state == STATE_OPEN && mCurrentState != state) {
- mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
- }
- if (state == STATE_OPENING) {
- mStatusBar.makeExpandedVisible(false);
- }
- if (state == STATE_CLOSED) {
- // Close the status bar in the next frame so we can show the end of the
- // animation.
- mView.post(mMaybeHideExpandedRunnable);
- }
- mCurrentState = state;
- }
- };
-
- public PanelBar.PanelStateChangeListener getPanelStateChangeListener() {
- return mPanelStateChangeListener;
+ if (state == STATE_OPEN && mCurrentPanelState != state) {
+ mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ }
+ if (state == STATE_OPENING) {
+ mStatusBar.makeExpandedVisible(false);
+ }
+ if (state == STATE_CLOSED) {
+ // Close the status bar in the next frame so we can show the end of the
+ // animation.
+ mView.post(mMaybeHideExpandedRunnable);
+ }
+ mCurrentPanelState = state;
}
-
/** Returns the handler that the status bar should forward touches to. */
public PhoneStatusBarView.TouchEventHandler getStatusBarTouchEventHandler() {
return getTouchHandler()::onTouchForwardedFromStatusBar;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 36bd31b..01587f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -54,7 +54,9 @@
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.tuner.TunerService;
import java.io.FileDescriptor;
@@ -105,7 +107,8 @@
private boolean mExpandingBelowNotch;
private final DockManager mDockManager;
private final NotificationPanelViewController mNotificationPanelViewController;
- private final StatusBarWindowView mStatusBarWindowView;
+ private final PanelExpansionStateManager mPanelExpansionStateManager;
+ private final StatusBarWindowController mStatusBarWindowController;
// Used for determining view / touch intersection
private int[] mTempLocation = new int[2];
@@ -134,7 +137,8 @@
NotificationShadeDepthController depthController,
NotificationShadeWindowView notificationShadeWindowView,
NotificationPanelViewController notificationPanelViewController,
- StatusBarWindowView statusBarWindowView,
+ PanelExpansionStateManager panelExpansionStateManager,
+ StatusBarWindowController statusBarWindowController,
NotificationStackScrollLayoutController notificationStackScrollLayoutController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
LockIconViewController lockIconViewController) {
@@ -157,8 +161,9 @@
mShadeController = shadeController;
mDockManager = dockManager;
mNotificationPanelViewController = notificationPanelViewController;
+ mPanelExpansionStateManager = panelExpansionStateManager;
mDepthController = depthController;
- mStatusBarWindowView = statusBarWindowView;
+ mStatusBarWindowController = statusBarWindowController;
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mLockIconViewController = lockIconViewController;
@@ -442,7 +447,7 @@
setDragDownHelper(mLockscreenShadeTransitionController.getTouchHelper());
mDepthController.setRoot(mView);
- mNotificationPanelViewController.addExpansionListener(mDepthController);
+ mPanelExpansionStateManager.addExpansionListener(mDepthController);
}
public NotificationShadeWindowView getView() {
@@ -496,7 +501,7 @@
if (statusBarView != null) {
mBarTransitions = new PhoneStatusBarTransitions(
statusBarView,
- mStatusBarWindowView.findViewById(R.id.status_bar_container));
+ mStatusBarWindowController.getBackgroundView());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
deleted file mode 100644
index e90258d..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import static java.lang.Float.isNaN;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.annotation.IntDef;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.widget.FrameLayout;
-
-import androidx.annotation.Nullable;
-
-import java.lang.annotation.Retention;
-
-public abstract class PanelBar extends FrameLayout {
- public static final boolean DEBUG = false;
- public static final String TAG = PanelBar.class.getSimpleName();
- private static final boolean SPEW = false;
- private static final String PANEL_BAR_SUPER_PARCELABLE = "panel_bar_super_parcelable";
- private static final String STATE = "state";
- protected float mPanelFraction;
-
- public static final void LOG(String fmt, Object... args) {
- if (!DEBUG) return;
- Log.v(TAG, String.format(fmt, args));
- }
-
- /** Enum for the current state of the panel. */
- @Retention(SOURCE)
- @IntDef({STATE_CLOSED, STATE_OPENING, STATE_OPEN})
- @interface PanelState {}
- public static final int STATE_CLOSED = 0;
- public static final int STATE_OPENING = 1;
- public static final int STATE_OPEN = 2;
-
- @Nullable private PanelStateChangeListener mPanelStateChangeListener;
- private int mState = STATE_CLOSED;
- private boolean mTracking;
-
- /** Updates the panel state if necessary. */
- public void updateState(@PanelState int state) {
- if (DEBUG) LOG("update state: %d -> %d", mState, state);
- if (mState != state) {
- go(state);
- }
- }
-
- private void go(@PanelState int state) {
- if (DEBUG) LOG("go state: %d -> %d", mState, state);
- mState = state;
- if (mPanelStateChangeListener != null) {
- mPanelStateChangeListener.onStateChanged(state);
- }
- }
-
- @Override
- protected Parcelable onSaveInstanceState() {
- Bundle bundle = new Bundle();
- bundle.putParcelable(PANEL_BAR_SUPER_PARCELABLE, super.onSaveInstanceState());
- bundle.putInt(STATE, mState);
- return bundle;
- }
-
- @Override
- protected void onRestoreInstanceState(Parcelable state) {
- if (state == null || !(state instanceof Bundle)) {
- super.onRestoreInstanceState(state);
- return;
- }
-
- Bundle bundle = (Bundle) state;
- super.onRestoreInstanceState(bundle.getParcelable(PANEL_BAR_SUPER_PARCELABLE));
- if (((Bundle) state).containsKey(STATE)) {
- go(bundle.getInt(STATE, STATE_CLOSED));
- }
- }
-
- public PanelBar(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- }
-
- /** Sets the listener that will be notified of panel state changes. */
- public void setPanelStateChangeListener(PanelStateChangeListener listener) {
- mPanelStateChangeListener = listener;
- }
-
- /**
- * @param frac the fraction from the expansion in [0, 1]
- * @param expanded whether the panel is currently expanded; this is independent from the
- * fraction as the panel also might be expanded if the fraction is 0
- */
- public void panelExpansionChanged(float frac, boolean expanded) {
- if (isNaN(frac)) {
- throw new IllegalArgumentException("frac cannot be NaN");
- }
- boolean fullyClosed = true;
- boolean fullyOpened = false;
- if (SPEW) LOG("panelExpansionChanged: start state=%d, f=%.1f", mState, frac);
- mPanelFraction = frac;
- // adjust any other panels that may be partially visible
- if (expanded) {
- if (mState == STATE_CLOSED) {
- go(STATE_OPENING);
- }
- fullyClosed = false;
- fullyOpened = frac >= 1f;
- }
- if (fullyOpened && !mTracking) {
- go(STATE_OPEN);
- } else if (fullyClosed && !mTracking && mState != STATE_CLOSED) {
- go(STATE_CLOSED);
- }
-
- if (SPEW) LOG("panelExpansionChanged: end state=%d [%s%s ]", mState,
- fullyOpened?" fullyOpened":"", fullyClosed?" fullyClosed":"");
- }
-
- public boolean isClosed() {
- return mState == STATE_CLOSED;
- }
-
- public void onTrackingStarted() {
- mTracking = true;
- }
-
- public void onTrackingStopped(boolean expand) {
- mTracking = false;
- }
-
- /** An interface that will be notified of panel state changes. */
- public interface PanelStateChangeListener {
- /** Called when the state changes. */
- void onStateChanged(@PanelState int state);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 481401b..249f988 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -23,7 +23,7 @@
import android.widget.FrameLayout;
public abstract class PanelView extends FrameLayout {
- public static final boolean DEBUG = PanelBar.DEBUG;
+ public static final boolean DEBUG = false;
public static final String TAG = PanelView.class.getSimpleName();
private PanelViewController.TouchHandler mTouchHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index e5296af..38cf787 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -59,15 +59,15 @@
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.wm.shell.animation.FlingAnimationUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.ArrayList;
public abstract class PanelViewController {
- public static final boolean DEBUG = PanelBar.DEBUG;
+ public static final boolean DEBUG = PanelView.DEBUG;
public static final String TAG = PanelView.class.getSimpleName();
private static final int NO_FIXED_DURATION = -1;
private static final long SHADE_OPEN_SPRING_OUT_DURATION = 350L;
@@ -86,7 +86,6 @@
private boolean mVibrateOnOpening;
protected boolean mIsLaunchAnimationRunning;
private int mFixedDuration = NO_FIXED_DURATION;
- protected ArrayList<PanelExpansionListener> mExpansionListeners = new ArrayList<>();
protected float mOverExpansion;
/**
@@ -154,8 +153,6 @@
private boolean mAnimateAfterExpanding;
private boolean mIsFlinging;
- PanelBar mBar;
-
private String mViewName;
private float mInitialTouchY;
private float mInitialTouchX;
@@ -185,6 +182,7 @@
protected final SysuiStatusBarStateController mStatusBarStateController;
protected final AmbientState mAmbientState;
protected final LockscreenGestureLogger mLockscreenGestureLogger;
+ private final PanelExpansionStateManager mPanelExpansionStateManager;
private final TouchHandler mTouchHandler;
protected abstract void onExpandingFinished();
@@ -211,20 +209,25 @@
return mAmbientState;
}
- public PanelViewController(PanelView view,
- FalsingManager falsingManager, DozeLog dozeLog,
+ public PanelViewController(
+ PanelView view,
+ FalsingManager falsingManager,
+ DozeLog dozeLog,
KeyguardStateController keyguardStateController,
- SysuiStatusBarStateController statusBarStateController, VibratorHelper vibratorHelper,
+ SysuiStatusBarStateController statusBarStateController,
+ VibratorHelper vibratorHelper,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
LatencyTracker latencyTracker,
FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
LockscreenGestureLogger lockscreenGestureLogger,
+ PanelExpansionStateManager panelExpansionStateManager,
AmbientState ambientState) {
mAmbientState = ambientState;
mView = view;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mLockscreenGestureLogger = lockscreenGestureLogger;
+ mPanelExpansionStateManager = panelExpansionStateManager;
mTouchHandler = createTouchHandler();
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
@@ -457,7 +460,6 @@
protected void onTrackingStopped(boolean expand) {
mTracking = false;
- mBar.onTrackingStopped(expand);
mStatusBar.onTrackingStopped(expand);
updatePanelExpansionAndVisibility();
}
@@ -465,7 +467,6 @@
protected void onTrackingStarted() {
endClosing();
mTracking = true;
- mBar.onTrackingStarted();
mStatusBar.onTrackingStarted();
notifyExpandingStarted();
updatePanelExpansionAndVisibility();
@@ -843,10 +844,6 @@
return mTracking;
}
- public void setBar(PanelBar panelBar) {
- mBar = panelBar;
- }
-
public void collapse(boolean delayed, float speedUpFactor) {
if (DEBUG) logf("collapse: " + this);
if (canPanelBeCollapsed()) {
@@ -1084,13 +1081,9 @@
* {@link #updateVisibility()}? That would allow us to make this method private.
*/
public void updatePanelExpansionAndVisibility() {
- if (mBar != null) {
- mBar.panelExpansionChanged(mExpandedFraction, isExpanded());
- }
+ mPanelExpansionStateManager.onPanelExpansionChanged(
+ mExpandedFraction, isExpanded(), mTracking);
updateVisibility();
- for (int i = 0; i < mExpansionListeners.size(); i++) {
- mExpansionListeners.get(i).onPanelExpansionChanged(mExpandedFraction, mTracking);
- }
}
public boolean isExpanded() {
@@ -1102,10 +1095,6 @@
&& !mIsSpringBackAnimation;
}
- public void addExpansionListener(PanelExpansionListener panelExpansionListener) {
- mExpansionListeners.add(panelExpansionListener);
- }
-
protected abstract boolean isPanelVisibleBecauseOfHeadsUp();
/**
@@ -1453,4 +1442,8 @@
protected float getExpansionFraction() {
return mExpandedFraction;
}
+
+ protected PanelExpansionStateManager getPanelExpansionStateManager() {
+ return mPanelExpansionStateManager;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 883313b..0acf2ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -16,23 +16,21 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.Point;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.view.DisplayCutout;
-import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
+import android.widget.FrameLayout;
import android.widget.LinearLayout;
import com.android.internal.policy.SystemBarUtils;
@@ -40,15 +38,13 @@
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.statusbar.window.StatusBarWindowView;
import com.android.systemui.util.leak.RotationUtils;
-import java.util.List;
import java.util.Objects;
-public class PhoneStatusBarView extends PanelBar {
+public class PhoneStatusBarView extends FrameLayout {
private static final String TAG = "PhoneStatusBarView";
- private static final boolean DEBUG = StatusBar.DEBUG;
- private static final boolean DEBUG_GESTURES = false;
private final StatusBarContentInsetsProvider mContentInsetsProvider;
StatusBar mBar;
@@ -65,8 +61,6 @@
private DisplayCutout mDisplayCutout;
private int mStatusBarHeight;
@Nullable
- private List<StatusBar.ExpansionChangedListener> mExpansionChangedListeners;
- @Nullable
private TouchEventHandler mTouchEventHandler;
/**
@@ -83,11 +77,6 @@
mBar = bar;
}
- public void setExpansionChangedListeners(
- @Nullable List<StatusBar.ExpansionChangedListener> listeners) {
- mExpansionChangedListeners = listeners;
- }
-
void setTouchEventHandler(TouchEventHandler handler) {
mTouchEventHandler = handler;
}
@@ -203,16 +192,6 @@
return super.onInterceptTouchEvent(event);
}
- @Override
- public void panelExpansionChanged(float frac, boolean expanded) {
- super.panelExpansionChanged(frac, expanded);
- if (mExpansionChangedListeners != null) {
- for (StatusBar.ExpansionChangedListener listener : mExpansionChangedListeners) {
- listener.onExpansionChanged(frac, expanded);
- }
- }
- }
-
public void updateResources() {
mCutoutSideNudge = getResources().getDimensionPixelSize(
R.dimen.display_cutout_margin_consumption);
@@ -249,17 +228,18 @@
private void updateLayoutForCutout() {
updateStatusBarHeight();
- updateCutoutLocation(StatusBarWindowView.cornerCutoutMargins(mDisplayCutout, getDisplay()));
+ updateCutoutLocation();
updateSafeInsets();
}
- private void updateCutoutLocation(Pair<Integer, Integer> cornerCutoutMargins) {
+ private void updateCutoutLocation() {
// Not all layouts have a cutout (e.g., Car)
if (mCutoutSpace == null) {
return;
}
- if (mDisplayCutout == null || mDisplayCutout.isEmpty() || cornerCutoutMargins != null) {
+ boolean hasCornerCutout = mContentInsetsProvider.currentRotationHasCornerCutout();
+ if (mDisplayCutout == null || mDisplayCutout.isEmpty() || hasCornerCutout) {
mCenterIconSpace.setVisibility(View.VISIBLE);
mCutoutSpace.setVisibility(View.GONE);
return;
@@ -269,8 +249,7 @@
mCutoutSpace.setVisibility(View.VISIBLE);
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mCutoutSpace.getLayoutParams();
- Rect bounds = new Rect();
- boundsFromDirection(mDisplayCutout, Gravity.TOP, bounds);
+ Rect bounds = mDisplayCutout.getBoundingRectTop();
bounds.left = bounds.left + mCutoutSideNudge;
bounds.right = bounds.right - mCutoutSideNudge;
@@ -279,16 +258,13 @@
}
private void updateSafeInsets() {
- Rect contentRect = mContentInsetsProvider
- .getStatusBarContentInsetsForRotation(RotationUtils.getExactRotation(getContext()));
-
- Point size = new Point();
- getDisplay().getRealSize(size);
+ Pair<Integer, Integer> insets = mContentInsetsProvider
+ .getStatusBarContentInsetsForCurrentRotation();
setPadding(
- contentRect.left,
+ insets.first,
getPaddingTop(),
- size.x - contentRect.right,
+ insets.second,
getPaddingBottom());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index de21e73..256b069 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -21,21 +21,21 @@
import android.view.ViewTreeObserver
import com.android.systemui.R
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
-import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.unfold.SysUIUnfoldComponent
import com.android.systemui.unfold.UNFOLD_STATUS_BAR
-import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.util.ViewController
+import com.android.systemui.util.kotlin.getOrNull
+import java.util.Optional
import javax.inject.Inject
import javax.inject.Named
-import dagger.Lazy
/** Controller for [PhoneStatusBarView]. */
class PhoneStatusBarViewController private constructor(
view: PhoneStatusBarView,
@Named(UNFOLD_STATUS_BAR) private val progressProvider: ScopedUnfoldTransitionProgressProvider?,
private val moveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?,
- touchEventHandler: PhoneStatusBarView.TouchEventHandler,
+ touchEventHandler: PhoneStatusBarView.TouchEventHandler
) : ViewController<PhoneStatusBarView>(view) {
override fun onViewAttached() {
@@ -115,21 +115,21 @@
}
class Factory @Inject constructor(
+ private val unfoldComponent: Optional<SysUIUnfoldComponent>,
@Named(UNFOLD_STATUS_BAR)
- private val progressProvider: Lazy<ScopedUnfoldTransitionProgressProvider>,
- private val moveFromCenterController: Lazy<StatusBarMoveFromCenterAnimationController>,
- private val unfoldConfig: UnfoldTransitionConfig,
+ private val progressProvider: Optional<ScopedUnfoldTransitionProgressProvider>
) {
fun create(
view: PhoneStatusBarView,
touchEventHandler: PhoneStatusBarView.TouchEventHandler
- ): PhoneStatusBarViewController {
- return PhoneStatusBarViewController(
+ ) =
+ PhoneStatusBarViewController(
view,
- if (unfoldConfig.isEnabled) progressProvider.get() else null,
- if (unfoldConfig.isEnabled) moveFromCenterController.get() else null,
+ progressProvider.getOrNull(),
+ unfoldComponent.map {
+ it.getStatusBarMoveFromCenterAnimationController()
+ }.getOrNull(),
touchEventHandler
)
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 1921357..1077347 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -53,6 +53,7 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.statusbar.notification.stack.ViewState;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.AlarmTimeout;
@@ -233,7 +234,8 @@
DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler,
KeyguardUpdateMonitor keyguardUpdateMonitor, DockManager dockManager,
ConfigurationController configurationController, @Main Executor mainExecutor,
- UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
+ UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
+ PanelExpansionStateManager panelExpansionStateManager) {
mScrimStateListener = lightBarController::setScrimState;
mDefaultScrimAlpha = BUSY_SCRIM_ALPHA;
@@ -269,6 +271,9 @@
ScrimController.this.onThemeChanged();
}
});
+ panelExpansionStateManager.addExpansionListener(
+ (fraction, expanded, tracking) -> setRawPanelExpansionFraction(fraction)
+ );
mColors = new GradientColors();
}
@@ -481,11 +486,12 @@
*
* The expansion fraction is tied to the scrim opacity.
*
- * See {@link PanelBar#panelExpansionChanged}.
+ * See {@link PanelExpansionListener#onPanelExpansionChanged}.
*
* @param rawPanelExpansionFraction From 0 to 1 where 0 means collapsed and 1 expanded.
*/
- public void setRawPanelExpansionFraction(
+ @VisibleForTesting
+ void setRawPanelExpansionFraction(
@FloatRange(from = 0.0, to = 1.0) float rawPanelExpansionFraction) {
if (isNaN(rawPanelExpansionFraction)) {
throw new IllegalArgumentException("rawPanelExpansionFraction should not be NaN");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
index 8732891..c814622 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone
import android.view.View
+import androidx.constraintlayout.motion.widget.MotionLayout
import com.android.systemui.R
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
@@ -37,6 +38,12 @@
batteryMeterViewController: BatteryMeterViewController
) {
+ companion object {
+ private val HEADER_TRANSITION_ID = R.id.header_transition
+ private val SPLIT_HEADER_TRANSITION_ID = R.id.split_header_transition
+ }
+
+ private val combinedHeaders = featureFlags.useCombinedQSHeaders()
// TODO(b/194178072) Handle RSSI hiding when multi carrier
private val iconManager: StatusBarIconController.IconManager
private val qsCarrierGroupController: QSCarrierGroupController
@@ -56,6 +63,7 @@
}
field = value
updateVisibility()
+ updatePosition()
}
var splitShadeMode = false
@@ -65,6 +73,7 @@
}
field = value
updateVisibility()
+ updateConstraints()
}
var shadeExpandedFraction = -1f
@@ -75,6 +84,28 @@
}
}
+ var qsExpandedFraction = -1f
+ set(value) {
+ if (visible && field != value) {
+ field = value
+ updateVisibility()
+ updatePosition()
+ }
+ }
+
+ init {
+ if (statusBar is MotionLayout) {
+ val context = statusBar.context
+ val resources = statusBar.resources
+ statusBar.getConstraintSet(R.id.qqs_header_constraint)
+ .load(context, resources.getXml(R.xml.qqs_header))
+ statusBar.getConstraintSet(R.id.qs_header_constraint)
+ .load(context, resources.getXml(R.xml.qs_header))
+ statusBar.getConstraintSet(R.id.split_header_constraint)
+ .load(context, resources.getXml(R.xml.split_header))
+ }
+ }
+
init {
batteryMeterViewController.init()
val batteryIcon: BatteryMeterView = statusBar.findViewById(R.id.batteryRemainingIcon)
@@ -88,10 +119,12 @@
qsCarrierGroupController = qsCarrierGroupControllerBuilder
.setQSCarrierGroup(statusBar.findViewById(R.id.carrier_group))
.build()
+ updateVisibility()
+ updateConstraints()
}
private fun updateVisibility() {
- val visibility = if (!splitShadeMode) {
+ val visibility = if (!splitShadeMode && !combinedHeaders) {
View.GONE
} else if (shadeExpanded) {
View.VISIBLE
@@ -104,6 +137,26 @@
}
}
+ private fun updateConstraints() {
+ if (!combinedHeaders) {
+ return
+ }
+ statusBar as MotionLayout
+ if (splitShadeMode) {
+ statusBar.setTransition(SPLIT_HEADER_TRANSITION_ID)
+ } else {
+ statusBar.setTransition(HEADER_TRANSITION_ID)
+ statusBar.transitionToStart()
+ updatePosition()
+ }
+ }
+
+ private fun updatePosition() {
+ if (statusBar is MotionLayout && !splitShadeMode && visible) {
+ statusBar.setProgress(qsExpandedFraction)
+ }
+ }
+
private fun updateListeners() {
qsCarrierGroupController.setListening(visible)
if (visible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index a92f946..28c6b99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -136,7 +136,6 @@
import com.android.systemui.SystemUI;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DelegateLaunchAnimatorController;
-import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.biometrics.AuthRippleController;
@@ -219,6 +218,7 @@
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -229,11 +229,8 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.tuner.TunerService;
-import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
-import com.android.systemui.unfold.UnfoldTransitionWallpaperController;
-import com.android.systemui.unfold.config.UnfoldTransitionConfig;
-import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
import com.android.systemui.util.DumpUtilsKt;
import com.android.systemui.util.WallpaperController;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -248,7 +245,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -338,7 +334,6 @@
}
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
- private boolean mCallingFadingAwayAfterReveal;
private StatusBarCommandQueueCallbacks mCommandQueueCallbacks;
void setWindowState(int state) {
@@ -440,10 +435,6 @@
mCommandQueueCallbacks.animateCollapsePanels(flags, force);
}
- public interface ExpansionChangedListener {
- void onExpansionChanged(float expansion, boolean expanded);
- }
-
/**
* The {@link StatusBarState} of the status bar.
*/
@@ -472,7 +463,7 @@
private AuthRippleController mAuthRippleController;
private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
protected NotificationShadeWindowController mNotificationShadeWindowController;
- protected StatusBarWindowController mStatusBarWindowController;
+ private final StatusBarWindowController mStatusBarWindowController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@VisibleForTesting
DozeServiceHost mDozeServiceHost;
@@ -504,7 +495,6 @@
private final StatusBarNotificationActivityStarter.Builder
mStatusBarNotificationActivityStarterBuilder;
private final ShadeController mShadeController;
- private final StatusBarWindowView mStatusBarWindowView;
private final LightsOutNotifController mLightsOutNotifController;
private final InitController mInitController;
@@ -541,14 +531,12 @@
private final NotificationGutsManager mGutsManager;
private final NotificationLogger mNotificationLogger;
private final NotificationViewHierarchyManager mViewHierarchyManager;
+ private final PanelExpansionStateManager mPanelExpansionStateManager;
private final KeyguardViewMediator mKeyguardViewMediator;
protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final BrightnessSliderController.Factory mBrightnessSliderFactory;
private final FeatureFlags mFeatureFlags;
- private final UnfoldTransitionConfig mUnfoldTransitionConfig;
- private final Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealOverlayAnimation;
- private final Lazy<NaturalRotationUnfoldProgressProvider> mNaturalUnfoldProgressProvider;
- private final Lazy<UnfoldTransitionWallpaperController> mUnfoldWallpaperController;
+
private final WallpaperController mWallpaperController;
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private final MessageRouter mMessageRouter;
@@ -556,8 +544,6 @@
private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private final TunerService mTunerService;
- private final List<ExpansionChangedListener> mExpansionChangedListeners;
-
// Flags for disabling the status bar
// Two variables becaseu the first one evidently ran out of room for new flags.
private int mDisabled1 = 0;
@@ -679,7 +665,6 @@
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private final ActivityLaunchAnimator mActivityLaunchAnimator;
- private final DialogLaunchAnimator mDialogLaunchAnimator;
private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
protected StatusBarNotificationPresenter mPresenter;
private NotificationActivityStarter mNotificationActivityStarter;
@@ -709,6 +694,7 @@
NotificationsController notificationsController,
LightBarController lightBarController,
AutoHideController autoHideController,
+ StatusBarWindowController statusBarWindowController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
PulseExpansionHandler pulseExpansionHandler,
NotificationWakeUpCoordinator notificationWakeUpCoordinator,
@@ -726,6 +712,7 @@
NotificationLogger notificationLogger,
NotificationInterruptStateProvider notificationInterruptStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
+ PanelExpansionStateManager panelExpansionStateManager,
KeyguardViewMediator keyguardViewMediator,
DisplayMetrics displayMetrics,
MetricsLogger metricsLogger,
@@ -767,7 +754,6 @@
StatusBarNotificationActivityStarter.Builder
statusBarNotificationActivityStarterBuilder,
ShadeController shadeController,
- StatusBarWindowView statusBarWindowView,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
ViewMediatorCallback viewMediatorCallback,
InitController initController,
@@ -785,10 +771,6 @@
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
NotificationIconAreaController notificationIconAreaController,
BrightnessSliderController.Factory brightnessSliderFactory,
- UnfoldTransitionConfig unfoldTransitionConfig,
- Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation,
- Lazy<UnfoldTransitionWallpaperController> unfoldTransitionWallpaperController,
- Lazy<NaturalRotationUnfoldProgressProvider> naturalRotationUnfoldProgressProvider,
WallpaperController wallpaperController,
OngoingCallController ongoingCallController,
SystemStatusAnimationScheduler animationScheduler,
@@ -805,12 +787,12 @@
Optional<StartingSurface> startingSurfaceOptional,
TunerService tunerService,
DumpManager dumpManager,
- ActivityLaunchAnimator activityLaunchAnimator,
- DialogLaunchAnimator dialogLaunchAnimator) {
+ ActivityLaunchAnimator activityLaunchAnimator) {
super(context);
mNotificationsController = notificationsController;
mLightBarController = lightBarController;
mAutoHideController = autoHideController;
+ mStatusBarWindowController = statusBarWindowController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mPulseExpansionHandler = pulseExpansionHandler;
mWakeUpCoordinator = notificationWakeUpCoordinator;
@@ -832,6 +814,7 @@
mNotificationLogger = notificationLogger;
mNotificationInterruptStateProvider = notificationInterruptStateProvider;
mViewHierarchyManager = notificationViewHierarchyManager;
+ mPanelExpansionStateManager = panelExpansionStateManager;
mKeyguardViewMediator = keyguardViewMediator;
mDisplayMetrics = displayMetrics;
mMetricsLogger = metricsLogger;
@@ -872,7 +855,6 @@
mSplitScreenOptional = splitScreenOptional;
mStatusBarNotificationActivityStarterBuilder = statusBarNotificationActivityStarterBuilder;
mShadeController = shadeController;
- mStatusBarWindowView = statusBarWindowView;
mLightsOutNotifController = lightsOutNotifController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardViewMediatorCallback = viewMediatorCallback;
@@ -885,10 +867,6 @@
mDemoModeController = demoModeController;
mNotificationIconAreaController = notificationIconAreaController;
mBrightnessSliderFactory = brightnessSliderFactory;
- mUnfoldTransitionConfig = unfoldTransitionConfig;
- mUnfoldLightRevealOverlayAnimation = unfoldLightRevealOverlayAnimation;
- mNaturalUnfoldProgressProvider = naturalRotationUnfoldProgressProvider;
- mUnfoldWallpaperController = unfoldTransitionWallpaperController;
mWallpaperController = wallpaperController;
mOngoingCallController = ongoingCallController;
mAnimationScheduler = animationScheduler;
@@ -907,10 +885,7 @@
mStartingSurfaceOptional = startingSurfaceOptional;
lockscreenShadeTransitionController.setStatusbar(this);
- mExpansionChangedListeners = new ArrayList<>();
- addExpansionChangedListener(
- (expansion, expanded) -> mScrimController.setRawPanelExpansionFraction(expansion));
- addExpansionChangedListener(this::onPanelExpansionChanged);
+ mPanelExpansionStateManager.addExpansionListener(this::onPanelExpansionChanged);
mBubbleExpandListener =
(isExpanding, key) -> mContext.getMainExecutor().execute(() -> {
@@ -920,7 +895,6 @@
mActivityIntentHelper = new ActivityIntentHelper(mContext);
mActivityLaunchAnimator = activityLaunchAnimator;
- mDialogLaunchAnimator = dialogLaunchAnimator;
// The status bar background may need updating when the ongoing call status changes.
mOngoingCallController.addCallback((animate) -> maybeUpdateBarMode());
@@ -1080,12 +1054,6 @@
mFalsingManager.addFalsingBeliefListener(mFalsingBeliefListener);
- if (mUnfoldTransitionConfig.isEnabled()) {
- mUnfoldLightRevealOverlayAnimation.get().init();
- mUnfoldWallpaperController.get().init();
- mNaturalUnfoldProgressProvider.get().init();
- }
-
mPluginManager.addPluginListener(
new PluginListener<OverlayPlugin>() {
private final ArraySet<OverlayPlugin> mOverlays = new ArraySet<>();
@@ -1158,16 +1126,14 @@
mNotificationLogger.setUpWithContainer(notifListContainer);
mNotificationIconAreaController.setupShelf(mNotificationShelfController);
- mNotificationPanelViewController.addExpansionListener(mWakeUpCoordinator);
- mNotificationPanelViewController.addExpansionListener(
- this::dispatchPanelExpansionForKeyguardDismiss);
+ mPanelExpansionStateManager.addExpansionListener(mWakeUpCoordinator);
mUserSwitcherController.init(mNotificationShadeWindowView);
// Allow plugins to reference DarkIconDispatcher and StatusBarStateController
mPluginDependencyProvider.allowPluginDependency(DarkIconDispatcher.class);
mPluginDependencyProvider.allowPluginDependency(StatusBarStateController.class);
- FragmentHostManager.get(mStatusBarWindowView)
+ mStatusBarWindowController.getFragmentHostManager()
.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
CollapsedStatusBarFragment statusBarFragment =
(CollapsedStatusBarFragment) fragment;
@@ -1175,15 +1141,7 @@
PhoneStatusBarView oldStatusBarView = mStatusBarView;
mStatusBarView = (PhoneStatusBarView) statusBarFragment.getView();
mStatusBarView.setBar(this);
- mStatusBarView.setPanelStateChangeListener(
- mNotificationPanelViewController.getPanelStateChangeListener());
mStatusBarView.setScrimController(mScrimController);
- mStatusBarView.setExpansionChangedListeners(mExpansionChangedListeners);
- for (ExpansionChangedListener listener : mExpansionChangedListeners) {
- sendInitialExpansionAmount(listener);
- }
-
- mNotificationPanelViewController.setBar(mStatusBarView);
mPhoneStatusBarViewController = mPhoneStatusBarViewControllerFactory
.create(mStatusBarView, mNotificationPanelViewController
@@ -1236,6 +1194,7 @@
mAnimationScheduler,
mStatusBarLocationPublisher,
mNotificationIconAreaController,
+ mPanelExpansionStateManager,
mFeatureFlags,
mStatusBarIconController,
mKeyguardStateController,
@@ -1410,12 +1369,6 @@
mDeviceProvisionedController.addCallback(mUserSetupObserver);
mUserSetupObserver.onUserSetupChanged();
- for (ExpansionChangedListener listener : mExpansionChangedListeners) {
- // The initial expansion amount comes from mNotificationPanelViewController, so we
- // should send the amount once we've fully set up that controller.
- sendInitialExpansionAmount(listener);
- }
-
// disable profiling bars, since they overlap and clutter the output on app windows
ThreadedRenderer.overrideProperty("disableProfileBars", "true");
@@ -1425,15 +1378,15 @@
/**
- * When swiping up to dismiss the lock screen, the panel expansion goes from 1f to 0f. This
- * results in the clock/notifications/other content disappearing off the top of the screen.
+ * When swiping up to dismiss the lock screen, the panel expansion fraction goes from 1f to 0f.
+ * This results in the clock/notifications/other content disappearing off the top of the screen.
*
- * We also use the expansion amount to animate in the app/launcher surface from the bottom of
+ * We also use the expansion fraction to animate in the app/launcher surface from the bottom of
* the screen, 'pushing' off the notifications and other content. To do this, we dispatch the
- * expansion amount to the KeyguardViewMediator if we're in the process of dismissing the
+ * expansion fraction to the KeyguardViewMediator if we're in the process of dismissing the
* keyguard.
*/
- private void dispatchPanelExpansionForKeyguardDismiss(float expansion, boolean trackingTouch) {
+ private void dispatchPanelExpansionForKeyguardDismiss(float fraction, boolean trackingTouch) {
// Things that mean we're not dismissing the keyguard, and should ignore this expansion:
// - Keyguard isn't even visible.
// - Keyguard is visible, but can't be dismissed (swiping up will show PIN/password prompt).
@@ -1452,12 +1405,14 @@
|| mKeyguardViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe()
|| mKeyguardUnlockAnimationController.isUnlockingWithSmartSpaceTransition()) {
mKeyguardStateController.notifyKeyguardDismissAmountChanged(
- 1f - expansion, trackingTouch);
+ 1f - fraction, trackingTouch);
}
}
- private void onPanelExpansionChanged(float frac, boolean expanded) {
- if (frac == 0 || frac == 1) {
+ private void onPanelExpansionChanged(float fraction, boolean expanded, boolean tracking) {
+ dispatchPanelExpansionForKeyguardDismiss(fraction, tracking);
+
+ if (fraction == 0 || fraction == 1) {
if (getNavigationBarView() != null) {
getNavigationBarView().onStatusBarPanelStateChanged();
}
@@ -1611,7 +1566,6 @@
.getNotificationShadeWindowViewController();
mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowViewController.setupExpandedStatusBar();
- mStatusBarWindowController = statusBarComponent.getStatusBarWindowController();
mNotificationPanelViewController = statusBarComponent.getNotificationPanelViewController();
statusBarComponent.getLockIconViewController().init();
mStackScrollerController = statusBarComponent.getNotificationStackScrollLayoutController();
@@ -1669,8 +1623,11 @@
});
mStatusBarKeyguardViewManager.registerStatusBar(
/* statusBar= */ this,
- mNotificationPanelViewController, mBiometricUnlockController,
- mStackScroller, mKeyguardBypassController);
+ mNotificationPanelViewController,
+ mPanelExpansionStateManager,
+ mBiometricUnlockController,
+ mStackScroller,
+ mKeyguardBypassController);
mKeyguardIndicationController
.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
@@ -1691,10 +1648,6 @@
return mNotificationShadeWindowView;
}
- public StatusBarWindowView getStatusBarWindow() {
- return mStatusBarWindowView;
- }
-
public NotificationShadeWindowViewController getNotificationShadeWindowViewController() {
return mNotificationShadeWindowViewController;
}
@@ -2421,7 +2374,7 @@
pw.print(" mDozing="); pw.println(mDozing);
pw.print(" mWallpaperSupported= "); pw.println(mWallpaperSupported);
- pw.println(" StatusBarWindowView: ");
+ pw.println(" ShadeWindowView: ");
if (mNotificationShadeWindowViewController != null) {
mNotificationShadeWindowViewController.dump(fd, pw, args);
dumpBarTransitions(pw, "PhoneStatusBarTransitions",
@@ -2658,26 +2611,12 @@
private ActivityLaunchAnimator.Controller wrapAnimationController(
ActivityLaunchAnimator.Controller animationController, boolean dismissShade) {
View rootView = animationController.getLaunchContainer().getRootView();
- if (rootView == mStatusBarWindowView) {
- // We are animating a view in the status bar. We have to make sure that the status bar
- // window matches the full screen during the animation and that we are expanding the
- // view below the other status bar text.
- animationController.setLaunchContainer(
- mStatusBarWindowController.getLaunchAnimationContainer());
- return new DelegateLaunchAnimatorController(animationController) {
- @Override
- public void onLaunchAnimationStart(boolean isExpandingFullyAbove) {
- getDelegate().onLaunchAnimationStart(isExpandingFullyAbove);
- mStatusBarWindowController.setLaunchAnimationRunning(true);
- }
-
- @Override
- public void onLaunchAnimationEnd(boolean isExpandingFullyAbove) {
- getDelegate().onLaunchAnimationEnd(isExpandingFullyAbove);
- mStatusBarWindowController.setLaunchAnimationRunning(false);
- }
- };
+ Optional<ActivityLaunchAnimator.Controller> controllerFromStatusBar =
+ mStatusBarWindowController.wrapAnimationControllerIfInStatusBar(
+ rootView, animationController);
+ if (controllerFromStatusBar.isPresent()) {
+ return controllerFromStatusBar.get();
}
if (dismissShade && rootView == mNotificationShadeWindowView) {
@@ -3156,20 +3095,8 @@
public void fadeKeyguardWhilePulsing() {
mNotificationPanelViewController.fadeOut(0, FADE_KEYGUARD_DURATION_PULSING,
()-> {
- Runnable finishFading = () -> {
- mCallingFadingAwayAfterReveal = false;
- hideKeyguard();
- mStatusBarKeyguardViewManager.onKeyguardFadedAway();
- };
- if (mLightRevealScrim.getRevealAmount() != 1.0f) {
- mCallingFadingAwayAfterReveal = true;
- // We're still revealing the Light reveal, let's only go to keyguard once
- // that has finished and nothing moves anymore.
- // Going there introduces lots of jank
- mLightRevealScrim.setFullyRevealedRunnable(finishFading);
- } else {
- finishFading.run();
- }
+ hideKeyguard();
+ mStatusBarKeyguardViewManager.onKeyguardFadedAway();
}).start();
}
@@ -4267,24 +4194,6 @@
return mTransientShown;
}
-
- public void addExpansionChangedListener(@NonNull ExpansionChangedListener listener) {
- mExpansionChangedListeners.add(listener);
- sendInitialExpansionAmount(listener);
- }
-
- private void sendInitialExpansionAmount(ExpansionChangedListener expansionChangedListener) {
- if (mNotificationPanelViewController != null) {
- expansionChangedListener.onExpansionChanged(
- mNotificationPanelViewController.getExpandedFraction(),
- mNotificationPanelViewController.isExpanded());
- }
- }
-
- public void removeExpansionChangedListener(@NonNull ExpansionChangedListener listener) {
- mExpansionChangedListeners.remove(listener);
- }
-
private void updateLightRevealScrimVisibility() {
if (mLightRevealScrim == null) {
// status bar may not be inflated yet
@@ -4334,7 +4243,7 @@
+ "mStatusBarKeyguardViewManager was null");
return;
}
- if (mKeyguardStateController.isKeyguardFadingAway() && !mCallingFadingAwayAfterReveal) {
+ if (mKeyguardStateController.isKeyguardFadingAway()) {
mStatusBarKeyguardViewManager.onKeyguardFadedAway();
}
}
@@ -4496,8 +4405,6 @@
&& !mBiometricUnlockController.isWakeAndUnlock()) {
mLightRevealScrim.setRevealAmount(1f - linear);
}
-
- mDialogLaunchAnimator.onDozeAmountChanged(linear);
}
@Override
@@ -4572,5 +4479,4 @@
return mStartingSurfaceOptional.get().getBackgroundColor(task);
}
};
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index 6ae8331..b7988bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -18,13 +18,14 @@
import android.content.Context
import android.content.res.Resources
+import android.graphics.Point
import android.graphics.Rect
import android.util.LruCache
import android.util.Pair
import android.view.DisplayCutout
-import android.view.View.LAYOUT_DIRECTION_RTL
-import android.view.WindowMetrics
+
import androidx.annotation.VisibleForTesting
+
import com.android.internal.policy.SystemBarUtils
import com.android.systemui.Dumpable
import com.android.systemui.R
@@ -32,16 +33,18 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.leak.RotationUtils
import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE
import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE
import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
import com.android.systemui.util.leak.RotationUtils.Rotation
+import com.android.systemui.util.leak.RotationUtils.getExactRotation
import com.android.systemui.util.leak.RotationUtils.getResourcesForRotation
+
import java.io.FileDescriptor
import java.io.PrintWriter
import java.lang.Math.max
+
import javax.inject.Inject
/**
@@ -112,48 +115,118 @@
}
/**
+ * Some views may need to care about whether or not the current top display cutout is located
+ * in the corner rather than somewhere in the center. In the case of a corner cutout, the
+ * status bar area is contiguous.
+ */
+ fun currentRotationHasCornerCutout(): Boolean {
+ val cutout = context.display.cutout ?: return false
+ val topBounds = cutout.boundingRectTop
+
+ val point = Point()
+ context.display.getRealSize(point)
+
+ return topBounds.left <= 0 || topBounds.right >= point.y
+ }
+
+ /**
* Calculates the maximum bounding rectangle for the privacy chip animation + ongoing privacy
* dot in the coordinates relative to the given rotation.
+ *
+ * @param rotation the rotation for which the bounds are required. This is an absolute value
+ * (i.e., ROTATION_NONE will always return the same bounds regardless of the context
+ * from which this method is called)
*/
fun getBoundingRectForPrivacyChipForRotation(@Rotation rotation: Int): Rect {
var insets = insetsCache[getCacheKey(rotation = rotation)]
- val rotatedResources = getResourcesForRotation(rotation, context)
if (insets == null) {
- insets = getStatusBarContentInsetsForRotation(rotation, rotatedResources)
+ insets = getStatusBarContentAreaForRotation(rotation)
}
+ val rotatedResources = getResourcesForRotation(rotation, context)
+
val dotWidth = rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_dot_diameter)
val chipWidth = rotatedResources.getDimensionPixelSize(
R.dimen.ongoing_appops_chip_max_width)
- val isRtl = context.resources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL
+ val isRtl = configurationController.isLayoutRtl
return getPrivacyChipBoundingRectForInsets(insets, dotWidth, chipWidth, isRtl)
}
/**
- * Calculates the necessary left and right locations for the status bar contents invariant of
- * the current device rotation, in the target rotation's coordinates
+ * Calculate the distance from the left and right edges of the screen to the status bar
+ * content area. This differs from the content area rects in that these values can be used
+ * directly as padding.
+ *
+ * @param rotation the target rotation for which to calculate insets
*/
- @JvmOverloads
- fun getStatusBarContentInsetsForRotation(
- @Rotation rotation: Int,
- rotatedResources: Resources = getResourcesForRotation(rotation, context)
- ): Rect {
- val key = getCacheKey(rotation = rotation)
- return insetsCache[key] ?: getCalculatedInsetsForRotation(rotation, rotatedResources)
- .also {
- insetsCache.put(key, it)
- }
+ fun getStatusBarContentInsetsForRotation(@Rotation rotation: Int): Pair<Int, Int> {
+ val key = getCacheKey(rotation)
+
+ val point = Point()
+ context.display.getRealSize(point)
+ // Target rotation can be a different orientation than the current device rotation
+ point.orientToRotZero(getExactRotation(context))
+ val width = point.logicalWidth(rotation)
+
+ val area = insetsCache[key] ?: getAndSetCalculatedAreaForRotation(
+ rotation, getResourcesForRotation(rotation, context), key)
+
+ return Pair(area.left, width - area.right)
}
- private fun getCalculatedInsetsForRotation(
+ /**
+ * Calculate the left and right insets for the status bar content in the device's current
+ * rotation
+ * @see getStatusBarContentAreaForRotation
+ */
+ fun getStatusBarContentInsetsForCurrentRotation(): Pair<Int, Int> {
+ return getStatusBarContentInsetsForRotation(getExactRotation(context))
+ }
+
+ /**
+ * Calculates the area of the status bar contents invariant of the current device rotation,
+ * in the target rotation's coordinates
+ *
+ * @param rotation the rotation for which the bounds are required. This is an absolute value
+ * (i.e., ROTATION_NONE will always return the same bounds regardless of the context
+ * from which this method is called)
+ */
+ @JvmOverloads
+ fun getStatusBarContentAreaForRotation(
+ @Rotation rotation: Int
+ ): Rect {
+ val key = getCacheKey(rotation)
+ return insetsCache[key] ?: getAndSetCalculatedAreaForRotation(
+ rotation, getResourcesForRotation(rotation, context), key)
+ }
+
+ /**
+ * Get the status bar content area for the given rotation, in absolute bounds
+ */
+ fun getStatusBarContentAreaForCurrentRotation(): Rect {
+ val rotation = getExactRotation(context)
+ return getStatusBarContentAreaForRotation(rotation)
+ }
+
+ private fun getAndSetCalculatedAreaForRotation(
+ @Rotation targetRotation: Int,
+ rotatedResources: Resources,
+ key: CacheKey
+ ): Rect {
+ return getCalculatedAreaForRotation(targetRotation, rotatedResources)
+ .also {
+ insetsCache.put(key, it)
+ }
+ }
+
+ private fun getCalculatedAreaForRotation(
@Rotation targetRotation: Int,
rotatedResources: Resources
): Rect {
val dc = context.display.cutout
- val currentRotation = RotationUtils.getExactRotation(context)
+ val currentRotation = getExactRotation(context)
- val isRtl = rotatedResources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL
val roundedCornerPadding = rotatedResources
.getDimensionPixelSize(R.dimen.rounded_corner_content_padding)
val minDotPadding = if (isPrivacyDotEnabled)
@@ -165,7 +238,7 @@
val minLeft: Int
val minRight: Int
- if (isRtl) {
+ if (configurationController.isLayoutRtl) {
minLeft = max(minDotPadding, roundedCornerPadding)
minRight = roundedCornerPadding
} else {
@@ -178,10 +251,10 @@
targetRotation,
dc,
context.resources.configuration.windowConfiguration.maxBounds,
- SystemBarUtils.getStatusBarHeight(context),
+ SystemBarUtils.getStatusBarHeightForRotation(context, targetRotation),
minLeft,
minRight,
- isRtl,
+ configurationController.isLayoutRtl,
dotWidth)
}
@@ -252,7 +325,7 @@
* @param currentRotation current device rotation
* @param targetRotation rotation for which to calculate the status bar content rect
* @param displayCutout [DisplayCutout] for the current display. possibly null
- * @param windowMetrics [WindowMetrics] for the current window
+ * @param maxBounds the display bounds in our current rotation
* @param statusBarHeight height of the status bar for the target rotation
* @param minLeft the minimum padding to enforce on the left
* @param minRight the minimum padding to enforce on the right
@@ -464,3 +537,22 @@
private fun Int.isHorizontal(): Boolean {
return this == ROTATION_LANDSCAPE || this == ROTATION_SEASCAPE
}
+
+private fun Point.orientToRotZero(@Rotation rot: Int) {
+ when (rot) {
+ ROTATION_NONE, ROTATION_UPSIDE_DOWN -> return
+ else -> {
+ // swap width and height to zero-orient bounds
+ val yTmp = y
+ y = x
+ x = yTmp
+ }
+ }
+}
+
+private fun Point.logicalWidth(@Rotation rot: Int): Int {
+ return when (rot) {
+ ROTATION_NONE, ROTATION_UPSIDE_DOWN -> x
+ else -> y
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
index ca877af..6eeae7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
@@ -24,6 +24,7 @@
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index cac66a3..58d2881 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -63,6 +63,8 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -266,6 +268,7 @@
@Override
public void registerStatusBar(StatusBar statusBar,
NotificationPanelViewController notificationPanelViewController,
+ PanelExpansionStateManager panelExpansionStateManager,
BiometricUnlockController biometricUnlockController,
View notificationContainer,
KeyguardBypassController bypassController) {
@@ -275,7 +278,9 @@
ViewGroup container = mStatusBar.getBouncerContainer();
mBouncer = mKeyguardBouncerFactory.create(container, mExpansionCallback);
mNotificationPanelViewController = notificationPanelViewController;
- notificationPanelViewController.addExpansionListener(this);
+ if (panelExpansionStateManager != null) {
+ panelExpansionStateManager.addExpansionListener(this);
+ }
mBypassController = bypassController;
mNotificationContainer = notificationContainer;
mKeyguardMessageAreaController = mKeyguardMessageAreaFactory.create(
@@ -323,7 +328,7 @@
}
@Override
- public void onPanelExpansionChanged(float expansion, boolean tracking) {
+ public void onPanelExpansionChanged(float fraction, boolean expanded, boolean tracking) {
// We don't want to translate the bounce when:
// • Keyguard is occluded, because we're in a FLAG_SHOW_WHEN_LOCKED activity and need to
// conserve the original animation.
@@ -336,14 +341,14 @@
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
} else if (mShowing) {
if (!isWakeAndUnlocking() && !mStatusBar.isInLaunchTransition()) {
- mBouncer.setExpansion(expansion);
+ mBouncer.setExpansion(fraction);
}
- if (expansion != KeyguardBouncer.EXPANSION_HIDDEN && tracking
+ if (fraction != KeyguardBouncer.EXPANSION_HIDDEN && tracking
&& !mKeyguardStateController.canDismissLockScreen()
&& !mBouncer.isShowing() && !mBouncer.isAnimatingAway()) {
mBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */);
}
- } else if (mPulsing && expansion == KeyguardBouncer.EXPANSION_VISIBLE) {
+ } else if (mPulsing && fraction == KeyguardBouncer.EXPANSION_VISIBLE) {
// Panel expanded while pulsing but didn't translate the bouncer (because we are
// unlocked.) Let's simply wake-up to dismiss the lock screen.
mStatusBar.wakeUpIfDozing(SystemClock.uptimeMillis(), mStatusBar.getBouncerContainer(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
index 8ef186c..805ddf5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
@@ -17,19 +17,17 @@
import android.view.View
import android.view.WindowManager
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator.ViewCenterProvider
-import com.android.systemui.unfold.UNFOLD_STATUS_BAR
+import com.android.systemui.unfold.SysUIUnfoldScope
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import javax.inject.Inject
-import javax.inject.Named
-@SysUISingleton
+@SysUIUnfoldScope
class StatusBarMoveFromCenterAnimationController @Inject constructor(
- @Named(UNFOLD_STATUS_BAR) private val progressProvider: ScopedUnfoldTransitionProgressProvider,
- private val windowManager: WindowManager,
+ private val progressProvider: ScopedUnfoldTransitionProgressProvider,
+ private val windowManager: WindowManager
) {
private val transitionListener = TransitionListener()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
deleted file mode 100644
index 8465889..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import static android.view.MotionEvent.ACTION_DOWN;
-import static android.view.MotionEvent.ACTION_MOVE;
-import static android.view.MotionEvent.ACTION_UP;
-import static android.view.WindowInsets.Type.systemBars;
-
-import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
-
-import android.content.Context;
-import android.graphics.Insets;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.util.Pair;
-import android.view.Display;
-import android.view.DisplayCutout;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.WindowInsets;
-import android.widget.FrameLayout;
-
-import androidx.annotation.NonNull;
-
-import com.android.systemui.util.leak.RotationUtils;
-
-/**
- * Status bar view.
- */
-public class StatusBarWindowView extends FrameLayout {
-
- public static final String TAG = "PhoneStatusBarWindowView";
- public static final boolean DEBUG = StatusBar.DEBUG;
-
- private int mLeftInset = 0;
- private int mRightInset = 0;
- private int mTopInset = 0;
-
- private float mTouchDownY = 0;
-
- public StatusBarWindowView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
- final Insets insets = windowInsets.getInsetsIgnoringVisibility(systemBars());
- mLeftInset = insets.left;
- mRightInset = insets.right;
- mTopInset = 0;
- DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout();
- if (displayCutout != null) {
- mTopInset = displayCutout.getWaterfallInsets().top;
- }
- applyMargins();
- return windowInsets;
- }
-
- /**
- * This is specifically for pulling down the status bar as a consistent motion in the visual
- * immersive mode. In the visual immersive mode, after the system detects a system gesture
- * motion from the top, we show permanent bars, and then forward the touch events from the
- * focused window to the status bar window. However, since the first relayed event is out of
- * bound of the status bar view, in order for the touch event to be correctly dispatched down,
- * we jot down the position Y of the initial touch down event, offset it to 0 in the y-axis,
- * and calculate the movement based on first touch down position.
- */
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- if (ev.getAction() == ACTION_DOWN && ev.getRawY() > getHeight()) {
- mTouchDownY = ev.getRawY();
- ev.setLocation(ev.getRawX(), mTopInset);
- } else if (ev.getAction() == ACTION_MOVE && mTouchDownY != 0) {
- ev.setLocation(ev.getRawX(), mTopInset + ev.getRawY() - mTouchDownY);
- } else if (ev.getAction() == ACTION_UP) {
- mTouchDownY = 0;
- }
- return super.dispatchTouchEvent(ev);
- }
-
- private void applyMargins() {
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- View child = getChildAt(i);
- if (child.getLayoutParams() instanceof LayoutParams) {
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (lp.rightMargin != mRightInset || lp.leftMargin != mLeftInset
- || lp.topMargin != mTopInset) {
- lp.rightMargin = mRightInset;
- lp.leftMargin = mLeftInset;
- lp.topMargin = mTopInset;
- child.requestLayout();
- }
- }
- }
- }
-
- /**
- * Compute the padding needed for status bar related views, e.g., PhoneStatusBar,
- * QuickStatusBarHeader and KeyguardStatusBarView).
- *
- * @param cutout
- * @param cornerCutoutPadding
- * @param roundedCornerContentPadding
- * @return
- */
- @NonNull
- public static Pair<Integer, Integer> paddingNeededForCutoutAndRoundedCorner(
- DisplayCutout cutout, Pair<Integer, Integer> cornerCutoutPadding,
- int roundedCornerContentPadding) {
- if (cutout == null) {
- return new Pair<>(roundedCornerContentPadding, roundedCornerContentPadding);
- }
-
- // padding needed for corner cutout.
- int leftCornerCutoutPadding = cutout.getSafeInsetLeft();
- int rightCornerCutoutPadding = cutout.getSafeInsetRight();
- if (cornerCutoutPadding != null) {
- leftCornerCutoutPadding = Math.max(leftCornerCutoutPadding, cornerCutoutPadding.first);
- rightCornerCutoutPadding = Math.max(rightCornerCutoutPadding,
- cornerCutoutPadding.second);
- }
-
- return new Pair<>(
- Math.max(leftCornerCutoutPadding, roundedCornerContentPadding),
- Math.max(rightCornerCutoutPadding, roundedCornerContentPadding));
- }
-
-
- /**
- * Compute the corner cutout margins in portrait mode
- */
- public static Pair<Integer, Integer> cornerCutoutMargins(DisplayCutout cutout,
- Display display) {
- return statusBarCornerCutoutMargins(cutout, display, RotationUtils.ROTATION_NONE, 0);
- }
-
- /**
- * Compute the corner cutout margins in the given orientation (exactRotation)
- */
- public static Pair<Integer, Integer> statusBarCornerCutoutMargins(DisplayCutout cutout,
- Display display, int exactRotation, int statusBarHeight) {
- if (cutout == null) {
- return null;
- }
- Point size = new Point();
- display.getRealSize(size);
-
- Rect bounds = new Rect();
- switch (exactRotation) {
- case RotationUtils.ROTATION_LANDSCAPE:
- boundsFromDirection(cutout, Gravity.LEFT, bounds);
- break;
- case RotationUtils.ROTATION_SEASCAPE:
- boundsFromDirection(cutout, Gravity.RIGHT, bounds);
- break;
- case RotationUtils.ROTATION_NONE:
- boundsFromDirection(cutout, Gravity.TOP, bounds);
- break;
- case RotationUtils.ROTATION_UPSIDE_DOWN:
- // we assume the cutout is always on top in portrait mode
- return null;
- }
-
- if (statusBarHeight >= 0 && bounds.top > statusBarHeight) {
- return null;
- }
-
- if (bounds.left <= 0) {
- return new Pair<>(bounds.right, 0);
- }
-
- if (bounds.right >= size.x) {
- return new Pair<>(0, size.x - bounds.left);
- }
-
- return null;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 18aa689..cf4aaba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -22,11 +22,15 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.Configuration;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.TypedValue;
import android.view.ViewGroup;
+import android.view.ViewRootImpl;
import android.view.Window;
import android.view.WindowInsets.Type;
import android.view.WindowManager;
@@ -35,6 +39,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.animation.DialogListener;
+import com.android.systemui.animation.DialogListener.DismissReason;
import com.android.systemui.animation.ListenableDialog;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -48,7 +53,8 @@
* The SystemUIDialog registers a listener for the screen off / close system dialogs broadcast,
* and dismisses itself when it receives the broadcast.
*/
-public class SystemUIDialog extends AlertDialog implements ListenableDialog {
+public class SystemUIDialog extends AlertDialog implements ListenableDialog,
+ ViewRootImpl.ConfigChangedCallback {
// TODO(b/203389579): Remove this once the dialog width on large screens has been agreed on.
private static final String FLAG_TABLET_DIALOG_WIDTH =
"persist.systemui.flag_tablet_dialog_width";
@@ -56,12 +62,22 @@
private final Context mContext;
private final DismissReceiver mDismissReceiver;
private final Set<DialogListener> mDialogListeners = new LinkedHashSet<>();
+ private final Handler mHandler = new Handler();
+
+ private int mLastWidth = Integer.MIN_VALUE;
+ private int mLastHeight = Integer.MIN_VALUE;
+ private int mLastConfigurationWidthDp = -1;
+ private int mLastConfigurationHeightDp = -1;
public SystemUIDialog(Context context) {
this(context, R.style.Theme_SystemUI_Dialog);
}
public SystemUIDialog(Context context, int theme) {
+ this(context, theme, true /* dismissOnDeviceLock */);
+ }
+
+ public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock) {
super(context, theme);
mContext = context;
@@ -70,18 +86,57 @@
attrs.setTitle(getClass().getSimpleName());
getWindow().setAttributes(attrs);
- mDismissReceiver = new DismissReceiver(this);
+ mDismissReceiver = dismissOnDeviceLock ? new DismissReceiver(this) : null;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- // Set the dialog window size.
- getWindow().setLayout(getDialogWidth(), ViewGroup.LayoutParams.WRAP_CONTENT);
+ Configuration config = getContext().getResources().getConfiguration();
+ mLastConfigurationWidthDp = config.screenWidthDp;
+ mLastConfigurationHeightDp = config.screenHeightDp;
+ updateWindowSize();
}
- private int getDialogWidth() {
+ private void updateWindowSize() {
+ // Only the thread that created this dialog can update its window size.
+ if (Looper.myLooper() != mHandler.getLooper()) {
+ mHandler.post(this::updateWindowSize);
+ return;
+ }
+
+ int width = getWidth();
+ int height = getHeight();
+ if (width == mLastWidth && height == mLastHeight) {
+ return;
+ }
+
+ mLastWidth = width;
+ mLastHeight = height;
+ getWindow().setLayout(width, height);
+
+ for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) {
+ listener.onSizeChanged();
+ }
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration configuration) {
+ if (mLastConfigurationWidthDp != configuration.screenWidthDp
+ || mLastConfigurationHeightDp != configuration.screenHeightDp) {
+ mLastConfigurationWidthDp = configuration.screenWidthDp;
+ mLastConfigurationHeightDp = configuration.compatScreenWidthDp;
+
+ updateWindowSize();
+ }
+ }
+
+ /**
+ * Return this dialog width. This method will be invoked when this dialog is created and when
+ * the device configuration changes, and the result will be used to resize this dialog window.
+ */
+ protected int getWidth() {
boolean isOnTablet =
mContext.getResources().getConfiguration().smallestScreenWidthDp >= 600;
if (!isOnTablet) {
@@ -108,16 +163,36 @@
}
}
+ /**
+ * Return this dialog height. This method will be invoked when this dialog is created and when
+ * the device configuration changes, and the result will be used to resize this dialog window.
+ */
+ protected int getHeight() {
+ return ViewGroup.LayoutParams.WRAP_CONTENT;
+ }
+
@Override
protected void onStart() {
super.onStart();
- mDismissReceiver.register();
+
+ if (mDismissReceiver != null) {
+ mDismissReceiver.register();
+ }
+
+ // Listen for configuration changes to resize this dialog window. This is mostly necessary
+ // for foldables that often go from large <=> small screen when folding/unfolding.
+ ViewRootImpl.addConfigCallback(this);
}
@Override
protected void onStop() {
super.onStop();
- mDismissReceiver.unregister();
+
+ if (mDismissReceiver != null) {
+ mDismissReceiver.unregister();
+ }
+
+ ViewRootImpl.removeConfigCallback(this);
}
@Override
@@ -132,10 +207,14 @@
@Override
public void dismiss() {
+ dismiss(DismissReason.UNKNOWN);
+ }
+
+ private void dismiss(DismissReason reason) {
super.dismiss();
for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) {
- listener.onDismiss();
+ listener.onDismiss(reason);
}
}
@@ -251,7 +330,11 @@
@Override
public void onReceive(Context context, Intent intent) {
- mDialog.dismiss();
+ if (mDialog instanceof SystemUIDialog) {
+ ((SystemUIDialog) mDialog).dismiss(DismissReason.DEVICE_LOCKED);
+ } else {
+ mDialog.dismiss();
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIHostDialogProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIHostDialogProvider.kt
index 6a49a6d..4f18f8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIHostDialogProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIHostDialogProvider.kt
@@ -3,6 +3,7 @@
import android.app.Dialog
import android.content.Context
import android.os.Bundle
+import android.view.ViewGroup
import com.android.systemui.animation.HostDialogProvider
/** An implementation of [HostDialogProvider] to be used when animating SysUI dialogs. */
@@ -16,12 +17,18 @@
return SystemUIHostDialog(context, theme, onCreateCallback, dismissOverride)
}
+ /**
+ * This host dialog is a SystemUIDialog so that it's displayed above all SystemUI windows. Note
+ * that it is not automatically dismissed when the device is locked, but only when the hosted
+ * (original) dialog is dismissed. That way, the behavior of the dialog (dismissed when locking
+ * or not) is consistent with when the dialog is shown with or without the dialog animator.
+ */
private class SystemUIHostDialog(
context: Context,
theme: Int,
private val onCreateCallback: () -> Unit,
private val dismissOverride: (() -> Unit) -> Unit
- ) : SystemUIDialog(context, theme) {
+ ) : SystemUIDialog(context, theme, false /* dismissOnDeviceLock */) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
onCreateCallback()
@@ -32,5 +39,13 @@
super.dismiss()
}
}
+
+ override fun getWidth(): Int {
+ return ViewGroup.LayoutParams.MATCH_PARENT
+ }
+
+ override fun getHeight(): Int {
+ return ViewGroup.LayoutParams.MATCH_PARENT
+ }
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
index 418f588..e06605e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
@@ -29,7 +29,6 @@
import com.android.systemui.statusbar.phone.StatusBarCommandQueueCallbacks;
import com.android.systemui.statusbar.phone.StatusBarDemoMode;
import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener;
-import com.android.systemui.statusbar.phone.StatusBarWindowController;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
@@ -82,12 +81,6 @@
NotificationShadeWindowViewController getNotificationShadeWindowViewController();
/**
- * Creates a StatusBarWindowViewController.
- */
- @StatusBarScope
- StatusBarWindowController getStatusBarWindowController();
-
- /**
* Creates a NotificationPanelViewController.
*/
@StatusBarScope
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 959c673..5a3cb6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -29,7 +29,6 @@
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.InitController;
import com.android.systemui.animation.ActivityLaunchAnimator;
-import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
@@ -96,12 +95,11 @@
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
-import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
-import com.android.systemui.statusbar.phone.StatusBarWindowView;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -109,11 +107,8 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.tuner.TunerService;
-import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
-import com.android.systemui.unfold.UnfoldTransitionWallpaperController;
-import com.android.systemui.unfold.config.UnfoldTransitionConfig;
-import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
import com.android.systemui.util.WallpaperController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.MessageRouter;
@@ -147,6 +142,7 @@
NotificationsController notificationsController,
LightBarController lightBarController,
AutoHideController autoHideController,
+ StatusBarWindowController statusBarWindowController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
PulseExpansionHandler pulseExpansionHandler,
NotificationWakeUpCoordinator notificationWakeUpCoordinator,
@@ -164,6 +160,7 @@
NotificationLogger notificationLogger,
NotificationInterruptStateProvider notificationInterruptStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
+ PanelExpansionStateManager panelExpansionStateManager,
KeyguardViewMediator keyguardViewMediator,
DisplayMetrics displayMetrics,
MetricsLogger metricsLogger,
@@ -205,7 +202,6 @@
StatusBarNotificationActivityStarter.Builder
statusBarNotificationActivityStarterBuilder,
ShadeController shadeController,
- StatusBarWindowView statusBarWindowView,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
ViewMediatorCallback viewMediatorCallback,
InitController initController,
@@ -223,11 +219,6 @@
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
NotificationIconAreaController notificationIconAreaController,
BrightnessSliderController.Factory brightnessSliderFactory,
- UnfoldTransitionConfig unfoldTransitionConfig,
- Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation,
- Lazy<NaturalRotationUnfoldProgressProvider> naturalRotationUnfoldProgressProvider,
- Lazy<UnfoldTransitionWallpaperController> unfoldTransitionWallpaperController,
- Lazy<StatusBarMoveFromCenterAnimationController> statusBarMoveFromCenterAnimation,
WallpaperController wallpaperController,
OngoingCallController ongoingCallController,
SystemStatusAnimationScheduler animationScheduler,
@@ -244,13 +235,13 @@
Optional<StartingSurface> startingSurfaceOptional,
TunerService tunerService,
DumpManager dumpManager,
- ActivityLaunchAnimator activityLaunchAnimator,
- DialogLaunchAnimator dialogLaunchAnimator) {
+ ActivityLaunchAnimator activityLaunchAnimator) {
return new StatusBar(
context,
notificationsController,
lightBarController,
autoHideController,
+ statusBarWindowController,
keyguardUpdateMonitor,
pulseExpansionHandler,
notificationWakeUpCoordinator,
@@ -268,6 +259,7 @@
notificationLogger,
notificationInterruptStateProvider,
notificationViewHierarchyManager,
+ panelExpansionStateManager,
keyguardViewMediator,
displayMetrics,
metricsLogger,
@@ -308,7 +300,6 @@
lightsOutNotifController,
statusBarNotificationActivityStarterBuilder,
shadeController,
- statusBarWindowView,
statusBarKeyguardViewManager,
viewMediatorCallback,
initController,
@@ -326,10 +317,6 @@
statusBarTouchableRegionManager,
notificationIconAreaController,
brightnessSliderFactory,
- unfoldTransitionConfig,
- unfoldLightRevealOverlayAnimation,
- unfoldTransitionWallpaperController,
- naturalRotationUnfoldProgressProvider,
wallpaperController,
ongoingCallController,
animationScheduler,
@@ -346,7 +333,7 @@
startingSurfaceOptional,
tunerService,
dumpManager,
- activityLaunchAnimator,
- dialogLaunchAnimator);
+ activityLaunchAnimator
+ );
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 9de0c46..2765fe3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -19,11 +19,13 @@
import android.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewStub;
import com.android.keyguard.LockIconView;
import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.biometrics.AuthRippleView;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
@@ -126,9 +128,16 @@
@Provides
@Named(SPLIT_SHADE_HEADER)
@StatusBarComponent.StatusBarScope
- public static View getSlitShadeStatusBarView(
- NotificationShadeWindowView notificationShadeWindowView) {
- return notificationShadeWindowView.findViewById(R.id.split_shade_status_bar);
+ public static View getSplitShadeStatusBarView(
+ NotificationShadeWindowView notificationShadeWindowView,
+ FeatureFlags featureFlags) {
+ ViewStub stub = notificationShadeWindowView.findViewById(R.id.qs_header_stub);
+ int layoutId = featureFlags.useCombinedQSHeaders()
+ ? R.layout.combined_qs_header
+ : R.layout.split_shade_header;
+ stub.setLayoutResource(layoutId);
+ View v = stub.inflate();
+ return v;
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 31cc823..1225813 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -39,8 +39,8 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
-import com.android.systemui.statusbar.phone.StatusBarWindowController
import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.time.SystemClock
import java.io.FileDescriptor
import java.io.PrintWriter
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelExpansionListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionListener.java
similarity index 65%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelExpansionListener.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionListener.java
index 655a25d..b9f806d20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelExpansionListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionListener.java
@@ -11,28 +11,21 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
-package com.android.systemui.statusbar.phone;
+package com.android.systemui.statusbar.phone.panelstate;
-/**
- * Panel and QS expansion callbacks.
- */
+/** A listener interface to be notified of expansion events for the notification panel. */
public interface PanelExpansionListener {
/**
* Invoked whenever the notification panel expansion changes, at every animation frame.
* This is the main expansion that happens when the user is swiping up to dismiss the
- * lock screen.
+ * lock screen and swiping to pull down the notification shade.
*
- * @param expansion 0 when collapsed, 1 when expanded.
+ * @param fraction 0 when collapsed, 1 when fully expanded.
+ * @param expanded true if the panel should be considered expanded.
* @param tracking {@code true} when the user is actively dragging the panel.
*/
- void onPanelExpansionChanged(float expansion, boolean tracking);
-
- /**
- * Invoked whenever the QS expansion changes, at every animation frame.
- * @param expansion 0 when collapsed, 1 when expanded.
- */
- default void onQsExpansionChanged(float expansion) {};
+ void onPanelExpansionChanged(float fraction, boolean expanded, boolean tracking);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManager.kt
new file mode 100644
index 0000000..2c7c8e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManager.kt
@@ -0,0 +1,160 @@
+/*
+ * 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.systemui.statusbar.phone.panelstate
+
+import android.annotation.IntDef
+import android.util.Log
+import androidx.annotation.FloatRange
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * A class responsible for managing the notification panel's current state.
+ *
+ * TODO(b/200063118): Make this class the one source of truth for the state of panel expansion.
+ */
+@SysUISingleton
+class PanelExpansionStateManager @Inject constructor() {
+
+ private val expansionListeners = mutableListOf<PanelExpansionListener>()
+ private val stateListeners = mutableListOf<PanelStateListener>()
+
+ @PanelState private var state: Int = STATE_CLOSED
+ @FloatRange(from = 0.0, to = 1.0) private var fraction: Float = 0f
+ private var expanded: Boolean = false
+ private var tracking: Boolean = false
+
+ /**
+ * Adds a listener that will be notified when the panel expansion fraction has changed.
+ *
+ * Listener will also be immediately notified with the current values.
+ */
+ fun addExpansionListener(listener: PanelExpansionListener) {
+ expansionListeners.add(listener)
+ listener.onPanelExpansionChanged(fraction, expanded, tracking)
+ }
+
+ /** Removes an expansion listener. */
+ fun removeExpansionListener(listener: PanelExpansionListener) {
+ expansionListeners.remove(listener)
+ }
+
+ /** Adds a listener that will be notified when the panel state has changed. */
+ fun addStateListener(listener: PanelStateListener) {
+ stateListeners.add(listener)
+ }
+
+ /** Removes a state listener. */
+ fun removeStateListener(listener: PanelStateListener) {
+ stateListeners.remove(listener)
+ }
+
+ /** Returns true if the panel is currently closed and false otherwise. */
+ fun isClosed(): Boolean = state == STATE_CLOSED
+
+ /**
+ * Called when the panel expansion has changed.
+ *
+ * @param fraction the fraction from the expansion in [0, 1]
+ * @param expanded whether the panel is currently expanded; this is independent from the
+ * fraction as the panel also might be expanded if the fraction is 0.
+ * @param tracking whether we're currently tracking the user's gesture.
+ */
+ fun onPanelExpansionChanged(
+ @FloatRange(from = 0.0, to = 1.0) fraction: Float,
+ expanded: Boolean,
+ tracking: Boolean
+ ) {
+ require(!fraction.isNaN()) { "fraction cannot be NaN" }
+ val oldState = state
+
+ this.fraction = fraction
+ this.expanded = expanded
+ this.tracking = tracking
+
+ var fullyClosed = true
+ var fullyOpened = false
+
+ if (expanded) {
+ if (this.state == STATE_CLOSED) {
+ updateStateInternal(STATE_OPENING)
+ }
+ fullyClosed = false
+ fullyOpened = fraction >= 1f
+ }
+
+ if (fullyOpened && !tracking) {
+ updateStateInternal(STATE_OPEN)
+ } else if (fullyClosed && !tracking && this.state != STATE_CLOSED) {
+ updateStateInternal(STATE_CLOSED)
+ }
+
+ debugLog(
+ "panelExpansionChanged:" +
+ "start state=${oldState.stateToString()} " +
+ "end state=${state.stateToString()} " +
+ "f=$fraction " +
+ "expanded=$expanded " +
+ "tracking=$tracking" +
+ "${if (fullyOpened) " fullyOpened" else ""} " +
+ if (fullyClosed) " fullyClosed" else ""
+ )
+
+ expansionListeners.forEach { it.onPanelExpansionChanged(fraction, expanded, tracking) }
+ }
+
+ /** Updates the panel state if necessary. */
+ fun updateState(@PanelState state: Int) {
+ debugLog("update state: ${this.state.stateToString()} -> ${state.stateToString()}")
+ if (this.state != state) {
+ updateStateInternal(state)
+ }
+ }
+
+ private fun updateStateInternal(@PanelState state: Int) {
+ debugLog("go state: ${this.state.stateToString()} -> ${state.stateToString()}")
+ this.state = state
+ stateListeners.forEach { it.onPanelStateChanged(state) }
+ }
+
+ private fun debugLog(msg: String) {
+ if (!DEBUG) return
+ Log.v(TAG, msg)
+ }
+}
+
+/** Enum for the current state of the panel. */
+@Retention(AnnotationRetention.SOURCE)
+@IntDef(value = [STATE_CLOSED, STATE_OPENING, STATE_OPEN])
+internal annotation class PanelState
+
+const val STATE_CLOSED = 0
+const val STATE_OPENING = 1
+const val STATE_OPEN = 2
+
+@PanelState
+private fun Int.stateToString(): String {
+ return when (this) {
+ STATE_CLOSED -> "CLOSED"
+ STATE_OPENING -> "OPENING"
+ STATE_OPEN -> "OPEN"
+ else -> this.toString()
+ }
+}
+
+private const val DEBUG = false
+private val TAG = PanelExpansionStateManager::class.simpleName
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelStateListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelStateListener.kt
new file mode 100644
index 0000000..e299592
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelStateListener.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.systemui.statusbar.phone.panelstate
+
+/** A listener interface to be notified of state change events for the notification panel. */
+interface PanelStateListener {
+ /** Called when the panel's expansion state has changed. */
+ fun onPanelStateChanged(@PanelState state: Int)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index e0b0dd36..9587261 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -26,7 +26,6 @@
import android.database.ContentObserver;
import android.provider.Settings;
import android.util.ArrayMap;
-import android.util.Log;
import android.view.accessibility.AccessibilityManager;
import com.android.internal.logging.MetricsLogger;
@@ -81,7 +80,8 @@
}
}
- public HeadsUpManager(@NonNull final Context context) {
+ public HeadsUpManager(@NonNull final Context context, HeadsUpManagerLogger logger) {
+ super(logger);
mContext = context;
mAccessibilityMgr = Dependency.get(AccessibilityManagerWrapper.class);
mUiEventLogger = Dependency.get(UiEventLogger.class);
@@ -102,9 +102,7 @@
context.getContentResolver(), SETTING_HEADS_UP_SNOOZE_LENGTH_MS, -1);
if (packageSnoozeLengthMs > -1 && packageSnoozeLengthMs != mSnoozeLengthMs) {
mSnoozeLengthMs = packageSnoozeLengthMs;
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs);
- }
+ mLogger.logSnoozeLengthChange(packageSnoozeLengthMs);
}
}
};
@@ -145,9 +143,7 @@
protected void setEntryPinned(
@NonNull HeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "setEntryPinned: " + isPinned);
- }
+ mLogger.logSetEntryPinned(headsUpEntry.mEntry.getKey(), isPinned);
NotificationEntry entry = headsUpEntry.mEntry;
if (entry.isRowPinned() != isPinned) {
entry.setRowPinned(isPinned);
@@ -198,10 +194,7 @@
if (hasPinnedNotification == mHasPinnedNotification) {
return;
}
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "Pinned mode changed: " + mHasPinnedNotification + " -> " +
- hasPinnedNotification);
- }
+ mLogger.logUpdatePinnedMode(hasPinnedNotification);
mHasPinnedNotification = hasPinnedNotification;
if (mHasPinnedNotification) {
MetricsLogger.count(mContext, "note_peek", 1);
@@ -219,12 +212,11 @@
Long snoozedUntil = mSnoozedPackages.get(key);
if (snoozedUntil != null) {
if (snoozedUntil > mClock.currentTimeMillis()) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, key + " snoozed");
- }
+ mLogger.logIsSnoozedReturned(key);
return true;
}
- mSnoozedPackages.remove(packageName);
+ mLogger.logPackageUnsnoozed(key);
+ mSnoozedPackages.remove(key);
}
return false;
}
@@ -236,8 +228,9 @@
for (String key : mAlertEntries.keySet()) {
AlertEntry entry = getHeadsUpEntry(key);
String packageName = entry.mEntry.getSbn().getPackageName();
- mSnoozedPackages.put(snoozeKey(packageName, mUser),
- mClock.currentTimeMillis() + mSnoozeLengthMs);
+ String snoozeKey = snoozeKey(packageName, mUser);
+ mLogger.logPackageSnoozed(snoozeKey);
+ mSnoozedPackages.put(snoozeKey, mClock.currentTimeMillis() + mSnoozeLengthMs);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
new file mode 100644
index 0000000..2bdf62bb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.LogLevel.VERBOSE
+import com.android.systemui.log.dagger.NotificationHeadsUpLog
+import javax.inject.Inject
+
+/** Logger for [HeadsUpManager]. */
+class HeadsUpManagerLogger @Inject constructor(
+ @NotificationHeadsUpLog private val buffer: LogBuffer
+) {
+ fun logPackageSnoozed(snoozeKey: String) {
+ buffer.log(TAG, INFO, {
+ str1 = snoozeKey
+ }, {
+ "package snoozed $str1"
+ })
+ }
+
+ fun logPackageUnsnoozed(snoozeKey: String) {
+ buffer.log(TAG, INFO, {
+ str1 = snoozeKey
+ }, {
+ "package unsnoozed $str1"
+ })
+ }
+
+ fun logIsSnoozedReturned(snoozeKey: String) {
+ buffer.log(TAG, INFO, {
+ str1 = snoozeKey
+ }, {
+ "package snoozed when queried $str1"
+ })
+ }
+
+ fun logReleaseAllImmediately() {
+ buffer.log(TAG, INFO, { }, {
+ "release all immediately"
+ })
+ }
+
+ fun logShowNotification(key: String) {
+ buffer.log(TAG, INFO, {
+ str1 = key
+ }, {
+ "show notification $str1"
+ })
+ }
+
+ fun logRemoveNotification(key: String, releaseImmediately: Boolean) {
+ buffer.log(TAG, INFO, {
+ str1 = key
+ bool1 = releaseImmediately
+ }, {
+ "remove notification $str1 releaseImmediately: $bool1"
+ })
+ }
+
+ fun logUpdateNotification(key: String, alert: Boolean, hasEntry: Boolean) {
+ buffer.log(TAG, INFO, {
+ str1 = key
+ bool1 = alert
+ bool2 = hasEntry
+ }, {
+ "update notification $str1 alert: $bool1 hasEntry: $bool2"
+ })
+ }
+
+ fun logUpdateEntry(updatePostTime: Boolean) {
+ buffer.log(TAG, INFO, {
+ bool1 = updatePostTime
+ }, {
+ "update entry updatePostTime: $bool1"
+ })
+ }
+
+ fun logSnoozeLengthChange(packageSnoozeLengthMs: Int) {
+ buffer.log(TAG, INFO, {
+ int1 = packageSnoozeLengthMs
+ }, {
+ "snooze length changed: ${int1}ms"
+ })
+ }
+
+ fun logSetEntryPinned(key: String, isPinned: Boolean) {
+ buffer.log(TAG, VERBOSE, {
+ str1 = key
+ bool1 = isPinned
+ }, {
+ "set entry pinned $str1 pinned: $bool1"
+ })
+ }
+
+ fun logUpdatePinnedMode(hasPinnedNotification: Boolean) {
+ buffer.log(TAG, INFO, {
+ bool1 = hasPinnedNotification
+ }, {
+ "has pinned notification changed to $bool1"
+ })
+ }
+}
+
+private const val TAG = "HeadsUpManager"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index e39e8ba..7e3f5fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -138,6 +138,10 @@
private boolean mRemoved;
private NotificationViewWrapper mWrapper;
+ // TODO(b/193539698): remove this; views shouldn't have access to their controller, and places
+ // that need the controller shouldn't have access to the view
+ private RemoteInputViewController mViewController;
+
/**
* Enum for logged notification remote input UiEvents.
*/
@@ -283,6 +287,24 @@
});
}
+ /**
+ * @deprecated TODO(b/193539698): views shouldn't have access to their controller, and places
+ * that need the controller shouldn't have access to the view
+ */
+ @Deprecated
+ public void setController(RemoteInputViewController controller) {
+ mViewController = controller;
+ }
+
+ /**
+ * @deprecated TODO(b/193539698): views shouldn't have access to their controller, and places
+ * that need the controller shouldn't have access to the view
+ */
+ @Deprecated
+ public RemoteInputViewController getController() {
+ return mViewController;
+ }
+
@VisibleForTesting
protected void setAttachment(ContentInfo item) {
if (mEntry.remoteInputAttachment != null && mEntry.remoteInputAttachment != item) {
@@ -298,6 +320,7 @@
mEntry.remoteInputUri = item.getClip().getItemAt(0).getUri();
mEntry.remoteInputMimeType = item.getClip().getDescription().getMimeType(0);
}
+
View attachment = findViewById(R.id.remote_input_content_container);
ImageView iconView = findViewById(R.id.remote_input_attachment_image);
iconView.setImageDrawable(null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
new file mode 100644
index 0000000..f948030
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.systemui.statusbar.policy
+
+import com.android.systemui.statusbar.policy.dagger.RemoteInputViewScope
+import javax.inject.Inject
+
+interface RemoteInputViewController
+
+@RemoteInputViewScope
+class RemoteInputViewControllerImpl @Inject constructor() : RemoteInputViewController
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/RemoteInput.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/RemoteInput.kt
new file mode 100644
index 0000000..fe56a98
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/RemoteInput.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.systemui.statusbar.policy.dagger
+
+import com.android.systemui.statusbar.policy.RemoteInputViewController
+import com.android.systemui.statusbar.policy.RemoteInputViewControllerImpl
+import dagger.Binds
+import dagger.Module
+import dagger.Subcomponent
+import javax.inject.Qualifier
+
+@Subcomponent(modules = [InternalRemoteInputViewModule::class])
+@RemoteInputViewScope
+interface RemoteInputViewSubcomponent {
+ val controller: RemoteInputViewController
+
+ @Subcomponent.Factory
+ interface Factory {
+ fun create(): RemoteInputViewSubcomponent
+ }
+}
+
+@Module
+private interface InternalRemoteInputViewModule {
+ @Binds
+ fun bindController(impl: RemoteInputViewControllerImpl): RemoteInputViewController
+}
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+internal annotation class RemoteInputViewScope
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
similarity index 73%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
index 9d2dbc1..700b58a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
@@ -14,10 +14,8 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.phone;
+package com.android.systemui.statusbar.window;
-import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
-import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
@@ -37,13 +35,20 @@
import android.view.Gravity;
import android.view.IWindowManager;
import android.view.Surface;
+import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.R;
+import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.DelegateLaunchAnimatorController;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
+
+import java.util.Optional;
import javax.inject.Inject;
@@ -63,7 +68,9 @@
private int mBarHeight = -1;
private final State mCurrentState = new State();
- private final ViewGroup mStatusBarView;
+ private final ViewGroup mStatusBarWindowView;
+ // The container in which we should run launch animations started from the status bar and
+ // expanding into the opening window.
private final ViewGroup mLaunchAnimationContainer;
private WindowManager.LayoutParams mLp;
private final WindowManager.LayoutParams mLpChanged;
@@ -71,17 +78,17 @@
@Inject
public StatusBarWindowController(
Context context,
+ @StatusBarWindowModule.InternalWindowView StatusBarWindowView statusBarWindowView,
WindowManager windowManager,
IWindowManager iWindowManager,
- StatusBarWindowView statusBarWindowView,
StatusBarContentInsetsProvider contentInsetsProvider,
@Main Resources resources) {
mContext = context;
mWindowManager = windowManager;
mIWindowManager = iWindowManager;
mContentInsetsProvider = contentInsetsProvider;
- mStatusBarView = statusBarWindowView;
- mLaunchAnimationContainer = mStatusBarView.findViewById(
+ mStatusBarWindowView = statusBarWindowView;
+ mLaunchAnimationContainer = mStatusBarWindowView.findViewById(
R.id.status_bar_launch_animation_container);
mLpChanged = new WindowManager.LayoutParams();
mResources = resources;
@@ -119,13 +126,63 @@
// hardware-accelerated.
mLp = getBarLayoutParams(mContext.getDisplay().getRotation());
- mWindowManager.addView(mStatusBarView, mLp);
+ mWindowManager.addView(mStatusBarWindowView, mLp);
mLpChanged.copyFrom(mLp);
mContentInsetsProvider.addCallback(this::calculateStatusBarLocationsForAllRotations);
calculateStatusBarLocationsForAllRotations();
}
+ /** Adds the given view to the status bar window view. */
+ public void addViewToWindow(View view, ViewGroup.LayoutParams layoutParams) {
+ mStatusBarWindowView.addView(view, layoutParams);
+ }
+
+ /** Returns the status bar window's background view. */
+ public View getBackgroundView() {
+ return mStatusBarWindowView.findViewById(R.id.status_bar_container);
+ }
+
+ /** Returns a fragment host manager for the status bar window view. */
+ public FragmentHostManager getFragmentHostManager() {
+ return FragmentHostManager.get(mStatusBarWindowView);
+ }
+
+ /**
+ * Provides an updated animation controller if we're animating a view in the status bar.
+ *
+ * This is needed because we have to make sure that the status bar window matches the full
+ * screen during the animation and that we are expanding the view below the other status bar
+ * text.
+ *
+ * @param rootView the root view of the animation
+ * @param animationController the default animation controller to use
+ * @return If the animation is on a view in the status bar, returns an Optional containing an
+ * updated animation controller that handles status-bar-related animation details. Returns an
+ * empty optional if the animation is *not* on a view in the status bar.
+ */
+ public Optional<ActivityLaunchAnimator.Controller> wrapAnimationControllerIfInStatusBar(
+ View rootView, ActivityLaunchAnimator.Controller animationController) {
+ if (rootView != mStatusBarWindowView) {
+ return Optional.empty();
+ }
+
+ animationController.setLaunchContainer(mLaunchAnimationContainer);
+ return Optional.of(new DelegateLaunchAnimatorController(animationController) {
+ @Override
+ public void onLaunchAnimationStart(boolean isExpandingFullyAbove) {
+ getDelegate().onLaunchAnimationStart(isExpandingFullyAbove);
+ setLaunchAnimationRunning(true);
+ }
+
+ @Override
+ public void onLaunchAnimationEnd(boolean isExpandingFullyAbove) {
+ getDelegate().onLaunchAnimationEnd(isExpandingFullyAbove);
+ setLaunchAnimationRunning(false);
+ }
+ });
+ }
+
private WindowManager.LayoutParams getBarLayoutParams(int rotation) {
WindowManager.LayoutParams lp = getBarLayoutParamsForRotation(rotation);
lp.paramsForRotation = new WindowManager.LayoutParams[4];
@@ -136,25 +193,7 @@
}
private WindowManager.LayoutParams getBarLayoutParamsForRotation(int rotation) {
- int height = mBarHeight;
- if (INSETS_LAYOUT_GENERALIZATION) {
- switch (rotation) {
- case ROTATION_UNDEFINED:
- case Surface.ROTATION_0:
- case Surface.ROTATION_180:
- height = SystemBarUtils.getStatusBarHeightForRotation(
- mContext, Surface.ROTATION_0);
- break;
- case Surface.ROTATION_90:
- height = SystemBarUtils.getStatusBarHeightForRotation(
- mContext, Surface.ROTATION_90);
- break;
- case Surface.ROTATION_270:
- height = SystemBarUtils.getStatusBarHeightForRotation(
- mContext, Surface.ROTATION_270);
- break;
- }
- }
+ int height = SystemBarUtils.getStatusBarHeightForRotation(mContext, rotation);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
height,
@@ -214,21 +253,11 @@
}
/**
- * Return the container in which we should run launch animations started from the status bar and
- * expanding into the opening window.
- *
- * @see #setLaunchAnimationRunning
- */
- public ViewGroup getLaunchAnimationContainer() {
- return mLaunchAnimationContainer;
- }
-
- /**
* Set whether a launch animation is currently running. If true, this will ensure that the
* window matches its parent height so that the animation is not clipped by the normal status
* bar height.
*/
- public void setLaunchAnimationRunning(boolean isLaunchAnimationRunning) {
+ private void setLaunchAnimationRunning(boolean isLaunchAnimationRunning) {
if (isLaunchAnimationRunning == mCurrentState.mIsLaunchAnimationRunning) {
return;
}
@@ -246,7 +275,7 @@
applyForceStatusBarVisibleFlag(state);
applyHeight(state);
if (mLp != null && mLp.copyFrom(mLpChanged) != 0) {
- mWindowManager.updateViewLayout(mStatusBarView, mLp);
+ mWindowManager.updateViewLayout(mStatusBarWindowView, mLp);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowModule.kt
new file mode 100644
index 0000000..874217a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowModule.kt
@@ -0,0 +1,47 @@
+package com.android.systemui.statusbar.window
+
+import android.view.LayoutInflater
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Module
+import dagger.Provides
+import javax.inject.Qualifier
+
+/** Module providing dependencies related to the status bar window. */
+@Module
+abstract class StatusBarWindowModule {
+ /**
+ * Provides a [StatusBarWindowView].
+ *
+ * Only [StatusBarWindowController] should inject the view.
+ */
+ @Module
+ companion object {
+ @JvmStatic
+ @Provides
+ @SysUISingleton
+ @InternalWindowView
+ fun providesStatusBarWindowView(layoutInflater: LayoutInflater): StatusBarWindowView {
+ return layoutInflater.inflate(
+ R.layout.super_status_bar,
+ /* root= */null
+ ) as StatusBarWindowView?
+ ?: throw IllegalStateException(
+ "R.layout.super_status_bar could not be properly inflated"
+ )
+ }
+ }
+
+ /**
+ * We want [StatusBarWindowView] to be provided to [StatusBarWindowController]'s constructor via
+ * dagger so that we can provide a fake window view when testing the controller. However, we wan
+ * want *only* the controller to be able to inject the window view.
+ *
+ * This protected qualifier annotation achieves this. [StatusBarWindowView] can only be injected
+ * if it's annotated with [InternalWindowView], and only classes inside this [statusbar.window]
+ * package can access the annotation.
+ */
+ @Retention(AnnotationRetention.BINARY)
+ @Qualifier
+ protected annotation class InternalWindowView
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
new file mode 100644
index 0000000..06cc96e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.window;
+
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_UP;
+import static android.view.WindowInsets.Type.systemBars;
+
+import android.content.Context;
+import android.graphics.Insets;
+import android.util.AttributeSet;
+import android.view.DisplayCutout;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowInsets;
+import android.widget.FrameLayout;
+
+/**
+ * Status bar view.
+ */
+public class StatusBarWindowView extends FrameLayout {
+
+ public static final String TAG = "PhoneStatusBarWindowView";
+ public static final boolean DEBUG = false;
+
+ private int mLeftInset = 0;
+ private int mRightInset = 0;
+ private int mTopInset = 0;
+
+ private float mTouchDownY = 0;
+
+ public StatusBarWindowView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
+ final Insets insets = windowInsets.getInsetsIgnoringVisibility(systemBars());
+ mLeftInset = insets.left;
+ mRightInset = insets.right;
+ mTopInset = 0;
+ DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout();
+ if (displayCutout != null) {
+ mTopInset = displayCutout.getWaterfallInsets().top;
+ }
+ applyMargins();
+ return windowInsets;
+ }
+
+ /**
+ * This is specifically for pulling down the status bar as a consistent motion in the visual
+ * immersive mode. In the visual immersive mode, after the system detects a system gesture
+ * motion from the top, we show permanent bars, and then forward the touch events from the
+ * focused window to the status bar window. However, since the first relayed event is out of
+ * bound of the status bar view, in order for the touch event to be correctly dispatched down,
+ * we jot down the position Y of the initial touch down event, offset it to 0 in the y-axis,
+ * and calculate the movement based on first touch down position.
+ */
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == ACTION_DOWN && ev.getRawY() > getHeight()) {
+ mTouchDownY = ev.getRawY();
+ ev.setLocation(ev.getRawX(), mTopInset);
+ } else if (ev.getAction() == ACTION_MOVE && mTouchDownY != 0) {
+ ev.setLocation(ev.getRawX(), mTopInset + ev.getRawY() - mTouchDownY);
+ } else if (ev.getAction() == ACTION_UP) {
+ mTouchDownY = 0;
+ }
+ return super.dispatchTouchEvent(ev);
+ }
+
+ private void applyMargins() {
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ if (child.getLayoutParams() instanceof LayoutParams) {
+ LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (lp.rightMargin != mRightInset || lp.leftMargin != mLeftInset
+ || lp.topMargin != mTopInset) {
+ lp.rightMargin = mRightInset;
+ lp.leftMargin = mLeftInset;
+ lp.topMargin = mTopInset;
+ child.requestLayout();
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index 923aff1..65518d6 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -65,6 +65,7 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyControllerImpl;
import com.android.systemui.statusbar.policy.SensorPrivacyController;
@@ -162,12 +163,19 @@
@Provides
static HeadsUpManagerPhone provideHeadsUpManagerPhone(
Context context,
+ HeadsUpManagerLogger headsUpManagerLogger,
StatusBarStateController statusBarStateController,
KeyguardBypassController bypassController,
NotificationGroupManagerLegacy groupManager,
ConfigurationController configurationController) {
- return new HeadsUpManagerPhone(context, statusBarStateController, bypassController,
- groupManager, configurationController);
+ return new HeadsUpManagerPhone(
+ context,
+ headsUpManagerLogger,
+ statusBarStateController,
+ bypassController,
+ groupManager,
+ configurationController
+ );
}
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.java
index f678513..8370615 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.java
@@ -17,8 +17,8 @@
package com.android.systemui.tv;
import com.android.systemui.dagger.WMComponent;
-import com.android.systemui.dagger.WMSingleton;
-import com.android.systemui.wmshell.TvWMShellModule;
+import com.android.wm.shell.dagger.WMSingleton;
+import com.android.wm.shell.dagger.TvWMShellModule;
import dagger.Subcomponent;
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
new file mode 100644
index 0000000..cc2c208
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.systemui.unfold
+
+import com.android.keyguard.KeyguardUnfoldTransition
+import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController
+import dagger.BindsInstance
+import dagger.Module
+import dagger.Provides
+import dagger.Subcomponent
+import java.util.Optional
+import javax.inject.Named
+import javax.inject.Scope
+
+@Scope
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class SysUIUnfoldScope
+
+/**
+ * Creates an injectable [SysUIUnfoldComponent] that provides objects that have been scoped with
+ * [@SysUIUnfoldScope]. Since [SysUIUnfoldComponent] depends upon:
+ * * [Optional<UnfoldTransitionProgressProvider>]
+ * * [Optional<ScopedUnfoldTransitionProgressProvider>]
+ * no objects will get constructed if these parameters are empty.
+ */
+@Module(subcomponents = [SysUIUnfoldComponent::class])
+object SysUIUnfoldModule {
+ @Provides
+ @SysUISingleton
+ fun provideSysUIUnfoldComponent(
+ provider: Optional<UnfoldTransitionProgressProvider>,
+ @Named(UNFOLD_STATUS_BAR) scopedProvider: Optional<ScopedUnfoldTransitionProgressProvider>,
+ factory: SysUIUnfoldComponent.Factory
+ ) =
+ provider.flatMap {
+ p -> scopedProvider.map { sp -> factory.create(p, sp) }
+ }
+}
+
+@SysUIUnfoldScope
+@Subcomponent
+interface SysUIUnfoldComponent {
+
+ @Subcomponent.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance provider: UnfoldTransitionProgressProvider,
+ @BindsInstance scopedProvider: ScopedUnfoldTransitionProgressProvider
+ ): SysUIUnfoldComponent
+ }
+
+ fun getKeyguardUnfoldTransition(): KeyguardUnfoldTransition
+
+ fun getStatusBarMoveFromCenterAnimationController(): StatusBarMoveFromCenterAnimationController
+
+ fun getUnfoldTransitionWallpaperController(): UnfoldTransitionWallpaperController
+
+ fun getUnfoldLightRevealOverlayAnimation(): UnfoldLightRevealOverlayAnimation
+}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index f0760d4..51de1321 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -31,7 +31,6 @@
import android.view.SurfaceSession
import android.view.WindowManager
import android.view.WindowlessWindowManager
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.statusbar.LightRevealEffect
@@ -44,7 +43,7 @@
import java.util.function.Consumer
import javax.inject.Inject
-@SysUISingleton
+@SysUIUnfoldScope
class UnfoldLightRevealOverlayAnimation @Inject constructor(
private val context: Context,
private val deviceStateManager: DeviceStateManager,
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/ShellUnfoldProgressProvider.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/unfold/ShellUnfoldProgressProvider.kt
rename to packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt
index 4a88435..bd04ad8 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/ShellUnfoldProgressProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt
@@ -21,7 +21,7 @@
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener
import java.util.concurrent.Executor
-class ShellUnfoldProgressProvider(
+class UnfoldProgressProvider(
private val unfoldProgressProvider: UnfoldTransitionProgressProvider
) : ShellUnfoldProgressProvider {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index 7e4ec67..cebc931 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -43,21 +43,27 @@
fun provideUnfoldTransitionProgressProvider(
context: Context,
config: UnfoldTransitionConfig,
- screenStatusProvider: LifecycleScreenStatusProvider,
+ screenStatusProvider: Lazy<LifecycleScreenStatusProvider>,
deviceStateManager: DeviceStateManager,
sensorManager: SensorManager,
@Main executor: Executor,
@Main handler: Handler
- ): UnfoldTransitionProgressProvider =
- createUnfoldTransitionProgressProvider(
- context,
- config,
- screenStatusProvider,
- deviceStateManager,
- sensorManager,
- handler,
- executor
- )
+ ) =
+ if (config.isEnabled) {
+ Optional.of(
+ createUnfoldTransitionProgressProvider(
+ context,
+ config,
+ screenStatusProvider.get(),
+ deviceStateManager,
+ sensorManager,
+ handler,
+ executor
+ )
+ )
+ } else {
+ Optional.empty()
+ }
@Provides
@Singleton
@@ -69,33 +75,37 @@
fun provideNaturalRotationProgressProvider(
context: Context,
windowManager: IWindowManager,
- unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider
- ): NaturalRotationUnfoldProgressProvider =
- NaturalRotationUnfoldProgressProvider(
- context,
- windowManager,
- unfoldTransitionProgressProvider
- )
+ unfoldTransitionProgressProvider: Optional<UnfoldTransitionProgressProvider>
+ ) =
+ unfoldTransitionProgressProvider.map {
+ provider -> NaturalRotationUnfoldProgressProvider(
+ context,
+ windowManager,
+ provider
+ )
+ }
@Provides
@Named(UNFOLD_STATUS_BAR)
@Singleton
fun provideStatusBarScopedTransitionProvider(
- source: NaturalRotationUnfoldProgressProvider
- ): ScopedUnfoldTransitionProgressProvider =
- ScopedUnfoldTransitionProgressProvider(source)
+ source: Optional<NaturalRotationUnfoldProgressProvider>
+ ) =
+ source.map {
+ provider -> ScopedUnfoldTransitionProgressProvider(provider)
+ }
@Provides
@Singleton
fun provideShellProgressProvider(
config: UnfoldTransitionConfig,
- provider: Lazy<UnfoldTransitionProgressProvider>
+ provider: Optional<UnfoldTransitionProgressProvider>
): Optional<ShellUnfoldProgressProvider> =
- if (config.isEnabled) {
- Optional.ofNullable(ShellUnfoldProgressProvider(provider.get()))
+ if (config.isEnabled && provider.isPresent()) {
+ Optional.of(UnfoldProgressProvider(provider.get()))
} else {
Optional.empty()
}
}
-const val UNFOLD_STATUS_BAR = "unfold_status_bar"
\ No newline at end of file
+const val UNFOLD_STATUS_BAR = "unfold_status_bar"
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionWallpaperController.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionWallpaperController.kt
index 4f45aaf..a184315 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionWallpaperController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionWallpaperController.kt
@@ -16,12 +16,11 @@
package com.android.systemui.unfold
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.util.WallpaperController
import javax.inject.Inject
-@SysUISingleton
+@SysUIUnfoldScope
class UnfoldTransitionWallpaperController @Inject constructor(
private val unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
private val wallpaperController: WallpaperController
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/nullability.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/nullability.kt
index 92c73a4..f3b7e0d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/nullability.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/nullability.kt
@@ -16,7 +16,14 @@
package com.android.systemui.util.kotlin
+import java.util.Optional
+
/**
* If [value] is not null, then returns block(value). Otherwise returns null.
*/
inline fun <T : Any, R> transform(value: T?, block: (T) -> R): R? = value?.let(block)
+
+/**
+ * Assists type-checking to unpack a Java Optional into T?
+ */
+inline fun <T> Optional<T>.getOrNull(): T? = orElse(null)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index e570598..5aa3617 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -377,7 +377,8 @@
private void playTouchFeedback() {
if (System.currentTimeMillis() - mLastToggledRingerOn < TOUCH_FEEDBACK_TIMEOUT_MS) {
try {
- mAudioService.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD);
+ mAudioService.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD,
+ UserHandle.USER_CURRENT);
} catch (RemoteException e) {
// ignore
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
index 5bcf828..5427806 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
@@ -12,6 +12,7 @@
import android.widget.LinearLayout
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogListener.DismissReason
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
@@ -63,10 +64,6 @@
assertEquals(1, hostDialogRoot.childCount)
assertEquals(dialog.contentView, hostDialogRoot.getChildAt(0))
- // If we are dozing, the host dialog window also fades out.
- runOnMainThreadAndWaitForIdleSync { dialogLaunchAnimator.onDozeAmountChanged(0.5f) }
- assertTrue(hostDialog.window!!.decorView.alpha < 1f)
-
// Hiding/showing/dismissing the dialog should hide/show/dismiss the host dialog given that
// it's a ListenableDialog.
runOnMainThreadAndWaitForIdleSync { dialog.hide() }
@@ -164,7 +161,7 @@
override fun dismiss() {
super.dismiss()
- notifyListeners { onDismiss() }
+ notifyListeners { onDismiss(DismissReason.UNKNOWN) }
}
override fun hide() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
index 5fee7fb..2d51092 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
@@ -16,8 +16,14 @@
package com.android.systemui.biometrics
+import android.animation.Animator
+import android.graphics.Insets
+import android.app.ActivityManager
+import android.app.ActivityTaskManager
+import android.content.ComponentName
import android.graphics.Rect
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
+import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
import android.hardware.biometrics.BiometricOverlayConstants.REASON_UNKNOWN
import android.hardware.biometrics.SensorProperties
import android.hardware.display.DisplayManager
@@ -33,7 +39,9 @@
import android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS
import android.view.DisplayInfo
import android.view.LayoutInflater
+import android.view.Surface
import android.view.View
+import android.view.ViewPropertyAnimator
import android.view.WindowInsets
import android.view.WindowManager
import android.view.WindowMetrics
@@ -41,6 +49,7 @@
import com.airbnb.lottie.LottieAnimationView
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.recents.OverviewProxyService
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import org.junit.Before
@@ -53,9 +62,13 @@
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
+import org.mockito.Mockito.anyFloat
+import org.mockito.Mockito.anyLong
+import org.mockito.Mockito.anyInt
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
@@ -77,10 +90,14 @@
@Mock
lateinit var windowManager: WindowManager
@Mock
+ lateinit var activityTaskManager: ActivityTaskManager
+ @Mock
lateinit var sidefpsView: View
@Mock
lateinit var displayManager: DisplayManager
@Mock
+ lateinit var overviewProxyService: OverviewProxyService
+ @Mock
lateinit var handler: Handler
@Captor
lateinit var overlayCaptor: ArgumentCaptor<View>
@@ -91,45 +108,63 @@
@Before
fun setup() {
+ context.addMockSystemService(DisplayManager::class.java, displayManager)
+ context.addMockSystemService(WindowManager::class.java, windowManager)
+
`when`(layoutInflater.inflate(R.layout.sidefps_view, null, false)).thenReturn(sidefpsView)
`when`(sidefpsView.findViewById<LottieAnimationView>(eq(R.id.sidefps_animation)))
.thenReturn(mock(LottieAnimationView::class.java))
+ with(mock(ViewPropertyAnimator::class.java)) {
+ `when`(sidefpsView.animate()).thenReturn(this)
+ `when`(alpha(anyFloat())).thenReturn(this)
+ `when`(setStartDelay(anyLong())).thenReturn(this)
+ `when`(setDuration(anyLong())).thenReturn(this)
+ `when`(setListener(any())).thenAnswer {
+ (it.arguments[0] as Animator.AnimatorListener)
+ .onAnimationEnd(mock(Animator::class.java))
+ this
+ }
+ }
`when`(fingerprintManager.sensorPropertiesInternal).thenReturn(
- listOf(
- FingerprintSensorPropertiesInternal(
- SENSOR_ID,
- SensorProperties.STRENGTH_STRONG,
- 5 /* maxEnrollmentsPerUser */,
- listOf() /* componentInfo */,
- FingerprintSensorProperties.TYPE_POWER_BUTTON,
- true /* resetLockoutRequiresHardwareAuthToken */
- )
+ listOf(
+ FingerprintSensorPropertiesInternal(
+ SENSOR_ID,
+ SensorProperties.STRENGTH_STRONG,
+ 5 /* maxEnrollmentsPerUser */,
+ listOf() /* componentInfo */,
+ FingerprintSensorProperties.TYPE_POWER_BUTTON,
+ true /* resetLockoutRequiresHardwareAuthToken */
)
- )
- `when`(windowManager.defaultDisplay).thenReturn(
- Display(
- DisplayManagerGlobal.getInstance(),
- DISPLAY_ID,
- DisplayInfo(),
- DEFAULT_DISPLAY_ADJUSTMENTS
- )
+ )
)
`when`(windowManager.maximumWindowMetrics).thenReturn(
WindowMetrics(Rect(0, 0, 800, 800), WindowInsets.CONSUMED)
)
+ }
+
+ private fun testWithDisplay(initInfo: DisplayInfo.() -> Unit = {}, block: () -> Unit) {
+ val displayInfo = DisplayInfo()
+ displayInfo.initInfo()
+ val dmGlobal = mock(DisplayManagerGlobal::class.java)
+ val display = Display(dmGlobal, DISPLAY_ID, displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS)
+ `when`(dmGlobal.getDisplayInfo(eq(DISPLAY_ID))).thenReturn(displayInfo)
+ `when`(windowManager.defaultDisplay).thenReturn(display)
sideFpsController = SidefpsController(
- mContext, layoutInflater, fingerprintManager, windowManager, executor,
- displayManager, handler
+ context.createDisplayContext(display), layoutInflater, fingerprintManager,
+ windowManager, activityTaskManager, overviewProxyService, displayManager, executor,
+ handler
)
overlayController = ArgumentCaptor.forClass(ISidefpsController::class.java).apply {
verify(fingerprintManager).setSidefpsController(capture())
}.value
+
+ block()
}
@Test
- fun testSubscribesToOrientationChangesWhenShowingOverlay() {
+ fun testSubscribesToOrientationChangesWhenShowingOverlay() = testWithDisplay {
overlayController.show(SENSOR_ID, REASON_UNKNOWN)
executor.runAllReady()
@@ -141,7 +176,7 @@
}
@Test
- fun testShowsAndHides() {
+ fun testShowsAndHides() = testWithDisplay {
overlayController.show(SENSOR_ID, REASON_UNKNOWN)
executor.runAllReady()
@@ -156,7 +191,7 @@
}
@Test
- fun testShowsOnce() {
+ fun testShowsOnce() = testWithDisplay {
repeat(5) {
overlayController.show(SENSOR_ID, REASON_UNKNOWN)
executor.runAllReady()
@@ -167,7 +202,7 @@
}
@Test
- fun testHidesOnce() {
+ fun testHidesOnce() = testWithDisplay {
overlayController.show(SENSOR_ID, REASON_UNKNOWN)
executor.runAllReady()
@@ -181,10 +216,80 @@
}
@Test
- fun testIgnoredForKeyguard() {
- overlayController.show(SENSOR_ID, REASON_AUTH_KEYGUARD)
+ fun testIgnoredForKeyguard() = testWithDisplay {
+ testIgnoredFor(REASON_AUTH_KEYGUARD)
+ }
+
+ @Test
+ fun testShowsForMostSettings() = testWithDisplay {
+ `when`(activityTaskManager.getTasks(anyInt())).thenReturn(listOf(fpEnrollTask()))
+ testIgnoredFor(REASON_AUTH_SETTINGS, ignored = false)
+ }
+
+ @Test
+ fun testIgnoredForVerySpecificSettings() = testWithDisplay {
+ `when`(activityTaskManager.getTasks(anyInt())).thenReturn(listOf(fpSettingsTask()))
+ testIgnoredFor(REASON_AUTH_SETTINGS)
+ }
+
+ private fun testIgnoredFor(reason: Int, ignored: Boolean = true) {
+ overlayController.show(SENSOR_ID, reason)
executor.runAllReady()
- verify(windowManager, never()).addView(any(), any())
+ verify(windowManager, if (ignored) never() else times(1)).addView(any(), any())
}
+
+ @Test
+ fun showsWithTaskbar() = testWithDisplay({ rotation = Surface.ROTATION_0 }) {
+ hidesWithTaskbar(visible = true)
+ }
+
+ @Test
+ fun showsWithTaskbar90() = testWithDisplay({ rotation = Surface.ROTATION_90 }) {
+ hidesWithTaskbar(visible = true)
+ }
+
+ @Test
+ fun showsWithTaskbar180() = testWithDisplay({ rotation = Surface.ROTATION_180 }) {
+ hidesWithTaskbar(visible = true)
+ }
+
+ @Test
+ fun showsWithTaskbarCollapsedDown() = testWithDisplay({ rotation = Surface.ROTATION_270 }) {
+ `when`(windowManager.currentWindowMetrics).thenReturn(
+ WindowMetrics(Rect(0, 0, 800, 800), insetsForSmallNavbar())
+ )
+ hidesWithTaskbar(visible = true)
+ }
+
+ @Test
+ fun hidesWithTaskbarDown() = testWithDisplay({ rotation = Surface.ROTATION_270 }) {
+ `when`(windowManager.currentWindowMetrics).thenReturn(
+ WindowMetrics(Rect(0, 0, 800, 800), insetsForLargeNavbar())
+ )
+ hidesWithTaskbar(visible = false)
+ }
+
+ private fun hidesWithTaskbar(visible: Boolean) {
+ overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+ executor.runAllReady()
+
+ sideFpsController.overviewProxyListener.onTaskbarStatusUpdated(true, false)
+ executor.runAllReady()
+
+ verify(windowManager).addView(any(), any())
+ verify(windowManager, never()).removeView(any())
+ verify(sidefpsView).visibility = if (visible) View.VISIBLE else View.GONE
+ }
+}
+
+private fun insetsForSmallNavbar() = insetsWithBottom(60)
+private fun insetsForLargeNavbar() = insetsWithBottom(100)
+private fun insetsWithBottom(bottom: Int) = WindowInsets.Builder()
+ .setInsets(WindowInsets.Type.navigationBars(), Insets.of(0, 0, 0, bottom))
+ .build()
+private fun fpEnrollTask() = settingsTask(".biometrics.fingerprint.FingerprintEnrollEnrolling")
+private fun fpSettingsTask() = settingsTask(".biometrics.fingerprint.FingerprintSettings")
+private fun settingsTask(cls: String) = ActivityManager.RunningTaskInfo().apply {
+ topActivity = ComponentName.createRelative("com.android.settings", cls)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index deabda3..d90eb73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -66,6 +66,7 @@
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.Execution;
@@ -219,7 +220,7 @@
mWindowManager,
mStatusBarStateController,
mFgExecutor,
- Optional.of(mStatusBar),
+ new PanelExpansionStateManager(),
mStatusBarKeyguardViewManager,
mDumpManager,
mKeyguardUpdateMonitor,
@@ -404,15 +405,75 @@
}
@Test
- public void showUdfpsOverlay_addsViewToWindow() throws RemoteException {
- mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ public void showUdfpsOverlay_addsViewToWindow_bp() throws RemoteException {
+ showUdfpsOverlay_addsViewToWindow(BiometricOverlayConstants.REASON_AUTH_BP);
+ }
+
+ @Test
+ public void showUdfpsOverlay_addsViewToWindow_keyguard() throws RemoteException {
+ showUdfpsOverlay_addsViewToWindow(BiometricOverlayConstants.REASON_AUTH_KEYGUARD);
+ }
+
+ @Test
+ public void showUdfpsOverlay_addsViewToWindow_settings() throws RemoteException {
+ showUdfpsOverlay_addsViewToWindow(BiometricOverlayConstants.REASON_AUTH_SETTINGS);
+ }
+
+ @Test
+ public void showUdfpsOverlay_addsViewToWindow_enroll_locate() throws RemoteException {
+ showUdfpsOverlay_addsViewToWindow(BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR);
+ }
+
+ @Test
+ public void showUdfpsOverlay_addsViewToWindow_enroll() throws RemoteException {
+ showUdfpsOverlay_addsViewToWindow(BiometricOverlayConstants.REASON_ENROLL_ENROLLING);
+ }
+
+ @Test
+ public void showUdfpsOverlay_addsViewToWindow_other() throws RemoteException {
+ showUdfpsOverlay_addsViewToWindow(BiometricOverlayConstants.REASON_AUTH_OTHER);
+ }
+
+ private void showUdfpsOverlay_addsViewToWindow(
+ @BiometricOverlayConstants.ShowReason int reason) throws RemoteException {
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, reason,
+ mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
verify(mWindowManager).addView(eq(mUdfpsView), any());
}
@Test
- public void hideUdfpsOverlay_removesViewFromWindow() throws RemoteException {
+ public void hideUdfpsOverlay_removesViewFromWindow_bp() throws RemoteException {
+ hideUdfpsOverlay_removesViewFromWindow(BiometricOverlayConstants.REASON_AUTH_BP);
+ }
+
+ @Test
+ public void hideUdfpsOverlay_removesViewFromWindow_keyguard() throws RemoteException {
+ hideUdfpsOverlay_removesViewFromWindow(BiometricOverlayConstants.REASON_AUTH_KEYGUARD);
+ }
+
+ @Test
+ public void hideUdfpsOverlay_removesViewFromWindow_settings() throws RemoteException {
+ hideUdfpsOverlay_removesViewFromWindow(BiometricOverlayConstants.REASON_AUTH_SETTINGS);
+ }
+
+ @Test
+ public void hideUdfpsOverlay_removesViewFromWindow_enroll_locate() throws RemoteException {
+ hideUdfpsOverlay_removesViewFromWindow(BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR);
+ }
+
+ @Test
+ public void hideUdfpsOverlay_removesViewFromWindow_enroll() throws RemoteException {
+ hideUdfpsOverlay_removesViewFromWindow(BiometricOverlayConstants.REASON_ENROLL_ENROLLING);
+ }
+
+ @Test
+ public void hideUdfpsOverlay_removesViewFromWindow_other() throws RemoteException {
+ hideUdfpsOverlay_removesViewFromWindow(BiometricOverlayConstants.REASON_AUTH_OTHER);
+ }
+
+ private void hideUdfpsOverlay_removesViewFromWindow(
+ @BiometricOverlayConstants.ShowReason int reason) throws RemoteException {
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 2821f3d..0e86964 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -41,9 +41,10 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -57,7 +58,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.Optional;
import java.util.List;
@SmallTest
@@ -72,7 +72,7 @@
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
- private StatusBar mStatusBar;
+ private PanelExpansionStateManager mPanelExpansionStateManager;
@Mock
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock
@@ -101,8 +101,8 @@
@Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor;
private StatusBarStateController.StateListener mStatusBarStateListener;
- @Captor private ArgumentCaptor<StatusBar.ExpansionChangedListener> mExpansionListenerCaptor;
- private List<StatusBar.ExpansionChangedListener> mExpansionListeners;
+ @Captor private ArgumentCaptor<PanelExpansionListener> mExpansionListenerCaptor;
+ private List<PanelExpansionListener> mExpansionListeners;
@Captor private ArgumentCaptor<StatusBarKeyguardViewManager.AlternateAuthInterceptor>
mAltAuthInterceptorCaptor;
@@ -121,7 +121,7 @@
mController = new UdfpsKeyguardViewController(
mView,
mStatusBarStateController,
- Optional.of(mStatusBar),
+ mPanelExpansionStateManager,
mStatusBarKeyguardViewManager,
mKeyguardUpdateMonitor,
mDumpManager,
@@ -170,8 +170,8 @@
mController.onViewDetached();
verify(mStatusBarStateController).removeCallback(mStatusBarStateListener);
- for (StatusBar.ExpansionChangedListener listener : mExpansionListeners) {
- verify(mStatusBar).removeExpansionChangedListener(listener);
+ for (PanelExpansionListener listener : mExpansionListeners) {
+ verify(mPanelExpansionStateManager).removeExpansionListener(listener);
}
verify(mKeyguardStateController).removeCallback(mKeyguardStateControllerCallback);
}
@@ -434,16 +434,16 @@
}
private void captureExpansionListeners() {
- verify(mStatusBar, times(2))
- .addExpansionChangedListener(mExpansionListenerCaptor.capture());
+ verify(mPanelExpansionStateManager, times(2))
+ .addExpansionListener(mExpansionListenerCaptor.capture());
// first (index=0) is from super class, UdfpsAnimationViewController.
// second (index=1) is from UdfpsKeyguardViewController
mExpansionListeners = mExpansionListenerCaptor.getAllValues();
}
- private void updateStatusBarExpansion(float expansion, boolean expanded) {
- for (StatusBar.ExpansionChangedListener listener : mExpansionListeners) {
- listener.onExpansionChanged(expansion, expanded);
+ private void updateStatusBarExpansion(float fraction, boolean expanded) {
+ for (PanelExpansionListener listener : mExpansionListeners) {
+ listener.onPanelExpansionChanged(fraction, expanded, /* tracking= */ false);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt
index ee1cc7b..890b9ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt
@@ -154,6 +154,28 @@
assertNull(wrapper.intent)
}
+ @Test
+ fun testClassCastExceptionComponentName_noCrash() {
+ val badIntent = Intent(ControlsProviderService.ACTION_ADD_CONTROL).apply {
+ putExtra(Intent.EXTRA_COMPONENT_NAME, Intent())
+ putExtra(ControlsProviderService.EXTRA_CONTROL, control)
+ }
+ receiver.onReceive(wrapper, badIntent)
+
+ assertNull(wrapper.intent)
+ }
+
+ @Test
+ fun testClassCastExceptionControl_noCrash() {
+ val badIntent = Intent(ControlsProviderService.ACTION_ADD_CONTROL).apply {
+ putExtra(Intent.EXTRA_COMPONENT_NAME, componentName)
+ putExtra(ControlsProviderService.EXTRA_CONTROL, Intent())
+ }
+ receiver.onReceive(wrapper, badIntent)
+
+ assertNull(wrapper.intent)
+ }
+
class MyWrapper(context: Context) : ContextWrapper(context) {
var intent: Intent? = null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/AppWidgetOverlayProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/AppWidgetOverlayProviderTest.java
new file mode 100644
index 0000000..0fc306b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/AppWidgetOverlayProviderTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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.systemui.dreams;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.isNull;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetHostView;
+import android.content.ComponentName;
+import android.testing.AndroidTestingRunner;
+import android.widget.RemoteViews;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestableContext;
+import com.android.systemui.dreams.appwidgets.AppWidgetOverlayProvider;
+import com.android.systemui.dreams.appwidgets.AppWidgetProvider;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.utils.leaks.LeakCheckedTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class AppWidgetOverlayProviderTest extends SysuiTestCase {
+ @Mock
+ ActivityStarter mActivityStarter;
+
+ @Mock
+ ComponentName mComponentName;
+
+ @Mock
+ AppWidgetProvider mAppWidgetProvider;
+
+ @Mock
+ AppWidgetHostView mAppWidgetHostView;
+
+ @Mock
+ OverlayHost.CreationCallback mCreationCallback;
+
+ @Mock
+ OverlayHost.InteractionCallback mInteractionCallback;
+
+ @Mock
+ PendingIntent mPendingIntent;
+
+ @Mock
+ RemoteViews.RemoteResponse mRemoteResponse;
+
+ AppWidgetOverlayProvider mOverlayProvider;
+
+ RemoteViews.InteractionHandler mInteractionHandler;
+
+ @Rule
+ public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
+
+ @Rule
+ public SysuiTestableContext mContext = new SysuiTestableContext(
+ InstrumentationRegistry.getContext(), mLeakCheck);
+
+ OverlayHostView.LayoutParams mLayoutParams = new OverlayHostView.LayoutParams(
+ OverlayHostView.LayoutParams.MATCH_PARENT, OverlayHostView.LayoutParams.MATCH_PARENT);
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mPendingIntent.isActivity()).thenReturn(true);
+ when(mAppWidgetProvider.getWidget(mComponentName)).thenReturn(mAppWidgetHostView);
+
+ mOverlayProvider = new AppWidgetOverlayProvider(
+ mActivityStarter,
+ mComponentName,
+ mAppWidgetProvider,
+ mLayoutParams
+ );
+
+ final ArgumentCaptor<RemoteViews.InteractionHandler> creationCallbackCapture =
+ ArgumentCaptor.forClass(RemoteViews.InteractionHandler.class);
+
+ mOverlayProvider.onCreateOverlay(mContext, mCreationCallback, mInteractionCallback);
+ verify(mAppWidgetHostView, times(1))
+ .setInteractionHandler(creationCallbackCapture.capture());
+ mInteractionHandler = creationCallbackCapture.getValue();
+ }
+
+ @Test
+ public void testWidgetBringup() {
+ // Make sure widget was requested.
+ verify(mAppWidgetProvider, times(1)).getWidget(eq(mComponentName));
+
+ // Make sure widget was returned to callback.
+ verify(mCreationCallback, times(1)).onCreated(eq(mAppWidgetHostView),
+ eq(mLayoutParams));
+ }
+
+ @Test
+ public void testWidgetInteraction() {
+ // Trigger interaction.
+ mInteractionHandler.onInteraction(mAppWidgetHostView, mPendingIntent,
+ mRemoteResponse);
+
+ // Ensure activity is started.
+ verify(mActivityStarter, times(1))
+ .startPendingIntentDismissingKeyguard(eq(mPendingIntent), isNull(),
+ eq(mAppWidgetHostView));
+ // Verify exit is requested.
+ verify(mInteractionCallback, times(1)).onExit();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
new file mode 100644
index 0000000..53bfeee
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -0,0 +1,153 @@
+/*
+ * 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.systemui.dreams;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.os.IBinder;
+import android.service.dreams.IDreamOverlay;
+import android.service.dreams.IDreamOverlayCallback;
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestableContext;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.utils.leaks.LeakCheckedTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DreamOverlayServiceTest extends SysuiTestCase {
+ private FakeSystemClock mFakeSystemClock = new FakeSystemClock();
+ private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
+
+ @Rule
+ public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
+
+ @Rule
+ public SysuiTestableContext mContext = new SysuiTestableContext(
+ InstrumentationRegistry.getContext(), mLeakCheck);
+
+ WindowManager.LayoutParams mWindowParams = new WindowManager.LayoutParams();
+
+ @Mock
+ IDreamOverlayCallback mDreamOverlayCallback;
+
+ @Mock
+ WindowManagerImpl mWindowManager;
+
+ @Mock
+ OverlayProvider mProvider;
+
+ @Mock
+ DreamOverlayStateController mDreamOverlayStateController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mContext.addMockSystemService(WindowManager.class, mWindowManager);
+ }
+
+ @Test
+ public void testInteraction() throws Exception {
+ final DreamOverlayService service = new DreamOverlayService(mContext, mMainExecutor,
+ mDreamOverlayStateController);
+ final IBinder proxy = service.onBind(new Intent());
+ final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+ clearInvocations(mWindowManager);
+
+ // Inform the overlay service of dream starting.
+ overlay.startDream(mWindowParams, mDreamOverlayCallback);
+ mMainExecutor.runAllReady();
+ verify(mWindowManager).addView(any(), any());
+
+ // Add overlay.
+ service.addOverlay(mProvider);
+ mMainExecutor.runAllReady();
+
+ final ArgumentCaptor<OverlayHost.CreationCallback> creationCallbackCapture =
+ ArgumentCaptor.forClass(OverlayHost.CreationCallback.class);
+ final ArgumentCaptor<OverlayHost.InteractionCallback> interactionCallbackCapture =
+ ArgumentCaptor.forClass(OverlayHost.InteractionCallback.class);
+
+ // Ensure overlay provider is asked to create view.
+ verify(mProvider).onCreateOverlay(any(), creationCallbackCapture.capture(),
+ interactionCallbackCapture.capture());
+ mMainExecutor.runAllReady();
+
+ // Inform service of overlay view creation.
+ final View view = new View(mContext);
+ creationCallbackCapture.getValue().onCreated(view, new ConstraintLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
+ ));
+
+ // Ask service to exit.
+ interactionCallbackCapture.getValue().onExit();
+ mMainExecutor.runAllReady();
+
+ // Ensure service informs dream host of exit.
+ verify(mDreamOverlayCallback).onExitRequested();
+ }
+
+ @Test
+ public void testListening() throws Exception {
+ final DreamOverlayService service = new DreamOverlayService(mContext, mMainExecutor,
+ mDreamOverlayStateController);
+
+ final IBinder proxy = service.onBind(new Intent());
+ final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+
+ // Inform the overlay service of dream starting.
+ overlay.startDream(mWindowParams, mDreamOverlayCallback);
+ mMainExecutor.runAllReady();
+
+ // Verify overlay service registered as listener with DreamOverlayStateController
+ // and inform callback of addition.
+ final ArgumentCaptor<DreamOverlayStateController.Callback> callbackCapture =
+ ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class);
+
+ verify(mDreamOverlayStateController).addCallback(callbackCapture.capture());
+ when(mDreamOverlayStateController.getOverlays()).thenReturn(Arrays.asList(mProvider));
+ callbackCapture.getValue().onOverlayChanged();
+ mMainExecutor.runAllReady();
+
+ // Verify provider is asked to create overlay.
+ verify(mProvider).onCreateOverlay(any(), any(), any());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
new file mode 100644
index 0000000..4e97be3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Collection;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DreamOverlayStateControllerTest extends SysuiTestCase {
+ @Mock
+ DreamOverlayStateController.Callback mCallback;
+
+ @Mock
+ OverlayProvider mProvider;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testCallback() {
+ final DreamOverlayStateController stateController = new DreamOverlayStateController();
+ stateController.addCallback(mCallback);
+
+ // Add overlay and verify callback is notified.
+ final DreamOverlayStateController.OverlayToken token =
+ stateController.addOverlay(mProvider);
+
+ verify(mCallback, times(1)).onOverlayChanged();
+
+ final Collection<OverlayProvider> providers = stateController.getOverlays();
+ assertEquals(providers.size(), 1);
+ assertTrue(providers.contains(mProvider));
+
+ clearInvocations(mCallback);
+
+ // Remove overlay and verify callback is notified.
+ stateController.removeOverlay(token);
+ verify(mCallback, times(1)).onOverlayChanged();
+ assertTrue(providers.isEmpty());
+ }
+
+ @Test
+ public void testNotifyOnCallbackAdd() {
+ final DreamOverlayStateController stateController = new DreamOverlayStateController();
+ final DreamOverlayStateController.OverlayToken token =
+ stateController.addOverlay(mProvider);
+
+ // Verify callback occurs on add when an overlay is already present.
+ stateController.addCallback(mCallback);
+ verify(mCallback, times(1)).onOverlayChanged();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimerTest.java
new file mode 100644
index 0000000..2e5b165
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimerTest.java
@@ -0,0 +1,188 @@
+/*
+ * 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.systemui.dreams.appwidgets;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.res.Resources;
+import android.testing.AndroidTestingRunner;
+import android.view.Gravity;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestableContext;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.dreams.dagger.AppWidgetOverlayComponent;
+import com.android.systemui.utils.leaks.LeakCheckedTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class AppWidgetOverlayPrimerTest extends SysuiTestCase {
+ @Rule
+ public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
+
+ @Rule
+ public SysuiTestableContext mContext = new SysuiTestableContext(
+ InstrumentationRegistry.getContext(), mLeakCheck);
+
+ @Mock
+ Resources mResources;
+
+ @Mock
+ AppWidgetOverlayComponent mAppWidgetOverlayComponent1;
+ @Mock
+ AppWidgetOverlayComponent mAppWidgetOverlayComponent2;
+
+ @Mock
+ AppWidgetOverlayProvider mAppWidgetOverlayProvider1;
+
+ @Mock
+ AppWidgetOverlayProvider mAppWidgetOverlayProvider2;
+
+ final ComponentName mAppOverlayComponent1 =
+ ComponentName.unflattenFromString("com.foo.bar/.Baz");
+ final ComponentName mAppOverlayComponent2 =
+ ComponentName.unflattenFromString("com.foo.bar/.Baz2");
+
+ final int mAppOverlayGravity1 = Gravity.BOTTOM | Gravity.START;
+ final int mAppOverlayGravity2 = Gravity.BOTTOM | Gravity.END;
+
+ final String[] mComponents = new String[]{mAppOverlayComponent1.flattenToString(),
+ mAppOverlayComponent2.flattenToString() };
+ final int[] mPositions = new int[]{ mAppOverlayGravity1, mAppOverlayGravity2 };
+
+ @Mock
+ DreamOverlayStateController mDreamOverlayStateController;
+
+ @Mock
+ AppWidgetOverlayComponent.Factory mAppWidgetOverlayProviderFactory;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mAppWidgetOverlayProviderFactory.build(eq(mAppOverlayComponent1), any()))
+ .thenReturn(mAppWidgetOverlayComponent1);
+ when(mAppWidgetOverlayComponent1.getAppWidgetOverlayProvider())
+ .thenReturn(mAppWidgetOverlayProvider1);
+ when(mAppWidgetOverlayProviderFactory.build(eq(mAppOverlayComponent2), any()))
+ .thenReturn(mAppWidgetOverlayComponent2);
+ when(mAppWidgetOverlayComponent2.getAppWidgetOverlayProvider())
+ .thenReturn(mAppWidgetOverlayProvider2);
+ when(mResources.getIntArray(R.array.config_dreamOverlayPositions)).thenReturn(mPositions);
+ when(mResources.getStringArray(R.array.config_dreamOverlayComponents))
+ .thenReturn(mComponents);
+ }
+
+ @Test
+ public void testLoading() {
+ final AppWidgetOverlayPrimer primer = new AppWidgetOverlayPrimer(mContext,
+ mResources,
+ mDreamOverlayStateController,
+ mAppWidgetOverlayProviderFactory);
+
+ // Inform primer to begin.
+ primer.onBootCompleted();
+
+ // Verify the first component is added to the state controller with the proper position.
+ {
+ final ArgumentCaptor<ConstraintLayout.LayoutParams> layoutParamsArgumentCaptor =
+ ArgumentCaptor.forClass(ConstraintLayout.LayoutParams.class);
+ verify(mAppWidgetOverlayProviderFactory, times(1)).build(eq(mAppOverlayComponent1),
+ layoutParamsArgumentCaptor.capture());
+
+ assertEquals(layoutParamsArgumentCaptor.getValue().startToStart,
+ ConstraintLayout.LayoutParams.PARENT_ID);
+ assertEquals(layoutParamsArgumentCaptor.getValue().bottomToBottom,
+ ConstraintLayout.LayoutParams.PARENT_ID);
+
+ verify(mDreamOverlayStateController, times(1))
+ .addOverlay(eq(mAppWidgetOverlayProvider1));
+ }
+
+ // Verify the second component is added to the state controller with the proper position.
+ {
+ final ArgumentCaptor<ConstraintLayout.LayoutParams> layoutParamsArgumentCaptor =
+ ArgumentCaptor.forClass(ConstraintLayout.LayoutParams.class);
+ verify(mAppWidgetOverlayProviderFactory, times(1)).build(eq(mAppOverlayComponent2),
+ layoutParamsArgumentCaptor.capture());
+
+ assertEquals(layoutParamsArgumentCaptor.getValue().endToEnd,
+ ConstraintLayout.LayoutParams.PARENT_ID);
+ assertEquals(layoutParamsArgumentCaptor.getValue().bottomToBottom,
+ ConstraintLayout.LayoutParams.PARENT_ID);
+ verify(mDreamOverlayStateController, times(1))
+ .addOverlay(eq(mAppWidgetOverlayProvider1));
+ }
+ }
+
+ @Test
+ public void testNoComponents() {
+ when(mResources.getStringArray(R.array.config_dreamOverlayComponents))
+ .thenReturn(new String[]{});
+
+ final AppWidgetOverlayPrimer primer = new AppWidgetOverlayPrimer(mContext,
+ mResources,
+ mDreamOverlayStateController,
+ mAppWidgetOverlayProviderFactory);
+
+ // Inform primer to begin.
+ primer.onBootCompleted();
+
+
+ // Make sure there is no request to add a widget if no components are specified by the
+ // product.
+ verify(mAppWidgetOverlayProviderFactory, never()).build(any(), any());
+ verify(mDreamOverlayStateController, never()).addOverlay(any());
+ }
+
+ @Test
+ public void testNoPositions() {
+ when(mResources.getIntArray(R.array.config_dreamOverlayPositions))
+ .thenReturn(new int[]{});
+
+ final AppWidgetOverlayPrimer primer = new AppWidgetOverlayPrimer(mContext,
+ mResources,
+ mDreamOverlayStateController,
+ mAppWidgetOverlayProviderFactory);
+
+ primer.onBootCompleted();
+
+ // Make sure there is no request to add a widget if no positions are specified by the
+ // product.
+ verify(mAppWidgetOverlayProviderFactory, never()).build(any(), any());
+ verify(mDreamOverlayStateController, never()).addOverlay(any());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java
index 8243be8..2fa32ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java
@@ -33,6 +33,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.util.settings.SecureSettings;
import org.junit.After;
import org.junit.Before;
@@ -52,7 +53,8 @@
public class FeatureFlagManagerTest extends SysuiTestCase {
FeatureFlagManager mFeatureFlagManager;
- @Mock private SystemPropertiesHelper mProps;
+ @Mock private FlagManager mFlagManager;
+ @Mock private SecureSettings mSecureSettings;
@Mock private Context mContext;
@Mock private DumpManager mDumpManager;
@@ -60,14 +62,14 @@
public void setup() {
MockitoAnnotations.initMocks(this);
- mFeatureFlagManager = new FeatureFlagManager(mProps, mContext, mDumpManager);
+ mFeatureFlagManager = new FeatureFlagManager(mSecureSettings, mContext, mDumpManager);
}
@After
public void onFinished() {
- // SystemPropertiesHelper and Context are provided for constructor consistency with the
+ // SecureSettings and Context are provided for constructor consistency with the
// debug version of the FeatureFlagManager, but should never be used.
- verifyZeroInteractions(mProps, mContext);
+ verifyZeroInteractions(mSecureSettings, mContext);
// The dump manager should be registered with even for the release version, but that's it.
verify(mDumpManager).registerDumpable(anyString(), any());
verifyNoMoreInteractions(mDumpManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 1bb660e..6d8645e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -57,17 +57,21 @@
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
-import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
+import java.util.Optional;
+import java.util.function.Function;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -90,7 +94,8 @@
private @Mock NavigationModeController mNavigationModeController;
private @Mock KeyguardDisplayManager mKeyguardDisplayManager;
private @Mock DozeParameters mDozeParameters;
- private @Mock UnfoldTransitionConfig mUnfoldTransitionConfig;
+ private @Mock Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent;
+ private @Mock Optional<UnfoldLightRevealOverlayAnimation> mUnfoldAnimationOptional;
private @Mock UnfoldLightRevealOverlayAnimation mUnfoldAnimation;
private @Mock SysuiStatusBarStateController mStatusBarStateController;
private @Mock KeyguardStateController mKeyguardStateController;
@@ -110,6 +115,12 @@
when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager);
when(mPowerManager.newWakeLock(anyInt(), any())).thenReturn(mock(WakeLock.class));
+ when(mSysUIUnfoldComponent.map(
+ ArgumentMatchers.<Function<SysUIUnfoldComponent, UnfoldLightRevealOverlayAnimation>>
+ any()))
+ .thenReturn(mUnfoldAnimationOptional);
+ when(mUnfoldAnimationOptional.isPresent()).thenReturn(true);
+ when(mUnfoldAnimationOptional.get()).thenReturn(mUnfoldAnimation);
mViewMediator = new KeyguardViewMediator(
mContext,
@@ -128,8 +139,7 @@
mNavigationModeController,
mKeyguardDisplayManager,
mDozeParameters,
- mUnfoldTransitionConfig,
- () -> mUnfoldAnimation,
+ mSysUIUnfoldComponent,
mStatusBarStateController,
mKeyguardStateController,
() -> mKeyguardUnlockAnimationController,
@@ -161,8 +171,6 @@
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public void testUnfoldTransitionEnabledDrawnTasksReady_onScreenTurningOn_callsDrawnCallback()
throws RemoteException {
- when(mUnfoldTransitionConfig.isEnabled()).thenReturn(true);
-
mViewMediator.onScreenTurningOn(mKeyguardDrawnCallback);
TestableLooper.get(this).processAllMessages();
onUnfoldOverlayReady();
@@ -175,7 +183,7 @@
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public void testUnfoldTransitionDisabledDrawnTasksReady_onScreenTurningOn_callsDrawnCallback()
throws RemoteException {
- when(mUnfoldTransitionConfig.isEnabled()).thenReturn(false);
+ when(mUnfoldAnimationOptional.isPresent()).thenReturn(false);
mViewMediator.onScreenTurningOn(mKeyguardDrawnCallback);
TestableLooper.get(this).processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index bf5a6e4..bbeadf6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -122,7 +122,7 @@
private lateinit var session: MediaSession
private val device = MediaDeviceData(true, null, DEVICE_NAME)
- private val disabledDevice = MediaDeviceData(false, null, null)
+ private val disabledDevice = MediaDeviceData(false, null, "Disabled Device")
@JvmField @Rule val mockito = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
index ab3b208..7dadbad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
@@ -56,6 +56,7 @@
private const val SESSION_KEY = "SESSION_KEY"
private const val SESSION_TITLE = "SESSION_TITLE"
private const val DEVICE_NAME = "DEVICE_NAME"
+private const val REMOTE_DEVICE_NAME = "REMOTE_DEVICE_NAME"
private const val USER_ID = 0
private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
@@ -195,8 +196,6 @@
// THEN the device should be disabled
val data = captureDeviceData(KEY)
assertThat(data.enabled).isFalse()
- assertThat(data.name).isNull()
- assertThat(data.icon).isNull()
}
@Test
@@ -263,6 +262,20 @@
}
@Test
+ fun deviceNameFromMR2RouteInfo() {
+ // GIVEN that MR2Manager returns a valid routing session
+ whenever(route.name).thenReturn(REMOTE_DEVICE_NAME)
+ // WHEN a notification is added
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ // THEN it uses the route name (instead of device name)
+ val data = captureDeviceData(KEY)
+ assertThat(data.enabled).isTrue()
+ assertThat(data.name).isEqualTo(REMOTE_DEVICE_NAME)
+ }
+
+ @Test
fun deviceDisabledWhenMR2ReturnsNullRouteInfo() {
// GIVEN that MR2Manager returns null for routing session
whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
@@ -273,8 +286,6 @@
// THEN the device is disabled
val data = captureDeviceData(KEY)
assertThat(data.enabled).isFalse()
- assertThat(data.name).isNull()
- assertThat(data.icon).isNull()
}
@Test
@@ -294,8 +305,6 @@
// THEN the device is disabled
val data = captureDeviceData(KEY)
assertThat(data.enabled).isFalse()
- assertThat(data.name).isNull()
- assertThat(data.icon).isNull()
}
@Test
@@ -315,8 +324,6 @@
// THEN the device is disabled
val data = captureDeviceData(KEY)
assertThat(data.enabled).isFalse()
- assertThat(data.name).isNull()
- assertThat(data.icon).isNull()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt
new file mode 100644
index 0000000..e2c6ff9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.systemui.qs
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.log.LogcatEchoTracker
+import com.android.systemui.statusbar.DisableFlagsLogger
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.mockito.Mockito.mock
+import java.io.PrintWriter
+import java.io.StringWriter
+
+@SmallTest
+class QSFragmentDisableFlagsLoggerTest : SysuiTestCase() {
+
+ private val buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
+ .create("buffer", 10)
+ private val disableFlagsLogger = DisableFlagsLogger(
+ listOf(DisableFlagsLogger.DisableFlag(0b001, 'A', 'a')),
+ listOf(DisableFlagsLogger.DisableFlag(0b001, 'B', 'b'))
+ )
+ private val logger = QSFragmentDisableFlagsLogger(buffer, disableFlagsLogger)
+
+ @Test
+ fun logDisableFlagChange_bufferHasStates() {
+ val state = DisableFlagsLogger.DisableState(0, 1)
+
+ logger.logDisableFlagChange(state, state)
+
+ val stringWriter = StringWriter()
+ buffer.dump(PrintWriter(stringWriter), tailLength = 0)
+ val actualString = stringWriter.toString()
+ val expectedLogString = disableFlagsLogger.getDisableFlagsString(
+ old = null, new = state, newAfterLocalModification = state
+ )
+
+ assertThat(actualString).contains(expectedLogString)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 0252420..047ff037 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -192,6 +192,7 @@
mQQSMediaHost,
mBypassController,
mQsComponentFactory,
+ mock(QSFragmentDisableFlagsLogger.class),
mFalsingManager,
mock(DumpManager.class));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
index 3059aa1..f41d7b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
@@ -4,13 +4,10 @@
import android.view.ViewGroup
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.plugins.qs.QSTile
-import com.android.systemui.qs.tileimpl.QSTileViewImpl
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.never
@@ -21,14 +18,13 @@
@SmallTest
class QSSquishinessControllerTest : SysuiTestCase() {
- @Mock private lateinit var qsTileHost: QSTileHost
@Mock private lateinit var qqsFooterActionsView: FooterActionsView
@Mock private lateinit var qqsFooterActionsViewLP: ViewGroup.MarginLayoutParams
@Mock private lateinit var qsAnimator: QSAnimator
+ @Mock private lateinit var qsPanelController: QSPanelController
@Mock private lateinit var quickQsPanelController: QuickQSPanelController
- @Mock private lateinit var qstileView: QSTileViewImpl
- @Mock private lateinit var qstile: QSTile
@Mock private lateinit var tileLayout: TileLayout
+ @Mock private lateinit var pagedTileLayout: PagedTileLayout
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
@@ -36,11 +32,10 @@
@Before
fun setup() {
- qsSquishinessController = QSSquishinessController(qsTileHost, qqsFooterActionsView,
- qsAnimator, quickQsPanelController)
- `when`(qsTileHost.tiles).thenReturn(mutableListOf(qstile))
- `when`(quickQsPanelController.getTileView(any())).thenReturn(qstileView)
+ qsSquishinessController = QSSquishinessController(qqsFooterActionsView, qsAnimator,
+ qsPanelController, quickQsPanelController)
`when`(quickQsPanelController.tileLayout).thenReturn(tileLayout)
+ `when`(qsPanelController.tileLayout).thenReturn(pagedTileLayout)
`when`(qqsFooterActionsView.layoutParams).thenReturn(qqsFooterActionsViewLP)
}
@@ -56,7 +51,7 @@
@Test
fun setSquishiness_updatesTiles() {
qsSquishinessController.squishiness = 0.5f
- verify(qstileView).squishinessFraction = 0.5f
verify(tileLayout).setSquishinessFraction(0.5f)
+ verify(pagedTileLayout).setSquishinessFraction(0.5f)
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index b34433c..3625874 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -34,6 +34,7 @@
import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.qs.carrier.QSCarrierGroup
import com.android.systemui.qs.carrier.QSCarrierGroupController
+import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.policy.Clock
@@ -50,6 +51,7 @@
import org.mockito.Answers
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
+import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
@@ -105,6 +107,8 @@
private lateinit var context: Context
@Mock
private lateinit var featureFlags: FeatureFlags
+ @Mock
+ private lateinit var insetsProvider: StatusBarContentInsetsProvider
private val qsExpansionPathInterpolator = QSExpansionPathInterpolator()
@@ -148,7 +152,8 @@
qsExpansionPathInterpolator,
featureFlags,
variableDateViewControllerFactory,
- batteryMeterViewController
+ batteryMeterViewController,
+ insetsProvider
)
}
@@ -247,7 +252,7 @@
controller.init()
val captor = argumentCaptor<List<String>>()
- verify(view).onAttach(any(), any(), capture(captor))
+ verify(view).onAttach(any(), any(), capture(captor), any(), anyBoolean())
assertThat(captor.value).containsExactly(
mContext.getString(com.android.internal.R.string.status_bar_mobile)
@@ -260,7 +265,7 @@
controller.init()
val captor = argumentCaptor<List<String>>()
- verify(view).onAttach(any(), any(), capture(captor))
+ verify(view).onAttach(any(), any(), capture(captor), any(), anyBoolean())
assertThat(captor.value).containsExactly(
mContext.getString(com.android.internal.R.string.status_bar_no_calling),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index 5e1fea5..b6e8979 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -99,7 +99,8 @@
mInternetDialogController, true, true, true, mock(UiEventLogger.class), mHandler,
mBgExecutor);
mInternetDialog.mAdapter = mInternetAdapter;
- mInternetDialog.onAccessPointsChanged(mWifiEntries, mInternetWifiEntry);
+ mInternetDialog.mConnectedWifiEntry = mInternetWifiEntry;
+ mInternetDialog.mWifiEntriesCount = mWifiEntries.size();
mInternetDialog.show();
mDialogView = mInternetDialog.mDialogView;
@@ -209,7 +210,7 @@
@Test
public void updateDialog_wifiOnAndNoConnectedWifi_hideConnectedWifi() {
// The precondition WiFi ON is already in setUp()
- mInternetDialog.onAccessPointsChanged(mWifiEntries, null /* connectedEntry*/);
+ mInternetDialog.mConnectedWifiEntry = null;
doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
mInternetDialog.updateDialog(false);
@@ -220,7 +221,7 @@
@Test
public void updateDialog_wifiOnAndNoWifiList_hideWifiListAndSeeAll() {
// The precondition WiFi ON is already in setUp()
- mInternetDialog.onAccessPointsChanged(null /* wifiEntries */, mInternetWifiEntry);
+ mInternetDialog.mWifiEntriesCount = 0;
mInternetDialog.updateDialog(false);
@@ -366,7 +367,8 @@
public void showProgressBar_wifiEnabledWithoutWifiEntries_showProgressBarThenHideSearch() {
Mockito.reset(mHandler);
when(mWifiManager.isWifiEnabled()).thenReturn(true);
- mInternetDialog.onAccessPointsChanged(null /* wifiEntries */, null /* connectedEntry*/);
+ mInternetDialog.mConnectedWifiEntry = null;
+ mInternetDialog.mWifiEntriesCount = 0;
mInternetDialog.showProgressBar();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index dee6020..9bf8775 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -23,6 +23,7 @@
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -42,6 +43,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
import org.junit.Before;
import org.junit.Rule;
@@ -81,6 +83,7 @@
private AlertEntry mLastCreatedEntry;
private TestableAlertingNotificationManager() {
+ super(mock(HeadsUpManagerLogger.class));
mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
mHandler = mTestHandler;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt
index 096efad..38ad6b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt
@@ -86,6 +86,23 @@
}
@Test
+ fun getDisableFlagsString_nullOld_onlyNewStateLogged() {
+ val result = disableFlagsLogger.getDisableFlagsString(
+ old = null,
+ new = DisableFlagsLogger.DisableState(
+ 0b001, // abC
+ 0b01, // mN
+ ),
+ )
+
+ assertThat(result).doesNotContain("Old")
+ assertThat(result).contains("New: abC.mN")
+ // We have no state to diff on, so we shouldn't see any diff in parentheses
+ assertThat(result).doesNotContain("(")
+ assertThat(result).doesNotContain(")")
+ }
+
+ @Test
fun getDisableFlagsString_nullLocalModification_localModNotLogged() {
val result = disableFlagsLogger.getDisableFlagsString(
DisableFlagsLogger.DisableState(0, 0),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index dbd5168..0bce621 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -119,15 +119,17 @@
@Test
fun onPanelExpansionChanged_apliesBlur_ifShade() {
- notificationShadeDepthController.onPanelExpansionChanged(1f /* expansion */,
- false /* tracking */)
+ notificationShadeDepthController.onPanelExpansionChanged(
+ rawFraction = 1f, expanded = true, tracking = false
+ )
verify(shadeAnimation).animateTo(eq(maxBlur), any())
}
@Test
fun onPanelExpansionChanged_animatesBlurIn_ifShade() {
- notificationShadeDepthController.onPanelExpansionChanged(0.01f /* expansion */,
- false /* tracking */)
+ notificationShadeDepthController.onPanelExpansionChanged(
+ rawFraction = 0.01f, expanded = false, tracking = false
+ )
verify(shadeAnimation).animateTo(eq(maxBlur), any())
}
@@ -135,8 +137,9 @@
fun onPanelExpansionChanged_animatesBlurOut_ifShade() {
onPanelExpansionChanged_animatesBlurIn_ifShade()
clearInvocations(shadeAnimation)
- notificationShadeDepthController.onPanelExpansionChanged(0f /* expansion */,
- false /* tracking */)
+ notificationShadeDepthController.onPanelExpansionChanged(
+ rawFraction = 0f, expanded = false, tracking = false
+ )
verify(shadeAnimation).animateTo(eq(0), any())
}
@@ -144,16 +147,19 @@
fun onPanelExpansionChanged_animatesBlurOut_ifFlick() {
onPanelExpansionChanged_apliesBlur_ifShade()
clearInvocations(shadeAnimation)
- notificationShadeDepthController.onPanelExpansionChanged(1f /* expansion */,
- true /* tracking */)
+ notificationShadeDepthController.onPanelExpansionChanged(
+ rawFraction = 1f, expanded = true, tracking = true
+ )
verify(shadeAnimation, never()).animateTo(anyInt(), any())
- notificationShadeDepthController.onPanelExpansionChanged(0.9f /* expansion */,
- true /* tracking */)
+ notificationShadeDepthController.onPanelExpansionChanged(
+ rawFraction = 0.9f, expanded = true, tracking = true
+ )
verify(shadeAnimation, never()).animateTo(anyInt(), any())
- notificationShadeDepthController.onPanelExpansionChanged(0.8f /* expansion */,
- false /* tracking */)
+ notificationShadeDepthController.onPanelExpansionChanged(
+ rawFraction = 0.8f, expanded = true, tracking = false
+ )
verify(shadeAnimation).animateTo(eq(0), any())
}
@@ -161,24 +167,28 @@
fun onPanelExpansionChanged_animatesBlurIn_ifFlickCancelled() {
onPanelExpansionChanged_animatesBlurOut_ifFlick()
clearInvocations(shadeAnimation)
- notificationShadeDepthController.onPanelExpansionChanged(0.6f /* expansion */,
- true /* tracking */)
+ notificationShadeDepthController.onPanelExpansionChanged(
+ rawFraction = 0.6f, expanded = true, tracking = true
+ )
verify(shadeAnimation).animateTo(eq(maxBlur), any())
}
@Test
fun onPanelExpansionChanged_respectsMinPanelPullDownFraction() {
notificationShadeDepthController.panelPullDownMinFraction = 0.5f
- notificationShadeDepthController.onPanelExpansionChanged(0.5f /* expansion */,
- true /* tracking */)
+ notificationShadeDepthController.onPanelExpansionChanged(
+ rawFraction = 0.5f, expanded = true, tracking = true
+ )
assertThat(notificationShadeDepthController.shadeExpansion).isEqualTo(0f)
- notificationShadeDepthController.onPanelExpansionChanged(0.75f /* expansion */,
- true /* tracking */)
+ notificationShadeDepthController.onPanelExpansionChanged(
+ rawFraction = 0.75f, expanded = true, tracking = true
+ )
assertThat(notificationShadeDepthController.shadeExpansion).isEqualTo(0.5f)
- notificationShadeDepthController.onPanelExpansionChanged(1f /* expansion */,
- true /* tracking */)
+ notificationShadeDepthController.onPanelExpansionChanged(
+ rawFraction = 1f, expanded = true, tracking = true
+ )
assertThat(notificationShadeDepthController.shadeExpansion).isEqualTo(1f)
}
@@ -196,7 +206,9 @@
fun setQsPanelExpansion_appliesBlur() {
statusBarState = StatusBarState.KEYGUARD
notificationShadeDepthController.qsPanelExpansion = 1f
- notificationShadeDepthController.onPanelExpansionChanged(1f, tracking = false)
+ notificationShadeDepthController.onPanelExpansionChanged(
+ rawFraction = 1f, expanded = true, tracking = false
+ )
notificationShadeDepthController.updateBlurCallback.doFrame(0)
verify(blurUtils).applyBlur(any(), eq(maxBlur), eq(false))
}
@@ -205,7 +217,9 @@
fun setQsPanelExpansion_easing() {
statusBarState = StatusBarState.KEYGUARD
notificationShadeDepthController.qsPanelExpansion = 0.25f
- notificationShadeDepthController.onPanelExpansionChanged(1f, tracking = false)
+ notificationShadeDepthController.onPanelExpansionChanged(
+ rawFraction = 1f, expanded = true, tracking = false
+ )
notificationShadeDepthController.updateBlurCallback.doFrame(0)
verify(wallpaperController).setNotificationShadeZoom(
eq(ShadeInterpolation.getNotificationScrimAlpha(0.25f)))
@@ -261,7 +275,9 @@
@Test
fun updateBlurCallback_setsBlur_whenExpanded() {
- notificationShadeDepthController.onPanelExpansionChanged(1f, false)
+ notificationShadeDepthController.onPanelExpansionChanged(
+ rawFraction = 1f, expanded = true, tracking = false
+ )
`when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat())
notificationShadeDepthController.updateBlurCallback.doFrame(0)
verify(blurUtils).applyBlur(any(), eq(maxBlur), eq(false))
@@ -269,7 +285,9 @@
@Test
fun updateBlurCallback_ignoreShadeBlurUntilHidden_overridesZoom() {
- notificationShadeDepthController.onPanelExpansionChanged(1f, false)
+ notificationShadeDepthController.onPanelExpansionChanged(
+ rawFraction = 1f, expanded = true, tracking = false
+ )
`when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat())
notificationShadeDepthController.blursDisabledForAppLaunch = true
notificationShadeDepthController.updateBlurCallback.doFrame(0)
@@ -300,7 +318,9 @@
// Brightness mirror is fully visible
`when`(brightnessSpring.ratio).thenReturn(1f)
// And shade is blurred
- notificationShadeDepthController.onPanelExpansionChanged(1f, false)
+ notificationShadeDepthController.onPanelExpansionChanged(
+ rawFraction = 1f, expanded = true, tracking = false
+ )
`when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat())
notificationShadeDepthController.updateBlurCallback.doFrame(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
index 3dc29a1..fc94262 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
@@ -56,6 +56,7 @@
/* If set, use this creation time instead of mClock.uptimeMillis */
private long mCreationTime = -1;
+ private int mStableIndex = -1;
public NotificationEntryBuilder() {
mSbnBuilder = new SbnBuilder();
@@ -102,6 +103,7 @@
/* ListEntry properties */
entry.setParent(mParent);
+ entry.getAttachState().setStableIndex(mStableIndex);
return entry;
}
@@ -122,6 +124,11 @@
return this;
}
+ public NotificationEntryBuilder setStableIndex(int index) {
+ mStableIndex = index;
+ return this;
+ }
+
/**
* Set the creation time
*/
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 190c352..913ffd4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -36,6 +36,7 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import android.os.SystemClock;
@@ -107,6 +108,7 @@
private List<NotificationEntryBuilder> mPendingSet = new ArrayList<>();
private List<NotificationEntry> mEntrySet = new ArrayList<>();
private List<ListEntry> mBuiltList;
+ private TestableStabilityManager mStabilityManager;
private Map<String, Integer> mNextIdMap = new ArrayMap<>();
private int mNextRank = 0;
@@ -122,6 +124,9 @@
mListBuilder.attach(mNotifCollection);
+ mStabilityManager = new TestableStabilityManager();
+ mListBuilder.setNotifStabilityManager(mStabilityManager);
+
Mockito.verify(mNotifCollection).setBuildListener(mBuildListenerCaptor.capture());
mReadyForBuildListener = Objects.requireNonNull(mBuildListenerCaptor.getValue());
}
@@ -622,7 +627,7 @@
validChildren.set(entries.size() == 2);
}
});
- mListBuilder.setSectioners(Arrays.asList(pkg1Sectioner));
+ mListBuilder.setSectioners(asList(pkg1Sectioner));
addNotif(0, PACKAGE_4);
addNotif(1, PACKAGE_1);
@@ -647,7 +652,7 @@
final NotifSectioner pkg4Sectioner = spy(new PackageSectioner(PACKAGE_4));
final NotifSectioner pkg5Sectioner = spy(new PackageSectioner(PACKAGE_5));
mListBuilder.setSectioners(
- Arrays.asList(pkg1Sectioner, pkg2Sectioner, pkg4Sectioner, pkg5Sectioner));
+ asList(pkg1Sectioner, pkg2Sectioner, pkg4Sectioner, pkg5Sectioner));
final NotifSection pkg1Section = new NotifSection(pkg1Sectioner, 0);
final NotifSection pkg2Section = new NotifSection(pkg2Sectioner, 1);
@@ -759,7 +764,7 @@
@Test
public void testThatNotifComparatorsAreCalled() {
// GIVEN a set of comparators that care about specific packages
- mListBuilder.setComparators(Arrays.asList(
+ mListBuilder.setComparators(asList(
new HypeComparator(PACKAGE_4),
new HypeComparator(PACKAGE_1, PACKAGE_3),
new HypeComparator(PACKAGE_2)
@@ -943,7 +948,7 @@
// THEN all the new notifs, including the new GroupEntry, are passed to the listener
assertEquals(
- Arrays.asList(
+ asList(
mEntrySet.get(0),
mBuiltList.get(1),
mEntrySet.get(4)),
@@ -989,7 +994,7 @@
// THEN all the new notifs, including the new GroupEntry, are passed to the listener
assertEquals(
- Arrays.asList(
+ asList(
mEntrySet.get(0),
mBuiltList.get(2),
mEntrySet.get(7),
@@ -1006,8 +1011,7 @@
dispatchBuild();
// GIVEN visual stability manager doesn't allow any group changes
- mListBuilder.setNotifStabilityManager(
- new TestableStabilityManager().setAllowGroupChanges(false));
+ mStabilityManager.setAllowGroupChanges(false);
// WHEN we run the pipeline with the addition of a group summary & child
addGroupSummary(1, PACKAGE_1, GROUP_1);
@@ -1026,8 +1030,7 @@
@Test
public void testStabilizeGroupsAllowsGroupingAllNewNotifications() {
// GIVEN visual stability manager doesn't allow any group changes
- mListBuilder.setNotifStabilityManager(
- new TestableStabilityManager().setAllowGroupChanges(false));
+ mStabilityManager.setAllowGroupChanges(false);
// WHEN we run the pipeline with all new notification groups
addGroupChild(0, PACKAGE_1, GROUP_1);
@@ -1063,8 +1066,7 @@
dispatchBuild();
// GIVEN visual stability manager doesn't allow any group changes
- mListBuilder.setNotifStabilityManager(
- new TestableStabilityManager().setAllowGroupChanges(false));
+ mStabilityManager.setAllowGroupChanges(false);
// WHEN we run the pipeline with the addition of a group summary & child
addGroupSummary(1, PACKAGE_1, GROUP_1);
@@ -1117,8 +1119,7 @@
dispatchBuild(); // group summary is hidden because it needs at least 2 children to group
// GIVEN visual stability manager doesn't allow any group changes
- mListBuilder.setNotifStabilityManager(
- new TestableStabilityManager().setAllowGroupChanges(false));
+ mStabilityManager.setAllowGroupChanges(false);
// WHEN we run the pipeline with the addition of a child
addGroupChild(2, PACKAGE_1, GROUP_1);
@@ -1144,9 +1145,7 @@
dispatchBuild();
// GIVEN visual stability manager doesn't allow any group changes
- final TestableStabilityManager stabilityManager =
- new TestableStabilityManager().setAllowGroupChanges(false);
- mListBuilder.setNotifStabilityManager(stabilityManager);
+ mStabilityManager.setAllowGroupChanges(false);
// WHEN the delayed summary is posted
addGroupSummary(4, PACKAGE_1, GROUP_1);
@@ -1163,8 +1162,8 @@
);
// WHEN visual stability manager allows group changes again
- stabilityManager.setAllowGroupChanges(true);
- stabilityManager.invalidateList();
+ mStabilityManager.setAllowGroupChanges(true);
+ mStabilityManager.invalidateList();
// THEN entries are grouped
verifyBuiltList(
@@ -1190,9 +1189,7 @@
assertEquals(originalSectioner, mEntrySet.get(0).getSection().getSectioner());
// WHEN section changes aren't allowed
- final TestableStabilityManager stabilityManager =
- new TestableStabilityManager().setAllowSectionChanges(false);
- mListBuilder.setNotifStabilityManager(stabilityManager);
+ mStabilityManager.setAllowSectionChanges(false);
// WHEN we try to change the section
final NotifSectioner newSectioner = new PackageSectioner(PACKAGE_1);
@@ -1203,8 +1200,8 @@
assertEquals(originalSectioner, mEntrySet.get(0).getSection().getSectioner());
// WHEN section changes are allowed again
- stabilityManager.setAllowSectionChanges(true);
- stabilityManager.invalidateList();
+ mStabilityManager.setAllowSectionChanges(true);
+ mStabilityManager.invalidateList();
// THEN the section updates
assertEquals(newSectioner, mEntrySet.get(0).getSection().getSectioner());
@@ -1228,7 +1225,7 @@
// THEN all the new notifs are passed to the listener out of order
assertEquals(
- Arrays.asList(
+ asList(
mEntrySet.get(0),
mEntrySet.get(1),
mEntrySet.get(2)),
@@ -1260,7 +1257,7 @@
// THEN all the new notifs are passed to the listener
assertEquals(
- Arrays.asList(
+ asList(
mEntrySet.get(0),
mEntrySet.get(1),
mEntrySet.get(2)),
@@ -1362,6 +1359,43 @@
}
@Test
+ public void testStableOrdering() {
+ mStabilityManager.setAllowEntryReordering(false);
+ assertOrder("ABCDEFG", "ACDEFXBG", "XABCDEFG"); // X
+ assertOrder("ABCDEFG", "ACDEFBG", "ABCDEFG"); // no change
+ assertOrder("ABCDEFG", "ACDEFBXZG", "XZABCDEFG"); // Z and X
+ assertOrder("ABCDEFG", "AXCDEZFBG", "XZABCDEFG"); // Z and X + gap
+ }
+
+ @Test
+ public void testActiveOrdering() {
+ assertOrder("ABCDEFG", "ACDEFXBG", "ACDEFXBG"); // X
+ assertOrder("ABCDEFG", "ACDEFBG", "ACDEFBG"); // no change
+ assertOrder("ABCDEFG", "ACDEFBXZG", "ACDEFBXZG"); // Z and X
+ assertOrder("ABCDEFG", "AXCDEZFBG", "AXCDEZFBG"); // Z and X + gap
+ }
+
+ @Test
+ public void testStableMultipleSectionOrdering() {
+ mListBuilder.setSectioners(asList(
+ new PackageSectioner(PACKAGE_1), new PackageSectioner(PACKAGE_2)));
+ mStabilityManager.setAllowEntryReordering(false);
+
+ addNotif(0, PACKAGE_1).setRank(1);
+ addNotif(1, PACKAGE_1).setRank(2);
+ addNotif(2, PACKAGE_2).setRank(0);
+ addNotif(3, PACKAGE_1).setRank(3);
+ dispatchBuild();
+
+ verifyBuiltList(
+ notif(0),
+ notif(1),
+ notif(3),
+ notif(2)
+ );
+ }
+
+ @Test
public void testInOrderPreRenderFilter() {
// GIVEN a PreRenderFilter that gets invalidated during the grouping stage
NotifFilter filter = new PackageFilter(PACKAGE_5);
@@ -1443,6 +1477,37 @@
return addGroupChildWithTag(index, packageId, groupId, null);
}
+ private void assertOrder(String visible, String active, String expected) {
+ StringBuilder differenceSb = new StringBuilder();
+ for (char c : active.toCharArray()) {
+ if (visible.indexOf(c) < 0) differenceSb.append(c);
+ }
+ String difference = differenceSb.toString();
+
+ for (int i = 0; i < visible.length(); i++) {
+ addNotif(i, String.valueOf(visible.charAt(i)))
+ .setRank(active.indexOf(visible.charAt(i)))
+ .setStableIndex(i);
+
+ }
+
+ for (int i = 0; i < difference.length(); i++) {
+ addNotif(i + visible.length(), String.valueOf(difference.charAt(i)))
+ .setRank(active.indexOf(difference.charAt(i)))
+ .setStableIndex(-1);
+ }
+
+ dispatchBuild();
+ StringBuilder resultSb = new StringBuilder();
+ for (int i = 0; i < expected.length(); i++) {
+ resultSb.append(mBuiltList.get(i).getRepresentativeEntry().getSbn().getPackageName());
+ }
+
+ assertEquals("visible [" + visible + "] active [" + active + "]",
+ expected, resultSb.toString());
+ mEntrySet.clear();
+ }
+
private int nextId(String packageName) {
Integer nextId = mNextIdMap.get(packageName);
if (nextId == null) {
@@ -1632,7 +1697,7 @@
IdPromoter(Integer... ids) {
super("IdPromoter");
- mIds = Arrays.asList(ids);
+ mIds = asList(ids);
}
@Override
@@ -1648,7 +1713,7 @@
HypeComparator(String ...preferredPackages) {
super("HypeComparator");
- mPreferredPackages = Arrays.asList(preferredPackages);
+ mPreferredPackages = asList(preferredPackages);
}
@Override
@@ -1710,6 +1775,7 @@
private static class TestableStabilityManager extends NotifStabilityManager {
boolean mAllowGroupChanges = true;
boolean mAllowSectionChanges = true;
+ boolean mAllowEntryReodering = true;
TestableStabilityManager() {
super("Test");
@@ -1725,6 +1791,12 @@
return this;
}
+ TestableStabilityManager setAllowEntryReordering(boolean allowSectionChanges) {
+ mAllowEntryReodering = allowSectionChanges;
+ return this;
+ }
+
+
@Override
public void onBeginRun() {
}
@@ -1738,6 +1810,11 @@
public boolean isSectionChangeAllowed(NotificationEntry entry) {
return mAllowSectionChanges;
}
+
+ @Override
+ public boolean isEntryReorderingAllowed(ListEntry entry) {
+ return mAllowEntryReodering;
+ }
}
private static final String PACKAGE_1 = "com.test1";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
index 8f241a3..2091cf8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
@@ -22,11 +22,13 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
+import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
@@ -55,6 +57,7 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
import java.util.Arrays;
@SmallTest
@@ -67,31 +70,41 @@
@Mock private NodeController mAlertingHeaderController;
@Mock private NodeController mSilentNodeController;
@Mock private SectionHeaderController mSilentHeaderController;
+ @Mock private NotificationListenerService.Ranking mRanking;
+ @Mock private StatusBarNotification mSbn;
@Captor private ArgumentCaptor<NotifFilter> mNotifFilterCaptor;
private NotificationEntry mEntry;
private NotifFilter mCapturedSuspendedFilter;
private NotifFilter mCapturedDozingFilter;
+ private RankingCoordinator mRankingCoordinator;
private NotifSectioner mAlertingSectioner;
private NotifSectioner mSilentSectioner;
+ private NotifSectioner mMinimizedSectioner;
+ private ArrayList<NotifSectioner> mSections = new ArrayList<>(3);
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- RankingCoordinator rankingCoordinator = new RankingCoordinator(
+ mRankingCoordinator = new RankingCoordinator(
mStatusBarStateController, mHighPriorityProvider, mAlertingHeaderController,
mSilentHeaderController, mSilentNodeController);
- mEntry = new NotificationEntryBuilder().build();
+ mEntry = spy(new NotificationEntryBuilder().build());
+ mRanking = spy(getRankingForUnfilteredNotif().build());
+ mEntry.setRanking(mRanking);
+ when(mEntry.getSbn()).thenReturn(mSbn);
- rankingCoordinator.attach(mNotifPipeline);
+ mRankingCoordinator.attach(mNotifPipeline);
verify(mNotifPipeline, times(2)).addPreGroupFilter(mNotifFilterCaptor.capture());
mCapturedSuspendedFilter = mNotifFilterCaptor.getAllValues().get(0);
mCapturedDozingFilter = mNotifFilterCaptor.getAllValues().get(1);
- mAlertingSectioner = rankingCoordinator.getAlertingSectioner();
- mSilentSectioner = rankingCoordinator.getSilentSectioner();
+ mAlertingSectioner = mRankingCoordinator.getAlertingSectioner();
+ mSilentSectioner = mRankingCoordinator.getSilentSectioner();
+ mMinimizedSectioner = mRankingCoordinator.getMinimizedSectioner();
+ mSections.addAll(Arrays.asList(mAlertingSectioner, mSilentSectioner, mMinimizedSectioner));
}
@Test
@@ -109,8 +122,9 @@
};
Mockito.doReturn(true).when(sbn).isClearable();
mSilentSectioner.onEntriesUpdated(Arrays.asList(listEntry));
+ when(mRanking.isAmbient()).thenReturn(false);
verify(mSilentHeaderController).setClearSectionEnabled(eq(true));
-
+ mRankingCoordinator.resetClearAllFlags();
Mockito.doReturn(false).when(sbn).isClearable();
mSilentSectioner.onEntriesUpdated(Arrays.asList(listEntry));
verify(mSilentHeaderController).setClearSectionEnabled(eq(false));
@@ -190,12 +204,78 @@
public void testIncludeInSectionSilent() {
// GIVEN the entry isn't high priority
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
+ when(mRanking.isAmbient()).thenReturn(false);
// THEN entry is in the silent section
assertFalse(mAlertingSectioner.isInSection(mEntry));
assertTrue(mSilentSectioner.isInSection(mEntry));
}
+ @Test
+ public void testMinSection() {
+ when(mEntry.getRanking()).thenReturn(mRanking);
+ when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
+ when(mRanking.isAmbient()).thenReturn(true);
+ assertInSection(mEntry, mMinimizedSectioner);
+ }
+
+ @Test
+ public void testSilentSection() {
+ when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
+ when(mRanking.isAmbient()).thenReturn(false);
+ assertInSection(mEntry, mSilentSectioner);
+ }
+
+ @Test
+ public void testClearableSilentSection() {
+ when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
+ when(mSbn.isClearable()).thenReturn(true);
+ when(mRanking.isAmbient()).thenReturn(false);
+ mSilentSectioner.onEntriesUpdated(Arrays.asList(mEntry));
+ verify(mSilentHeaderController).setClearSectionEnabled(eq(true));
+ }
+
+ @Test
+ public void testClearableMinimizedSection() {
+ when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
+ when(mSbn.isClearable()).thenReturn(true);
+ when(mRanking.isAmbient()).thenReturn(true);
+ mMinimizedSectioner.onEntriesUpdated(Arrays.asList(mEntry));
+ verify(mSilentHeaderController).setClearSectionEnabled(eq(true));
+ }
+
+ @Test
+ public void testNotClearableSilentSection() {
+ when(mSbn.isClearable()).thenReturn(false);
+ when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
+ when(mRanking.isAmbient()).thenReturn(false);
+ mSilentSectioner.onEntriesUpdated(Arrays.asList(mEntry));
+ mMinimizedSectioner.onEntriesUpdated(Arrays.asList(mEntry));
+ mAlertingSectioner.onEntriesUpdated(Arrays.asList(mEntry));
+ verify(mSilentHeaderController, times(2)).setClearSectionEnabled(eq(false));
+ }
+
+ @Test
+ public void testNotClearableMinimizedSection() {
+ when(mSbn.isClearable()).thenReturn(false);
+ when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
+ when(mRanking.isAmbient()).thenReturn(true);
+ mSilentSectioner.onEntriesUpdated(Arrays.asList(mEntry));
+ mMinimizedSectioner.onEntriesUpdated(Arrays.asList(mEntry));
+ mAlertingSectioner.onEntriesUpdated(Arrays.asList(mEntry));
+ verify(mSilentHeaderController, times(2)).setClearSectionEnabled(eq(false));
+ }
+
+ private void assertInSection(NotificationEntry entry, NotifSectioner section) {
+ for (NotifSectioner current: mSections) {
+ if (current == section) {
+ assertTrue(current.isInSection(entry));
+ } else {
+ assertFalse(current.isInSection(entry));
+ }
+ }
+ }
+
private RankingBuilder getRankingForUnfilteredNotif() {
return new RankingBuilder()
.setKey(mEntry.getKey())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index d3738f4..ed42ac3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -87,6 +87,7 @@
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
import com.android.systemui.statusbar.policy.SmartReplyStateInflater;
+import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.time.FakeSystemClock;
@@ -251,6 +252,7 @@
new ExpandableNotificationRowController(
viewCaptor.getValue(),
mListContainer,
+ mock(RemoteInputViewSubcomponent.Factory.class),
mock(ActivatableNotificationViewController.class),
mNotificationMediaManager,
mock(PluginManager.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 42aecfd..f3eece8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -68,9 +68,11 @@
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
import com.android.systemui.statusbar.policy.SmartReplyStateInflater;
+import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
import com.android.systemui.tests.R;
import com.android.systemui.wmshell.BubblesManager;
import com.android.systemui.wmshell.BubblesTestActivity;
@@ -129,9 +131,14 @@
Optional.of((mock(Bubbles.class))),
mock(DumpManager.class));
mGroupExpansionManager = mGroupMembershipManager;
- mHeadsUpManager = new HeadsUpManagerPhone(mContext, mStatusBarStateController,
- mock(KeyguardBypassController.class), mock(NotificationGroupManagerLegacy.class),
- mock(ConfigurationControllerImpl.class));
+ mHeadsUpManager = new HeadsUpManagerPhone(
+ mContext,
+ mock(HeadsUpManagerLogger.class),
+ mStatusBarStateController,
+ mock(KeyguardBypassController.class),
+ mock(NotificationGroupManagerLegacy.class),
+ mock(ConfigurationControllerImpl.class)
+ );
mGroupMembershipManager.setHeadsUpManager(mHeadsUpManager);
mIconManager = new IconManager(
mock(CommonNotifCollection.class),
@@ -255,6 +262,22 @@
}
/**
+ * Returns an {@link ExpandableNotificationRow} that should be shown as a bubble.
+ */
+ public ExpandableNotificationRow createShortcutBubble(String shortcutId)
+ throws Exception {
+ Notification n = createNotification(false /* isGroupSummary */,
+ null /* groupKey */, makeShortcutBubbleMetadata(shortcutId));
+ n.flags |= FLAG_BUBBLE;
+ ExpandableNotificationRow row = generateRow(n, PKG, UID, USER_HANDLE,
+ 0 /* extraInflationFlags */, IMPORTANCE_HIGH);
+ modifyRanking(row.getEntry())
+ .setCanBubble(true)
+ .build();
+ return row;
+ }
+
+ /**
* Returns an {@link ExpandableNotificationRow} that should be shown as a bubble and is part
* of a group of notifications.
*/
@@ -457,6 +480,7 @@
row.initialize(
entry,
+ mock(RemoteInputViewSubcomponent.Factory.class),
APP_NAME,
entry.getKey(),
mock(ExpansionLogger.class),
@@ -506,6 +530,12 @@
.build();
}
+ private BubbleMetadata makeShortcutBubbleMetadata(String shortcutId) {
+ return new BubbleMetadata.Builder(shortcutId)
+ .setDesiredHeight(314)
+ .build();
+ }
+
private static class MockSmartReplyInflater implements SmartReplyStateInflater {
@Override
public InflatedSmartReplyState inflateSmartReplyState(NotificationEntry entry) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLoggerTest.kt
index f3136c7..bf8cc37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLoggerTest.kt
@@ -40,7 +40,7 @@
private val logger = CollapsedStatusBarFragmentLogger(buffer, disableFlagsLogger)
@Test
- fun logToBuffer_bufferHasStates() {
+ fun logDisableFlagChange_bufferHasStates() {
val state = DisableFlagsLogger.DisableState(0, 1)
logger.logDisableFlagChange(state, state)
@@ -48,7 +48,9 @@
val stringWriter = StringWriter()
buffer.dump(PrintWriter(stringWriter), tailLength = 0)
val actualString = stringWriter.toString()
- val expectedLogString = disableFlagsLogger.getDisableFlagsString(state, state)
+ val expectedLogString = disableFlagsLogger.getDisableFlagsString(
+ old = null, new = state, newAfterLocalModification = state
+ )
assertThat(actualString).contains(expectedLogString)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index be54a6a..e4c4c63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -48,6 +48,7 @@
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
@@ -261,6 +262,7 @@
mAnimationScheduler,
mLocationPublisher,
mMockNotificationAreaController,
+ new PanelExpansionStateManager(),
mock(FeatureFlags.class),
mStatusBarIconController,
mKeyguardStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
index 1ce336e..34c43ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
@@ -28,6 +28,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import org.junit.Before;
import org.junit.Test;
@@ -44,12 +45,15 @@
private DozeParameters mDozeParameters;
@Mock
private DozeLog mDozeLog;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
private DozeScrimController mDozeScrimController;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mDozeScrimController = new DozeScrimController(mDozeParameters, mDozeLog);
+ mDozeScrimController = new DozeScrimController(mDozeParameters, mDozeLog,
+ mStatusBarStateController);
mDozeScrimController.setDozing(true);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 83b6d2c..0f419c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -39,6 +39,7 @@
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
import org.junit.Before;
import org.junit.Ignore;
@@ -57,6 +58,7 @@
private HeadsUpManagerPhone mHeadsUpManager;
+ @Mock private HeadsUpManagerLogger mHeadsUpManagerLogger;
@Mock private NotificationGroupManagerLegacy mGroupManager;
@Mock private View mNotificationShadeWindowView;
@Mock private VisualStabilityManager mVSManager;
@@ -69,14 +71,21 @@
private final class TestableHeadsUpManagerPhone extends HeadsUpManagerPhone {
TestableHeadsUpManagerPhone(
Context context,
+ HeadsUpManagerLogger headsUpManagerLogger,
NotificationGroupManagerLegacy groupManager,
VisualStabilityManager vsManager,
StatusBarStateController statusBarStateController,
KeyguardBypassController keyguardBypassController,
ConfigurationController configurationController
) {
- super(context, statusBarStateController, keyguardBypassController,
- groupManager, configurationController);
+ super(
+ context,
+ headsUpManagerLogger,
+ statusBarStateController,
+ keyguardBypassController,
+ groupManager,
+ configurationController
+ );
setup(vsManager);
mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
@@ -96,8 +105,15 @@
when(mVSManager.isReorderingAllowed()).thenReturn(true);
mDependency.injectMockDependency(NotificationShadeWindowController.class);
mDependency.injectMockDependency(ConfigurationController.class);
- mHeadsUpManager = new TestableHeadsUpManagerPhone(mContext, mGroupManager, mVSManager,
- mStatusBarStateController, mBypassController, mConfigurationController);
+ mHeadsUpManager = new TestableHeadsUpManagerPhone(
+ mContext,
+ mHeadsUpManagerLogger,
+ mGroupManager,
+ mVSManager,
+ mStatusBarStateController,
+ mBypassController,
+ mConfigurationController
+ );
super.setUp();
mHeadsUpManager.mHandler = mTestHandler;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index faf968b..8d05e66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -85,6 +85,8 @@
private BiometricUnlockController mBiometricUnlockController;
@Mock
private SysuiStatusBarStateController mStatusBarStateController;
+ @Mock
+ private StatusBarContentInsetsProvider mStatusBarContentInsetsProvider;
private TestNotificationPanelViewStateProvider mNotificationPanelViewStateProvider;
private KeyguardStatusBarView mKeyguardStatusBarView;
@@ -118,7 +120,8 @@
mKeyguardBypassController,
mKeyguardUpdateMonitor,
mBiometricUnlockController,
- mStatusBarStateController
+ mStatusBarStateController,
+ mStatusBarContentInsetsProvider
);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index 80d9c08..b717d28 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -48,6 +48,7 @@
import com.android.systemui.statusbar.notification.row.RowContentBindParams;
import com.android.systemui.statusbar.notification.row.RowContentBindStage;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
import com.android.wm.shell.bubbles.Bubbles;
import org.junit.Before;
@@ -86,7 +87,7 @@
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mHeadsUpManager = new HeadsUpManager(mContext) {};
+ mHeadsUpManager = new HeadsUpManager(mContext, mock(HeadsUpManagerLogger.class)) {};
when(mNotificationEntryManager.getPendingNotificationsIterator())
.thenReturn(mPendingEntries.values());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
index 25aa93a..bd4efdb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
@@ -33,6 +33,7 @@
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.wm.shell.bubbles.Bubbles;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 747acdf..fbab075 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -131,11 +131,13 @@
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
@@ -152,6 +154,7 @@
import java.lang.ref.WeakReference;
import java.util.List;
+import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -186,8 +189,6 @@
@Mock
private HeadsUpTouchHelper.Callback mHeadsUpCallback;
@Mock
- private PanelBar mPanelBar;
- @Mock
private KeyguardUpdateMonitor mUpdateMonitor;
@Mock
private KeyguardBypassController mKeyguardBypassController;
@@ -347,7 +348,7 @@
private DumpManager mDumpManager;
@Mock
private NotificationsQSContainerController mNotificationsQSContainerController;
-
+ private Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty();
private SysuiStatusBarStateController mStatusBarStateController;
private NotificationPanelViewController mNotificationPanelViewController;
private View.AccessibilityDelegate mAccessibiltyDelegate;
@@ -511,14 +512,15 @@
mSplitShadeHeaderController,
mUnlockedScreenOffAnimationController,
mLockscreenGestureLogger,
+ new PanelExpansionStateManager(),
mNotificationRemoteInputManager,
+ mSysUIUnfoldComponent,
mControlsComponent);
mNotificationPanelViewController.initDependencies(
mStatusBar,
() -> {},
mNotificationShelfController);
mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
- mNotificationPanelViewController.setBar(mPanelBar);
mNotificationPanelViewController.setKeyguardIndicationController(
mKeyguardIndicationController);
ArgumentCaptor<View.OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index 6e9bb2d..1adba6e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -52,7 +52,9 @@
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.tuner.TunerService;
import org.junit.Before;
@@ -90,7 +92,7 @@
@Mock private NotificationPanelViewController mNotificationPanelViewController;
@Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
@Mock private NotificationShadeDepthController mNotificationShadeDepthController;
- @Mock private StatusBarWindowView mStatusBarWindowView;
+ @Mock private StatusBarWindowController mStatusBarWindowController;
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -135,7 +137,8 @@
mNotificationShadeDepthController,
mView,
mNotificationPanelViewController,
- mStatusBarWindowView,
+ new PanelExpansionStateManager(),
+ mStatusBarWindowController,
mNotificationStackScrollLayoutController,
mStatusBarKeyguardViewManager,
mLockIconViewController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 310a8ba..eea8eb9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -26,6 +26,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.unfold.SysUIUnfoldComponent
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.util.mockito.any
@@ -39,6 +40,7 @@
import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import java.util.Optional
@SmallTest
class PhoneStatusBarViewControllerTest : SysuiTestCase() {
@@ -51,10 +53,11 @@
private lateinit var panelView: ViewGroup
@Mock
private lateinit var scrimController: ScrimController
-
@Mock
private lateinit var moveFromCenterAnimation: StatusBarMoveFromCenterAnimationController
@Mock
+ private lateinit var sysuiUnfoldComponent: SysUIUnfoldComponent
+ @Mock
private lateinit var progressProvider: ScopedUnfoldTransitionProgressProvider
private lateinit var view: PhoneStatusBarView
@@ -66,7 +69,8 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
`when`(panelViewController.view).thenReturn(panelView)
-
+ `when`(sysuiUnfoldComponent.getStatusBarMoveFromCenterAnimationController())
+ .thenReturn(moveFromCenterAnimation)
// create the view on main thread as it requires main looper
InstrumentationRegistry.getInstrumentation().runOnMainSync {
val parent = FrameLayout(mContext) // add parent to keep layout params
@@ -112,9 +116,8 @@
private fun createController(view: PhoneStatusBarView): PhoneStatusBarViewController {
return PhoneStatusBarViewController.Factory(
- { progressProvider },
- { moveFromCenterAnimation },
- unfoldConfig
+ Optional.of(sysuiUnfoldComponent),
+ Optional.of(progressProvider)
).create(view, touchEventHandler)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
index fe34903..e8ad5fd3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -54,59 +54,6 @@
}
@Test
- fun panelExpansionChanged_expansionChangeListenerNotified() {
- val listener = TestExpansionChangedListener()
- view.setExpansionChangedListeners(listOf(listener))
- val fraction = 0.4f
- val isExpanded = true
-
- view.panelExpansionChanged(fraction, isExpanded)
-
- assertThat(listener.fraction).isEqualTo(fraction)
- assertThat(listener.isExpanded).isEqualTo(isExpanded)
- }
-
- @Test
- fun panelExpansionChanged_noListeners_noCrash() {
- view.panelExpansionChanged(1f, false)
- // No assert needed, just testing no crash
- }
-
- @Test
- fun panelStateChanged_toStateOpening_listenerNotified() {
- val listener = TestStateChangedListener()
- view.setPanelStateChangeListener(listener)
-
- view.panelExpansionChanged(0.5f, true)
-
- assertThat(listener.state).isEqualTo(PanelBar.STATE_OPENING)
- }
-
- @Test
- fun panelStateChanged_toStateOpen_listenerNotified() {
- val listener = TestStateChangedListener()
- view.setPanelStateChangeListener(listener)
-
- view.panelExpansionChanged(1f, true)
-
- assertThat(listener.state).isEqualTo(PanelBar.STATE_OPEN)
- }
-
- @Test
- fun panelStateChanged_toStateClosed_listenerNotified() {
- val listener = TestStateChangedListener()
- view.setPanelStateChangeListener(listener)
-
- // First, open the panel
- view.panelExpansionChanged(1f, true)
-
- // Then, close it again
- view.panelExpansionChanged(0f, false)
-
- assertThat(listener.state).isEqualTo(PanelBar.STATE_CLOSED)
- }
-
- @Test
fun onTouchEvent_listenerNotified() {
val handler = TestTouchEventHandler()
view.setTouchEventHandler(handler)
@@ -145,24 +92,6 @@
// No assert needed, just testing no crash
}
- private class TestExpansionChangedListener
- : StatusBar.ExpansionChangedListener {
- var fraction: Float = 0f
- var isExpanded: Boolean = false
-
- override fun onExpansionChanged(expansion: Float, expanded: Boolean) {
- this.fraction = expansion
- this.isExpanded = expanded
- }
- }
-
- private class TestStateChangedListener : PanelBar.PanelStateChangeListener {
- var state: Int = 0
- override fun onStateChanged(state: Int) {
- this.state = state
- }
- }
-
private class TestTouchEventHandler : PhoneStatusBarView.TouchEventHandler {
var lastEvent: MotionEvent? = null
var returnValue: Boolean = false
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 6849dab..42f2206 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -55,6 +55,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
import com.android.systemui.scrim.ScrimView;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -112,6 +113,10 @@
private ConfigurationController mConfigurationController;
@Mock
private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
+ // TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The
+ // event-dispatch-on-registration pattern caused some of these unit tests to fail.)
+ @Mock
+ private PanelExpansionStateManager mPanelExpansionStateManager;
private static class AnimatorListener implements Animator.AnimatorListener {
@@ -224,7 +229,8 @@
mDozeParameters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder,
new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor,
mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()),
- mUnlockedScreenOffAnimationController);
+ mUnlockedScreenOffAnimationController,
+ mPanelExpansionStateManager);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
mScrimController.setAnimatorListener(mAnimatorListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
index a9e8164..4f68a3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
@@ -1,8 +1,7 @@
package com.android.systemui.statusbar.phone
-import android.content.Context
-import android.content.res.Resources
import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
import android.view.View
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
@@ -15,14 +14,16 @@
import org.junit.Before
import org.junit.Rule
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
-import org.mockito.Mockito.`when` as whenever
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
@SmallTest
+@RunWith(AndroidTestingRunner::class)
class SplitShadeHeaderControllerTest : SysuiTestCase() {
@Mock private lateinit var view: View
@@ -33,8 +34,6 @@
@Mock private lateinit var featureFlags: FeatureFlags
@Mock private lateinit var batteryMeterView: BatteryMeterView
@Mock private lateinit var batteryMeterViewController: BatteryMeterViewController
- @Mock private lateinit var resources: Resources
- @Mock private lateinit var context: Context
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
var viewVisibility = View.GONE
@@ -45,8 +44,8 @@
whenever<BatteryMeterView>(view.findViewById(R.id.batteryRemainingIcon))
.thenReturn(batteryMeterView)
whenever<StatusIconContainer>(view.findViewById(R.id.statusIcons)).thenReturn(statusIcons)
+ whenever(view.context).thenReturn(context)
whenever(statusIcons.context).thenReturn(context)
- whenever(context.resources).thenReturn(resources)
whenever(qsCarrierGroupControllerBuilder.setQSCarrierGroup(any()))
.thenReturn(qsCarrierGroupControllerBuilder)
whenever(qsCarrierGroupControllerBuilder.build()).thenReturn(qsCarrierGroupController)
@@ -55,6 +54,7 @@
null
}
whenever(view.visibility).thenAnswer { _ -> viewVisibility }
+ whenever(featureFlags.useCombinedQSHeaders()).thenReturn(false)
splitShadeHeaderController = SplitShadeHeaderController(view, statusBarIconController,
qsCarrierGroupControllerBuilder, featureFlags, batteryMeterViewController)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
index e5158e7..e86676b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
@@ -467,7 +467,7 @@
screenBounds = Rect(0, 0, 1080, 2160),
displayUniqueId = "1"
)
- val firstDisplayInsets = provider.getStatusBarContentInsetsForRotation(ROTATION_NONE)
+ val firstDisplayInsets = provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
givenDisplay(
screenBounds = Rect(0, 0, 800, 600),
displayUniqueId = "2"
@@ -475,7 +475,7 @@
configurationController.onConfigurationChanged(configuration)
// WHEN: get insets on the second display
- val secondDisplayInsets = provider.getStatusBarContentInsetsForRotation(ROTATION_NONE)
+ val secondDisplayInsets = provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
// THEN: insets are updated
assertThat(firstDisplayInsets).isNotEqualTo(secondDisplayInsets)
@@ -492,13 +492,13 @@
displayUniqueId = "1"
)
val firstDisplayInsetsFirstCall = provider
- .getStatusBarContentInsetsForRotation(ROTATION_NONE)
+ .getStatusBarContentAreaForRotation(ROTATION_NONE)
givenDisplay(
screenBounds = Rect(0, 0, 800, 600),
displayUniqueId = "2"
)
configurationController.onConfigurationChanged(configuration)
- provider.getStatusBarContentInsetsForRotation(ROTATION_NONE)
+ provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
givenDisplay(
screenBounds = Rect(0, 0, 1080, 2160),
displayUniqueId = "1"
@@ -507,7 +507,7 @@
// WHEN: get insets on the first display again
val firstDisplayInsetsSecondCall = provider
- .getStatusBarContentInsetsForRotation(ROTATION_NONE)
+ .getStatusBarContentAreaForRotation(ROTATION_NONE)
// THEN: insets for the first and second calls for the first display are the same
assertThat(firstDisplayInsetsFirstCall).isEqualTo(firstDisplayInsetsSecondCall)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 2d944aa..dcffd22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -51,6 +51,7 @@
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -137,9 +138,13 @@
mUnlockedScreenOffAnimationController,
mKeyguardMessageAreaFactory,
mShadeController);
- mStatusBarKeyguardViewManager.registerStatusBar(mStatusBar,
- mNotificationPanelView, mBiometrucUnlockController,
- mNotificationContainer, mBypassController);
+ mStatusBarKeyguardViewManager.registerStatusBar(
+ mStatusBar,
+ mNotificationPanelView,
+ new PanelExpansionStateManager(),
+ mBiometrucUnlockController,
+ mNotificationContainer,
+ mBypassController);
mStatusBarKeyguardViewManager.show(null);
}
@@ -179,8 +184,10 @@
public void onPanelExpansionChanged_neverHidesFullscreenBouncer() {
// TODO: StatusBar should not be here, mBouncer.isFullscreenBouncer() should do the same.
when(mStatusBar.isFullScreenUserSwitcherState()).thenReturn(true);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(0.5f /* expansion */,
- true /* tracking */);
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(
+ /* fraction= */ 0.5f,
+ /* expanded= */ false,
+ /* tracking= */ true);
verify(mBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_VISIBLE));
}
@@ -188,51 +195,67 @@
public void onPanelExpansionChanged_neverHidesScrimmedBouncer() {
when(mBouncer.isShowing()).thenReturn(true);
when(mBouncer.isScrimmed()).thenReturn(true);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(0.5f /* expansion */,
- true /* tracking */);
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(
+ /* fraction= */ 0.5f,
+ /* expanded= */ false,
+ /* tracking= */ true);
verify(mBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_VISIBLE));
}
@Test
public void onPanelExpansionChanged_neverShowsDuringHintAnimation() {
when(mNotificationPanelView.isUnlockHintRunning()).thenReturn(true);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(0.5f /* expansion */,
- true /* tracking */);
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(
+ /* fraction= */ 0.5f,
+ /* expanded= */ false,
+ /* tracking= */ true);
verify(mBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_HIDDEN));
}
@Test
public void onPanelExpansionChanged_propagatesToBouncer() {
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(0.5f /* expansion */,
- true /* tracking */);
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(
+ /* fraction= */ 0.5f,
+ /* expanded= */ false,
+ /* tracking= */ true);
verify(mBouncer).setExpansion(eq(0.5f));
}
@Test
public void onPanelExpansionChanged_showsBouncerWhenSwiping() {
when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(0.5f /* expansion */,
- true /* tracking */);
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(
+ /* fraction= */ 0.5f,
+ /* expanded= */ false,
+ /* tracking= */ true);
verify(mBouncer).show(eq(false), eq(false));
// But not when it's already visible
reset(mBouncer);
when(mBouncer.isShowing()).thenReturn(true);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(0.5f /* expansion */, true /* tracking */);
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(
+ /* fraction= */ 0.5f,
+ /* expanded= */ false,
+ /* tracking= */ true);
verify(mBouncer, never()).show(eq(false), eq(false));
// Or animating away
reset(mBouncer);
when(mBouncer.isAnimatingAway()).thenReturn(true);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(0.5f /* expansion */, true /* tracking */);
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(
+ /* fraction= */ 0.5f,
+ /* expanded= */ false,
+ /* tracking= */ true);
verify(mBouncer, never()).show(eq(false), eq(false));
}
@Test
public void onPanelExpansionChanged_neverTranslatesBouncerWhenOccluded() {
mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animate */);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(0.5f /* expansion */,
- true /* tracking */);
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(
+ /* fraction= */ 0.5f,
+ /* expanded= */ false,
+ /* tracking= */ true);
verify(mBouncer, never()).setExpansion(eq(0.5f));
}
@@ -240,16 +263,20 @@
public void onPanelExpansionChanged_neverTranslatesBouncerWhenWakeAndUnlock() {
when(mBiometrucUnlockController.getMode())
.thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(KeyguardBouncer.EXPANSION_VISIBLE,
- false /* tracking */);
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(
+ /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+ /* expanded= */ true,
+ /* tracking= */ false);
verify(mBouncer, never()).setExpansion(anyFloat());
}
@Test
public void onPanelExpansionChanged_neverTranslatesBouncerWhenLaunchingApp() {
when(mStatusBar.isInLaunchTransition()).thenReturn(true);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(KeyguardBouncer.EXPANSION_VISIBLE,
- false /* tracking */);
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(
+ /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+ /* expanded= */ true,
+ /* tracking= */ false);
verify(mBouncer, never()).setExpansion(anyFloat());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index f14b126..7a93d03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -78,7 +78,6 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityLaunchAnimator;
-import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollectorFake;
@@ -133,6 +132,7 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -141,11 +141,8 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.tuner.TunerService;
-import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
-import com.android.systemui.unfold.UnfoldTransitionWallpaperController;
-import com.android.systemui.unfold.config.UnfoldTransitionConfig;
-import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
import com.android.systemui.util.WallpaperController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.MessageRouterImpl;
@@ -224,6 +221,7 @@
@Mock private KeyguardBypassController mKeyguardBypassController;
@Mock private DynamicPrivacyController mDynamicPrivacyController;
@Mock private AutoHideController mAutoHideController;
+ @Mock private StatusBarWindowController mStatusBarWindowController;
@Mock private NotificationViewHierarchyManager mNotificationViewHierarchyManager;
@Mock private UserSwitcherController mUserSwitcherController;
@Mock private NetworkController mNetworkController;
@@ -245,7 +243,6 @@
@Mock private StatusBarComponent mStatusBarComponent;
@Mock private PluginManager mPluginManager;
@Mock private LegacySplitScreen mLegacySplitScreen;
- @Mock private StatusBarWindowView mStatusBarWindowView;
@Mock private LightsOutNotifController mLightsOutNotifController;
@Mock private ViewMediatorCallback mViewMediatorCallback;
@Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
@@ -260,10 +257,6 @@
@Mock private DemoModeController mDemoModeController;
@Mock private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
@Mock private BrightnessSliderController.Factory mBrightnessSliderFactory;
- @Mock private UnfoldTransitionConfig mUnfoldTransitionConfig;
- @Mock private Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealOverlayAnimationLazy;
- @Mock private Lazy<NaturalRotationUnfoldProgressProvider> mNaturalRotationProgressProvider;
- @Mock private Lazy<UnfoldTransitionWallpaperController> mUnfoldWallpaperController;
@Mock private WallpaperController mWallpaperController;
@Mock private OngoingCallController mOngoingCallController;
@Mock private SystemStatusAnimationScheduler mAnimationScheduler;
@@ -281,7 +274,6 @@
@Mock private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
@Mock private PhoneStatusBarViewController.Factory mPhoneStatusBarViewControllerFactory;
@Mock private ActivityLaunchAnimator mActivityLaunchAnimator;
- @Mock private DialogLaunchAnimator mDialogLaunchAnimator;
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
@@ -368,6 +360,7 @@
mNotificationsController,
mLightBarController,
mAutoHideController,
+ mStatusBarWindowController,
mKeyguardUpdateMonitor,
mPulseExpansionHandler,
mNotificationWakeUpCoordinator,
@@ -385,6 +378,7 @@
notificationLogger,
mNotificationInterruptStateProvider,
mNotificationViewHierarchyManager,
+ new PanelExpansionStateManager(),
mKeyguardViewMediator,
new DisplayMetrics(),
mMetricsLogger,
@@ -424,7 +418,6 @@
mLightsOutNotifController,
mStatusBarNotificationActivityStarterBuilder,
mShadeController,
- mStatusBarWindowView,
mStatusBarKeyguardViewManager,
mViewMediatorCallback,
mInitController,
@@ -442,10 +435,6 @@
mStatusBarTouchableRegionManager,
mNotificationIconAreaController,
mBrightnessSliderFactory,
- mUnfoldTransitionConfig,
- mUnfoldLightRevealOverlayAnimationLazy,
- mUnfoldWallpaperController,
- mNaturalRotationProgressProvider,
mWallpaperController,
mOngoingCallController,
mAnimationScheduler,
@@ -462,11 +451,14 @@
Optional.of(mStartingSurface),
mTunerService,
mock(DumpManager.class),
- mActivityLaunchAnimator,
- mDialogLaunchAnimator);
- when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class),
- any(NotificationPanelViewController.class), any(BiometricUnlockController.class),
- any(ViewGroup.class), any(KeyguardBypassController.class)))
+ mActivityLaunchAnimator);
+ when(mKeyguardViewMediator.registerStatusBar(
+ any(StatusBar.class),
+ any(NotificationPanelViewController.class),
+ any(PanelExpansionStateManager.class),
+ any(BiometricUnlockController.class),
+ any(ViewGroup.class),
+ any(KeyguardBypassController.class)))
.thenReturn(mStatusBarKeyguardViewManager);
when(mKeyguardViewMediator.getViewMediatorCallback()).thenReturn(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index 3d2ff47..b385b7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -42,7 +42,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
-import com.android.systemui.statusbar.phone.StatusBarWindowController
+import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.time.FakeSystemClock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManagerTest.kt
new file mode 100644
index 0000000..32bad5c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManagerTest.kt
@@ -0,0 +1,213 @@
+/*
+ * 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.systemui.statusbar.phone.panelstate
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+
+@SmallTest
+class PanelExpansionStateManagerTest : SysuiTestCase() {
+
+ private lateinit var panelExpansionStateManager: PanelExpansionStateManager
+
+ @Before
+ fun setUp() {
+ panelExpansionStateManager = PanelExpansionStateManager()
+ }
+
+ @Test
+ fun onPanelExpansionChanged_listenerNotified() {
+ val listener = TestPanelExpansionListener()
+ panelExpansionStateManager.addExpansionListener(listener)
+ val fraction = 0.6f
+ val expanded = true
+ val tracking = true
+
+ panelExpansionStateManager.onPanelExpansionChanged(fraction, expanded, tracking)
+
+ assertThat(listener.fraction).isEqualTo(fraction)
+ assertThat(listener.expanded).isEqualTo(expanded)
+ assertThat(listener.tracking).isEqualTo(tracking)
+ }
+
+ @Test
+ fun addExpansionListener_listenerNotifiedOfCurrentValues() {
+ val fraction = 0.6f
+ val expanded = true
+ val tracking = true
+ panelExpansionStateManager.onPanelExpansionChanged(fraction, expanded, tracking)
+ val listener = TestPanelExpansionListener()
+
+ panelExpansionStateManager.addExpansionListener(listener)
+
+ assertThat(listener.fraction).isEqualTo(fraction)
+ assertThat(listener.expanded).isEqualTo(expanded)
+ assertThat(listener.tracking).isEqualTo(tracking)
+ }
+
+ @Test
+ fun updateState_listenerNotified() {
+ val listener = TestPanelStateListener()
+ panelExpansionStateManager.addStateListener(listener)
+
+ panelExpansionStateManager.updateState(STATE_OPEN)
+
+ assertThat(listener.state).isEqualTo(STATE_OPEN)
+ }
+
+ /* ***** [PanelExpansionStateManager.onPanelExpansionChanged] test cases *******/
+
+ /* Fraction < 1 test cases */
+
+ @Test
+ fun onPEC_fractionLessThanOne_expandedTrue_trackingFalse_becomesStateOpening() {
+ val listener = TestPanelStateListener()
+ panelExpansionStateManager.addStateListener(listener)
+
+ panelExpansionStateManager.onPanelExpansionChanged(
+ fraction = 0.5f, expanded = true, tracking = false
+ )
+
+ assertThat(listener.state).isEqualTo(STATE_OPENING)
+ }
+
+ @Test
+ fun onPEC_fractionLessThanOne_expandedTrue_trackingTrue_becomesStateOpening() {
+ val listener = TestPanelStateListener()
+ panelExpansionStateManager.addStateListener(listener)
+
+ panelExpansionStateManager.onPanelExpansionChanged(
+ fraction = 0.5f, expanded = true, tracking = true
+ )
+
+ assertThat(listener.state).isEqualTo(STATE_OPENING)
+ }
+
+ @Test
+ fun onPEC_fractionLessThanOne_expandedFalse_trackingFalse_becomesStateClosed() {
+ val listener = TestPanelStateListener()
+ panelExpansionStateManager.addStateListener(listener)
+ // Start out on a different state
+ panelExpansionStateManager.updateState(STATE_OPEN)
+
+ panelExpansionStateManager.onPanelExpansionChanged(
+ fraction = 0.5f, expanded = false, tracking = false
+ )
+
+ assertThat(listener.state).isEqualTo(STATE_CLOSED)
+ }
+
+ @Test
+ fun onPEC_fractionLessThanOne_expandedFalse_trackingTrue_doesNotBecomeStateClosed() {
+ val listener = TestPanelStateListener()
+ panelExpansionStateManager.addStateListener(listener)
+ // Start out on a different state
+ panelExpansionStateManager.updateState(STATE_OPEN)
+
+ panelExpansionStateManager.onPanelExpansionChanged(
+ fraction = 0.5f, expanded = false, tracking = true
+ )
+
+ assertThat(listener.state).isEqualTo(STATE_OPEN)
+ }
+
+ /* Fraction = 1 test cases */
+
+ @Test
+ fun onPEC_fractionOne_expandedTrue_trackingFalse_becomesStateOpeningThenStateOpen() {
+ val listener = TestPanelStateListener()
+ panelExpansionStateManager.addStateListener(listener)
+
+ panelExpansionStateManager.onPanelExpansionChanged(
+ fraction = 1f, expanded = true, tracking = false
+ )
+
+ assertThat(listener.previousState).isEqualTo(STATE_OPENING)
+ assertThat(listener.state).isEqualTo(STATE_OPEN)
+ }
+
+ @Test
+ fun onPEC_fractionOne_expandedTrue_trackingTrue_becomesStateOpening() {
+ val listener = TestPanelStateListener()
+ panelExpansionStateManager.addStateListener(listener)
+
+ panelExpansionStateManager.onPanelExpansionChanged(
+ fraction = 1f, expanded = true, tracking = true
+ )
+
+ assertThat(listener.state).isEqualTo(STATE_OPENING)
+ }
+
+ @Test
+ fun onPEC_fractionOne_expandedFalse_trackingFalse_becomesStateClosed() {
+ val listener = TestPanelStateListener()
+ panelExpansionStateManager.addStateListener(listener)
+ // Start out on a different state
+ panelExpansionStateManager.updateState(STATE_OPEN)
+
+ panelExpansionStateManager.onPanelExpansionChanged(
+ fraction = 1f, expanded = false, tracking = false
+ )
+
+ assertThat(listener.state).isEqualTo(STATE_CLOSED)
+ }
+
+ @Test
+ fun onPEC_fractionOne_expandedFalse_trackingTrue_doesNotBecomeStateClosed() {
+ val listener = TestPanelStateListener()
+ panelExpansionStateManager.addStateListener(listener)
+ // Start out on a different state
+ panelExpansionStateManager.updateState(STATE_OPEN)
+
+ panelExpansionStateManager.onPanelExpansionChanged(
+ fraction = 1f, expanded = false, tracking = true
+ )
+
+ assertThat(listener.state).isEqualTo(STATE_OPEN)
+ }
+
+ /* ***** end [PanelExpansionStateManager.onPanelExpansionChanged] test cases ******/
+
+ class TestPanelExpansionListener : PanelExpansionListener {
+ var fraction: Float = 0f
+ var expanded: Boolean = false
+ var tracking: Boolean = false
+
+ override fun onPanelExpansionChanged(
+ fraction: Float,
+ expanded: Boolean,
+ tracking: Boolean
+ ) {
+ this.fraction = fraction
+ this.expanded = expanded
+ this.tracking = tracking
+ }
+ }
+
+ class TestPanelStateListener : PanelStateListener {
+ @PanelState var previousState: Int = STATE_CLOSED
+ @PanelState var state: Int = STATE_CLOSED
+
+ override fun onPanelStateChanged(state: Int) {
+ this.previousState = this.state
+ this.state = state
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
index b53cbf7..5e852e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
@@ -60,7 +60,7 @@
private final class TestableHeadsUpManager extends HeadsUpManager {
TestableHeadsUpManager(Context context) {
- super(context);
+ super(context, mock(HeadsUpManagerLogger.class));
mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 1159e09..15a92dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -185,6 +185,8 @@
private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
@Captor
private ArgumentCaptor<NotificationRemoveInterceptor> mRemoveInterceptorCaptor;
+ @Captor
+ private ArgumentCaptor<List<Bubble>> mBubbleListCaptor;
private BubblesManager mBubblesManager;
// TODO(178618782): Move tests on the controller directly to the shell
@@ -1165,6 +1167,22 @@
verify(mDataRepository, times(1)).loadBubbles(anyInt(), any());
}
+ /**
+ * Verifies that shortcut deletions triggers that bubble being removed from XML.
+ */
+ @Test
+ public void testDeleteShortcutsDeletesXml() throws Exception {
+ ExpandableNotificationRow row = mNotificationTestHelper.createShortcutBubble("shortcutId");
+ BubbleEntry shortcutBubbleEntry = BubblesManager.notifToBubbleEntry(row.getEntry());
+ mBubbleController.updateBubble(shortcutBubbleEntry);
+
+ mBubbleData.dismissBubbleWithKey(shortcutBubbleEntry.getKey(),
+ Bubbles.DISMISS_SHORTCUT_REMOVED);
+
+ verify(mDataRepository, atLeastOnce()).removeBubbles(anyInt(), mBubbleListCaptor.capture());
+ assertThat(mBubbleListCaptor.getValue().get(0).getKey()).isEqualTo(
+ shortcutBubbleEntry.getKey());
+ }
/**
* Verifies that the package manager for the user is used when loading info for the bubble.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
index 05c4822..43b181e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -93,6 +93,7 @@
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
+import com.android.wm.shell.bubbles.Bubble;
import com.android.wm.shell.bubbles.BubbleData;
import com.android.wm.shell.bubbles.BubbleDataRepository;
import com.android.wm.shell.bubbles.BubbleEntry;
@@ -167,6 +168,9 @@
@Captor
private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor;
+ @Captor
+ private ArgumentCaptor<List<Bubble>> mBubbleListCaptor;
+
private BubblesManager mBubblesManager;
private TestableBubbleController mBubbleController;
private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
@@ -1013,6 +1017,23 @@
verify(mDataRepository, times(1)).loadBubbles(anyInt(), any());
}
+ /**
+ * Verifies that shortcut deletions triggers that bubble being removed from XML.
+ */
+ @Test
+ public void testDeleteShortcutsDeletesXml() throws Exception {
+ ExpandableNotificationRow row = mNotificationTestHelper.createShortcutBubble("shortcutId");
+ BubbleEntry shortcutBubbleEntry = BubblesManager.notifToBubbleEntry(row.getEntry());
+ mBubbleController.updateBubble(shortcutBubbleEntry);
+
+ mBubbleData.dismissBubbleWithKey(shortcutBubbleEntry.getKey(),
+ Bubbles.DISMISS_SHORTCUT_REMOVED);
+
+ verify(mDataRepository, atLeastOnce()).removeBubbles(anyInt(), mBubbleListCaptor.capture());
+ assertThat(mBubbleListCaptor.getValue().get(0).getKey()).isEqualTo(
+ shortcutBubbleEntry.getKey());
+ }
+
@Test
public void testShowManageMenuChangesSysuiState() {
mBubbleController.updateBubble(mBubbleEntry);
diff --git a/packages/services/CameraExtensionsProxy/AndroidManifest.xml b/packages/services/CameraExtensionsProxy/AndroidManifest.xml
index ef1d581..79c9d13 100644
--- a/packages/services/CameraExtensionsProxy/AndroidManifest.xml
+++ b/packages/services/CameraExtensionsProxy/AndroidManifest.xml
@@ -2,6 +2,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cameraextensions">
+ <queries>
+ <intent>
+ <action android:name="androidx.camera.extensions.action.VENDOR_ACTION" />
+ </intent>
+ </queries>
+
<application
android:label="@string/app_name"
android:defaultToDeviceProtectedStorage="true"
diff --git a/proto/src/criticalevents/critical_event_log.proto b/proto/src/criticalevents/critical_event_log.proto
index 27787c2..0e03434 100644
--- a/proto/src/criticalevents/critical_event_log.proto
+++ b/proto/src/criticalevents/critical_event_log.proto
@@ -56,6 +56,7 @@
oneof event {
Watchdog watchdog = 2;
HalfWatchdog half_watchdog = 3;
+ AppNotResponding anr = 4;
}
message Watchdog {
@@ -75,4 +76,34 @@
// Required.
optional string subject = 1;
}
+
+ message AppNotResponding {
+ // The ANR subject.
+ // Optional, may be redacted for privacy.
+ optional string subject = 1;
+
+ // Name of the ANRing process.
+ // Optional, may be redacted for privacy.
+ optional string process = 2;
+
+ // PID of the ANRing process.
+ // Required.
+ optional int32 pid = 3;
+
+ // UID of the ANRing process.
+ // Required.
+ optional int32 uid = 4;
+
+ // Category of the ANRing process (DATA_APP, SYSTEM_APP, etc).
+ // Required.
+ optional ProcessClass process_class = 5;
+ }
+
+ // Mirrors definition & values in {@link android.server.ServerProtoEnums}.
+ enum ProcessClass {
+ PROCESS_CLASS_UNKNOWN = 0;
+ DATA_APP = 1;
+ SYSTEM_APP = 2;
+ SYSTEM_SERVER = 3;
+ }
}
\ No newline at end of file
diff --git a/proto/src/task_snapshot.proto b/proto/src/task_snapshot.proto
index 4b0e4c2..1cbc17e 100644
--- a/proto/src/task_snapshot.proto
+++ b/proto/src/task_snapshot.proto
@@ -41,4 +41,8 @@
// The task height when the snapshot was taken
int32 task_height = 15;
int32 appearance = 16;
+ int32 letterbox_inset_left = 17;
+ int32 letterbox_inset_top = 18;
+ int32 letterbox_inset_right = 19;
+ int32 letterbox_inset_bottom = 20;
}
diff --git a/rs/jni/Android.bp b/rs/jni/Android.bp
new file mode 100644
index 0000000..9a6fa8e
--- /dev/null
+++ b/rs/jni/Android.bp
@@ -0,0 +1,54 @@
+//
+// 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 {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+cc_library_shared {
+ name: "librs_jni",
+
+ srcs: ["android_renderscript_RenderScript.cpp"],
+
+ shared_libs: [
+ "libandroid",
+ "libandroid_runtime",
+ "libandroidfw",
+ "libRS",
+ "libcutils",
+ "libhwui",
+ "liblog",
+ "libutils",
+ "libui",
+ "libgui",
+ "libjnigraphics",
+ ],
+
+ header_libs: [
+ "jni_headers",
+ "libbase_headers",
+ ],
+
+ include_dirs: ["frameworks/rs"],
+
+ cflags: [
+ "-Wno-unused-parameter",
+ "-Wunused",
+ "-Wunreachable-code",
+ "-Wno-deprecated-declarations",
+ ],
+}
diff --git a/rs/jni/Android.mk b/rs/jni/Android.mk
deleted file mode 100644
index 0caba42..0000000
--- a/rs/jni/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- android_renderscript_RenderScript.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- libandroid \
- libandroid_runtime \
- libandroidfw \
- libRS \
- libcutils \
- libhwui \
- liblog \
- libutils \
- libui \
- libgui \
- libjnigraphics
-
-LOCAL_HEADER_LIBRARIES := \
- jni_headers \
- libbase_headers
-
-LOCAL_C_INCLUDES += \
- frameworks/rs
-
-LOCAL_CFLAGS += -Wno-unused-parameter
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code -Wno-deprecated-declarations
-
-LOCAL_MODULE:= librs_jni
-LOCAL_LICENSE_KINDS:= SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS:= notice
-LOCAL_NOTICE_FILE:= $(LOCAL_PATH)/../../NOTICE
-LOCAL_MODULE_TAGS := optional
-LOCAL_REQUIRED_MODULES := libRS
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index acdbcb58..67bb726 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -256,6 +256,18 @@
void setGestureDetectionPassthroughRegion(int displayId, Region region);
void setTouchExplorationPassthroughRegion(int displayId, Region region);
+
+ void setServiceDetectsGesturesEnabled(int displayId, boolean mode);
+
+ void requestTouchExploration(int displayId);
+
+ void requestDragging(int displayId, int pointerId);
+
+ void requestDelegating(int displayId);
+
+ void onDoubleTap(int displayId);
+
+ void onDoubleTapAndHold(int displayId);
}
public AbstractAccessibilityServiceConnection(Context context, ComponentName componentName,
@@ -1650,7 +1662,7 @@
}
}
- private IAccessibilityServiceClient getServiceInterfaceSafely() {
+ protected IAccessibilityServiceClient getServiceInterfaceSafely() {
synchronized (mLock) {
return mServiceInterface;
}
@@ -2048,4 +2060,28 @@
protected void logTraceWM(String methodName, String params) {
mTrace.logTrace(TRACE_WM + "." + methodName, FLAGS_WINDOW_MANAGER_INTERNAL, params);
}
+
+ public void setServiceDetectsGesturesEnabled(int displayId, boolean mode) {
+ mSystemSupport.setServiceDetectsGesturesEnabled(displayId, mode);
+ }
+
+ public void requestTouchExploration(int displayId) {
+ mSystemSupport.requestTouchExploration(displayId);
+ }
+
+ public void requestDragging(int displayId, int pointerId) {
+ mSystemSupport.requestDragging(displayId, pointerId);
+ }
+
+ public void requestDelegating(int displayId) {
+ mSystemSupport.requestDelegating(displayId);
+ }
+
+ public void onDoubleTap(int displayId) {
+ mSystemSupport.onDoubleTap(displayId);
+ }
+
+ public void onDoubleTapAndHold(int displayId) {
+ mSystemSupport.onDoubleTapAndHold(displayId);
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 10cfd04..75724bf 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -893,6 +893,42 @@
}
}
+ public void setServiceDetectsGesturesEnabled(int displayId, boolean mode) {
+ if (mTouchExplorer.contains(displayId)) {
+ mTouchExplorer.get(displayId).setServiceDetectsGestures(mode);
+ }
+ }
+
+ public void requestTouchExploration(int displayId) {
+ if (mTouchExplorer.contains(displayId)) {
+ mTouchExplorer.get(displayId).requestTouchExploration();
+ }
+ }
+
+ public void requestDragging(int displayId, int pointerId) {
+ if (mTouchExplorer.contains(displayId)) {
+ mTouchExplorer.get(displayId).requestDragging(pointerId);
+ }
+ }
+
+ public void requestDelegating(int displayId) {
+ if (mTouchExplorer.contains(displayId)) {
+ mTouchExplorer.get(displayId).requestDelegating();
+ }
+ }
+
+ public void onDoubleTap(int displayId) {
+ if (mTouchExplorer.contains(displayId)) {
+ mTouchExplorer.get(displayId).onDoubleTap();
+ }
+ }
+
+ public void onDoubleTapAndHold(int displayId) {
+ if (mTouchExplorer.contains(displayId)) {
+ mTouchExplorer.get(displayId).onDoubleTapAndHold();
+ }
+ }
+
/**
* Dumps all {@link AccessibilityInputFilter}s here.
*/
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index e6f91a25f..214769b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -44,6 +44,7 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityShortcutInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
+import android.accessibilityservice.TouchInteractionController;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityOptions;
@@ -101,6 +102,7 @@
import android.view.IWindow;
import android.view.KeyEvent;
import android.view.MagnificationSpec;
+import android.view.MotionEvent;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
@@ -1198,7 +1200,7 @@
}
/**
- * Called when a gesture is detected on a display.
+ * Called when a gesture is detected on a display by the framework.
*
* @param gestureEvent the detail of the gesture.
* @return true if the event is handled.
@@ -1213,6 +1215,29 @@
}
}
+ /** Send a motion event to the service to allow it to perform gesture detection. */
+ public boolean sendMotionEventToListeningServices(MotionEvent event) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "Sending event to service: " + event);
+ }
+ return notifyMotionEvent(event);
+ }
+ }
+
+ /**
+ * Notifies services that the touch state on a given display has changed.
+ */
+ public boolean onTouchStateChanged(int displayId, int state) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "Notifying touch state:"
+ + TouchInteractionController.stateToString(state));
+ }
+ return notifyTouchState(displayId, state);
+ }
+ }
+
/**
* Called when the system action list is changed.
*/
@@ -1527,6 +1552,30 @@
return false;
}
+ private boolean notifyMotionEvent(MotionEvent event) {
+ AccessibilityUserState state = getCurrentUserStateLocked();
+ for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
+ AccessibilityServiceConnection service = state.mBoundServices.get(i);
+ if (service.mRequestTouchExplorationMode) {
+ service.notifyMotionEvent(event);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean notifyTouchState(int displayId, int touchState) {
+ AccessibilityUserState state = getCurrentUserStateLocked();
+ for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
+ AccessibilityServiceConnection service = state.mBoundServices.get(i);
+ if (service.mRequestTouchExplorationMode) {
+ service.notifyTouchState(displayId, touchState);
+ return true;
+ }
+ }
+ return false;
+ }
+
private void notifyClearAccessibilityCacheLocked() {
AccessibilityUserState state = getCurrentUserStateLocked();
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
@@ -2083,7 +2132,6 @@
if (userState.isSendMotionEventsEnabled()) {
flags |= AccessibilityInputFilter.FLAG_SEND_MOTION_EVENTS;
}
-
if (userState.isAutoclickEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_AUTOCLICK;
}
@@ -3960,6 +4008,93 @@
}
}
+ @Override
+ public void setServiceDetectsGesturesEnabled(int displayId, boolean mode) {
+ mMainHandler.sendMessage(
+ obtainMessage(AccessibilityManagerService::setServiceDetectsGesturesInternal, this,
+ displayId, mode));
+ }
+
+ private void setServiceDetectsGesturesInternal(int displayId, boolean mode) {
+ synchronized (mLock) {
+ if (mHasInputFilter && mInputFilter != null) {
+ mInputFilter.setServiceDetectsGesturesEnabled(displayId, mode);
+ }
+ }
+ }
+
+ @Override
+ public void requestTouchExploration(int displayId) {
+ mMainHandler.sendMessage(obtainMessage(
+ AccessibilityManagerService::requestTouchExplorationInternal, this, displayId));
+ }
+
+ private void requestTouchExplorationInternal(int displayId) {
+ synchronized (mLock) {
+ if (mHasInputFilter && mInputFilter != null) {
+ mInputFilter.requestTouchExploration(displayId);
+ }
+ }
+ }
+
+ @Override
+ public void requestDragging(int displayId, int pointerId) {
+ mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::requestDraggingInternal,
+ this, displayId, pointerId));
+ }
+
+ private void requestDraggingInternal(int displayId, int pointerId) {
+ synchronized (mLock) {
+ if (mHasInputFilter && mInputFilter != null) {
+ mInputFilter.requestDragging(displayId, pointerId);
+ }
+ }
+ }
+
+ @Override
+ public void requestDelegating(int displayId) {
+ mMainHandler.sendMessage(
+ obtainMessage(
+ AccessibilityManagerService::requestDelegatingInternal, this, displayId));
+ }
+
+ private void requestDelegatingInternal(int displayId) {
+ synchronized (mLock) {
+ if (mHasInputFilter && mInputFilter != null) {
+ mInputFilter.requestDelegating(displayId);
+ }
+ }
+ }
+
+ @Override
+ public void onDoubleTap(int displayId) {
+ mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::onDoubleTapInternal,
+ this, displayId));
+ }
+
+ private void onDoubleTapInternal(int displayId) {
+ synchronized (mLock) {
+ if (mHasInputFilter && mInputFilter != null) {
+ mInputFilter.onDoubleTap(displayId);
+ }
+ }
+ }
+
+ @Override
+ public void onDoubleTapAndHold(int displayId) {
+ mMainHandler
+ .sendMessage(obtainMessage(AccessibilityManagerService::onDoubleTapAndHoldInternal,
+ this, displayId));
+ }
+
+ private void onDoubleTapAndHoldInternal(int displayId) {
+ synchronized (mLock) {
+ if (mHasInputFilter && mInputFilter != null) {
+ mInputFilter.onDoubleTapAndHold(displayId);
+ }
+ }
+ }
+
private void updateFocusAppearanceDataLocked(AccessibilityUserState userState) {
if (userState.mUserId != mCurrentUserId) {
return;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 4bf48a2..e9f5870 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -22,6 +22,7 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
+import android.accessibilityservice.TouchInteractionController;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -30,12 +31,15 @@
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import android.view.Display;
+import android.view.MotionEvent;
+
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -454,4 +458,48 @@
mSystemSupport.onClientChangeLocked(false);
}
}
+
+ public void notifyMotionEvent(MotionEvent event) {
+ final Message msg = obtainMessage(
+ AccessibilityServiceConnection::notifyMotionEventInternal,
+ AccessibilityServiceConnection.this, event);
+ mMainHandler.sendMessage(msg);
+ }
+
+ public void notifyTouchState(int displayId, int state) {
+ final Message msg = obtainMessage(
+ AccessibilityServiceConnection::notifyTouchStateInternal,
+ AccessibilityServiceConnection.this, displayId, state);
+ mMainHandler.sendMessage(msg);
+ }
+
+ private void notifyMotionEventInternal(MotionEvent event) {
+ final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
+ if (listener != null) {
+ try {
+ if (mTrace.isA11yTracingEnabled()) {
+ logTraceSvcClient(".onMotionEvent ",
+ event.toString());
+ }
+ listener.onMotionEvent(event);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error sending motion event to" + mService, re);
+ }
+ }
+ }
+
+ private void notifyTouchStateInternal(int displayId, int state) {
+ final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
+ if (listener != null) {
+ try {
+ if (mTrace.isA11yTracingEnabled()) {
+ logTraceSvcClient(".onTouchStateChanged ",
+ TouchInteractionController.stateToString(state));
+ }
+ listener.onTouchStateChanged(displayId, state);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error sending motion event to" + mService, re);
+ }
+ }
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
index df4a52e..b6223c7 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
@@ -382,18 +382,21 @@
}
public boolean longPressWithTouchEvents(MotionEvent event, int policyFlags) {
- final int pointerIndex = event.getActionIndex();
- final int pointerId = event.getPointerId(pointerIndex);
Point clickLocation = mTempPoint;
final int result = computeClickLocation(clickLocation);
if (result == CLICK_LOCATION_NONE) {
return false;
}
- mLongPressingPointerId = pointerId;
- mLongPressingPointerDeltaX = (int) event.getX(pointerIndex) - clickLocation.x;
- mLongPressingPointerDeltaY = (int) event.getY(pointerIndex) - clickLocation.y;
- sendDownForAllNotInjectedPointers(event, policyFlags);
- return true;
+ if (event != null) {
+ final int pointerIndex = event.getActionIndex();
+ final int pointerId = event.getPointerId(pointerIndex);
+ mLongPressingPointerId = pointerId;
+ mLongPressingPointerDeltaX = (int) event.getX(pointerIndex) - clickLocation.x;
+ mLongPressingPointerDeltaY = (int) event.getY(pointerIndex) - clickLocation.y;
+ sendDownForAllNotInjectedPointers(event, policyFlags);
+ return true;
+ }
+ return false;
}
void clear() {
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index 74f0bcb..946d22e4 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -46,6 +46,7 @@
import android.os.Handler;
import android.util.DisplayMetrics;
import android.util.Slog;
+import android.view.Display;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
@@ -152,6 +153,7 @@
private Region mGestureDetectionPassthroughRegion;
private Region mTouchExplorationPassthroughRegion;
+ private int mDisplayId = Display.INVALID_DISPLAY;
/**
* Creates a new instance.
@@ -182,8 +184,9 @@
TouchExplorer(Context context, AccessibilityManagerService service, GestureManifold detector,
@NonNull Handler mainHandler) {
mContext = context;
+ mDisplayId = context.getDisplayId();
mAms = service;
- mState = new TouchState();
+ mState = new TouchState(mDisplayId, mAms);
mReceivedPointerTracker = mState.getReceivedPointerTracker();
mDispatcher = new EventDispatcher(context, mAms, super.getNext(), mState);
mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout();
@@ -267,6 +270,7 @@
}
try {
checkForMalformedEvent(event);
+ checkForMalformedEvent(rawEvent);
} catch (IllegalArgumentException e) {
Slog.e(LOG_TAG, "Ignoring malformed event: " + event.toString(), e);
return;
@@ -277,7 +281,7 @@
Slog.d(LOG_TAG, mState.toString());
}
- mState.onReceivedMotionEvent(rawEvent);
+ mState.onReceivedMotionEvent(event, rawEvent, policyFlags);
if (shouldPerformGestureDetection(event)) {
if (mGestureDetector.onMotionEvent(event, rawEvent, policyFlags)) {
// Event was handled by the gesture detector.
@@ -307,9 +311,12 @@
// It will be delivered on gesture completion or cancelation.
// Note that the delay for sending GESTURE_DETECTION_END remains in place.
mSendTouchInteractionEndDelayed.cancel();
+ if (mState.isServiceDetectingGestures()) {
+ mAms.sendMotionEventToListeningServices(rawEvent);
+ }
} else {
Slog.e(LOG_TAG, "Illegal state: " + mState);
- clear(event, policyFlags);
+ clear(event, policyFlags);
}
}
@@ -367,7 +374,7 @@
AccessibilityGestureEvent gestureEvent =
new AccessibilityGestureEvent(
AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD,
- event.getDisplayId(),
+ mDisplayId,
mGestureDetector.getMotionEvents());
dispatchGesture(gestureEvent);
}
@@ -389,7 +396,7 @@
AccessibilityGestureEvent gestureEvent =
new AccessibilityGestureEvent(
AccessibilityService.GESTURE_DOUBLE_TAP,
- event.getDisplayId(),
+ mDisplayId,
mGestureDetector.getMotionEvents());
dispatchGesture(gestureEvent);
}
@@ -404,13 +411,37 @@
if (!mAms.performActionOnAccessibilityFocusedItem(
AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK)) {
Slog.e(LOG_TAG, "ACTION_CLICK failed. Dispatching motion events to simulate click.");
-
- mDispatcher.clickWithTouchEvents(event, rawEvent, policyFlags);
+ if (event != null && rawEvent != null) {
+ mDispatcher.clickWithTouchEvents(event, rawEvent, policyFlags);
+ }
return true;
}
return true;
}
+ /**
+ * Executes a double-tap. The framework will first attempt to execute the appropriate
+ * accessibility action and if that fails, the framework will deliver touch events to the last
+ * touch-explored location.
+ */
+ public void onDoubleTap() {
+ MotionEvent event = mState.getLastReceivedEvent();
+ MotionEvent rawEvent = mState.getLastReceivedRawEvent();
+ int policyFlags = mState.getLastReceivedPolicyFlags();
+ onDoubleTap(event, rawEvent, policyFlags);
+ }
+
+ /**
+ * Executes a double-tap and hold gesture using touch events. The user can continue to move
+ * their finger around the screen to execute a drag.
+ */
+ public void onDoubleTapAndHold() {
+ MotionEvent event = mState.getLastReceivedEvent();
+ MotionEvent rawEvent = mState.getLastReceivedRawEvent();
+ int policyFlags = mState.getLastReceivedPolicyFlags();
+ onDoubleTapAndHold(event, rawEvent, policyFlags);
+ }
+
@Override
public boolean onGestureStarted() {
if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
@@ -473,7 +504,7 @@
AccessibilityGestureEvent gestureEvent =
new AccessibilityGestureEvent(
AccessibilityService.GESTURE_UNKNOWN,
- event.getDisplayId(),
+ mDisplayId,
mGestureDetector.getMotionEvents());
dispatchGesture(gestureEvent);
}
@@ -502,7 +533,6 @@
*/
private void handleActionDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
mAms.onTouchInteractionStart();
-
// If we still have not notified the user for the last
// touch, we figure out what to do. If were waiting
// we resent the delayed callback and wait again.
@@ -512,7 +542,6 @@
if (mState.isTouchExploring()) {
sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
}
-
if (mState.isClear()) {
if (!mSendHoverEnterAndMoveDelayed.isPending()) {
// Queue a delayed transition to STATE_TOUCH_EXPLORING.
@@ -521,7 +550,14 @@
// The idea is to avoid getting stuck in STATE_TOUCH_INTERACTING
final int pointerId = mReceivedPointerTracker.getPrimaryPointerId();
final int pointerIdBits = (1 << pointerId);
- mSendHoverEnterAndMoveDelayed.post(event, rawEvent, pointerIdBits, policyFlags);
+ if (mState.isServiceDetectingGestures()) {
+ // This transition will be triggered manually by the service.
+ mSendHoverEnterAndMoveDelayed.setPointerIdBits(pointerIdBits);
+ mSendHoverEnterAndMoveDelayed.setPolicyFlags(policyFlags);
+ mSendHoverEnterAndMoveDelayed.addEvent(event, rawEvent);
+ } else {
+ mSendHoverEnterAndMoveDelayed.post(event, rawEvent, pointerIdBits, policyFlags);
+ }
} else {
// Cache the event until we discern exploration from gesturing.
mSendHoverEnterAndMoveDelayed.addEvent(event, rawEvent);
@@ -548,6 +584,9 @@
// Avoid duplicated TYPE_TOUCH_INTERACTION_START event when 2nd tap of double tap.
mSendTouchInteractionEndDelayed.cancel();
}
+ if (mState.isServiceDetectingGestures()) {
+ mAms.sendMotionEventToListeningServices(rawEvent);
+ }
}
/**
@@ -571,6 +610,11 @@
case ACTION_MOVE:
handleActionMoveStateTouchInteracting(event, rawEvent, policyFlags);
break;
+ case ACTION_POINTER_UP:
+ if (mState.isServiceDetectingGestures()) {
+ mAms.sendMotionEventToListeningServices(rawEvent);
+ }
+ break;
case ACTION_UP:
handleActionUp(event, rawEvent, policyFlags);
break;
@@ -612,15 +656,16 @@
// Another finger down means that if we have not started to deliver
// hover events, we will not have to. The code for ACTION_MOVE will
// decide what we will actually do next.
-
if (mSendHoverEnterAndMoveDelayed.isPending()) {
mSendHoverEnterAndMoveDelayed.cancel();
mSendHoverExitDelayed.cancel();
} else {
- // We have already delivered at least one hover event, so send hover exit to keep the
- // stream consistent.
+ // We have already delivered at least one hover event, so send hover exit to keep
sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
}
+ if (mState.isServiceDetectingGestures()) {
+ mAms.sendMotionEventToListeningServices(rawEvent);
+ }
}
/**
@@ -632,12 +677,19 @@
final int pointerId = mReceivedPointerTracker.getPrimaryPointerId();
final int pointerIndex = event.findPointerIndex(pointerId);
int pointerIdBits = (1 << pointerId);
+ if (mState.isServiceDetectingGestures()) {
+ mAms.sendMotionEventToListeningServices(rawEvent);
+ mSendHoverEnterAndMoveDelayed.addEvent(event, rawEvent);
+ return;
+ }
switch (event.getPointerCount()) {
case 1:
// We have not started sending events since we try to
// figure out what the user is doing.
if (mSendHoverEnterAndMoveDelayed.isPending()) {
// Cache the event until we discern exploration from gesturing.
+ // When the service is detecting gestures we rely on it to fire touch
+ // exploration.
mSendHoverEnterAndMoveDelayed.addEvent(event, rawEvent);
}
break;
@@ -684,7 +736,7 @@
AccessibilityGestureEvent gestureEvent =
new AccessibilityGestureEvent(
AccessibilityService.GESTURE_PASSTHROUGH,
- event.getDisplayId(),
+ mDisplayId,
mGestureDetector.getMotionEvents());
dispatchGesture(gestureEvent);
}
@@ -708,7 +760,7 @@
AccessibilityGestureEvent gestureEvent =
new AccessibilityGestureEvent(
AccessibilityService.GESTURE_PASSTHROUGH,
- event.getDisplayId(),
+ mDisplayId,
mGestureDetector.getMotionEvents());
dispatchGesture(gestureEvent);
}
@@ -730,7 +782,7 @@
AccessibilityGestureEvent gestureEvent =
new AccessibilityGestureEvent(
AccessibilityService.GESTURE_PASSTHROUGH,
- event.getDisplayId(),
+ mDisplayId,
mGestureDetector.getMotionEvents());
dispatchGesture(gestureEvent);
}
@@ -751,7 +803,7 @@
AccessibilityGestureEvent gestureEvent =
new AccessibilityGestureEvent(
AccessibilityService.GESTURE_PASSTHROUGH,
- event.getDisplayId(),
+ mDisplayId,
mGestureDetector.getMotionEvents());
dispatchGesture(gestureEvent);
}
@@ -767,7 +819,10 @@
* Handles ACTION_UP while in the touch interacting state. This event represents all fingers
* being lifted from the screen.
*/
- private void handleActionUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ private void handleActionUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mState.isServiceDetectingGestures() && mState.isTouchInteracting()) {
+ mAms.sendMotionEventToListeningServices(rawEvent);
+ }
mAms.onTouchInteractionEnd();
final int pointerId = event.getPointerId(event.getActionIndex());
final int pointerIdBits = (1 << pointerId);
@@ -883,6 +938,10 @@
clear(event, policyFlags);
return;
case ACTION_POINTER_DOWN:
+ if (mState.isServiceDetectingGestures()) {
+ mAms.sendMotionEventToListeningServices(rawEvent);
+ return;
+ }
// We are in dragging state so we have two pointers and another one
// goes down => delegate the three pointers to the view hierarchy
mState.startDelegating();
@@ -896,6 +955,15 @@
if (mDraggingPointerId == INVALID_POINTER_ID) {
break;
}
+ if (mState.isServiceDetectingGestures()) {
+ // Allow the service to judge whether this is dragging or delegation
+ mAms.sendMotionEventToListeningServices(rawEvent);
+ computeDraggingPointerIdIfNeeded(event);
+ mDispatcher.sendMotionEvent(
+ event, ACTION_MOVE, rawEvent, pointerIdBits, policyFlags);
+ return;
+
+ }
switch (event.getPointerCount()) {
case 1:
// do nothing
@@ -921,6 +989,10 @@
}
break;
default:
+ if (mState.isServiceDetectingGestures()) {
+ mAms.sendMotionEventToListeningServices(rawEvent);
+ return;
+ }
mState.startDelegating();
mDraggingPointerId = INVALID_POINTER_ID;
event = MotionEvent.obtainNoHistory(event);
@@ -1210,9 +1282,7 @@
mTouchExplorationPassthroughRegion = region;
}
- /**
- * Whether to send the motion events that make up each gesture to the accessibility service.
- */
+ /** Whether to send the motion events that make up each gesture to the accessibility service. */
public void setSendMotionEventsEnabled(boolean mode) {
mGestureDetector.setSendMotionEventsEnabled(mode);
}
@@ -1221,7 +1291,15 @@
return mGestureDetector.isSendMotionEventsEnabled();
}
+ /** Sets whether or not motion events should be passed to the service to detect gestures. */
+ public void setServiceDetectsGestures(boolean mode) {
+ mState.setServiceDetectsGestures(mode);
+ }
+
private boolean shouldPerformGestureDetection(MotionEvent event) {
+ if (mState.isServiceDetectingGestures()) {
+ return false;
+ }
if (mState.isDelegating() || mState.isDragging()) {
return false;
}
@@ -1237,8 +1315,105 @@
}
/**
- * Class for delayed exiting from gesture detecting mode.
+ * This method allows the service to request that TouchExplorer enter the touch exploration
+ * state.
*/
+ public void requestTouchExploration() {
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "Starting touch explorer from service.");
+ }
+ if (mState.isServiceDetectingGestures() && mState.isTouchInteracting()) {
+ // Cancel without deleting events.
+ mHandler.removeCallbacks(mSendHoverEnterAndMoveDelayed);
+ mSendHoverEnterAndMoveDelayed.run();
+ mSendHoverEnterAndMoveDelayed.clear();
+ final MotionEvent prototype = mState.getLastReceivedEvent();
+ final MotionEvent rawEvent = mState.getLastReceivedRawEvent();
+ final int pointerId = mReceivedPointerTracker.getPrimaryPointerId();
+ final int pointerIdBits = (1 << pointerId);
+ final int policyFlags = mState.getLastReceivedPolicyFlags();
+ mSendHoverExitDelayed.post(prototype, rawEvent, pointerIdBits, policyFlags);
+ }
+ }
+
+ /** This method allows the service to request that TouchExplorer enter the dragging state. */
+ public void requestDragging(int pointerId) {
+ if (mState.isServiceDetectingGestures()) {
+ if (pointerId < 0 || pointerId > TouchState.MAX_POINTER_COUNT
+ || !mReceivedPointerTracker.isReceivedPointerDown(pointerId)) {
+ Slog.e(LOG_TAG, "Trying to drag with invalid pointer: " + pointerId);
+ return;
+ }
+ if (mState.isTouchExploring()) {
+ if (mSendHoverExitDelayed.isPending()) {
+ mSendHoverExitDelayed.forceSendAndRemove();
+ }
+ if (mSendTouchExplorationEndDelayed.isPending()) {
+ mSendTouchExplorationEndDelayed.forceSendAndRemove();
+ }
+ }
+ if (!mState.isTouchInteracting()) {
+ // It makes no sense to drag.
+ Slog.e(LOG_TAG, "Error: Trying to drag from "
+ + mState.getStateSymbolicName(mState.getState()));
+ return;
+ }
+ mDraggingPointerId = pointerId;
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "Drag requested on pointer " + mDraggingPointerId);
+ }
+ MotionEvent event = mState.getLastReceivedEvent();
+ MotionEvent rawEvent = mState.getLastReceivedRawEvent();
+ if (event == null || rawEvent == null) {
+ Slog.e(LOG_TAG, "Unable to start dragging: unable to get last event.");
+ return;
+ }
+ int policyFlags = mState.getLastReceivedPolicyFlags();
+ int pointerIdBits = 1 << mDraggingPointerId;
+ event.setEdgeFlags(mReceivedPointerTracker.getLastReceivedDownEdgeFlags());
+ MotionEvent downEvent = computeDownEventForDrag(event);
+ mState.startDragging();
+ if (downEvent != null) {
+ mDispatcher.sendMotionEvent(
+ downEvent, ACTION_DOWN, rawEvent, pointerIdBits, policyFlags);
+ mDispatcher.sendMotionEvent(event, ACTION_MOVE, rawEvent, pointerIdBits,
+ policyFlags);
+ } else {
+ mDispatcher.sendMotionEvent(event, ACTION_DOWN, rawEvent, pointerIdBits,
+ policyFlags);
+ }
+ }
+ }
+
+ /** This method allows the service to request that TouchExplorer enter the delegating state. */
+ public void requestDelegating() {
+ if (mState.isServiceDetectingGestures()) {
+ if (mState.isTouchExploring()) {
+ if (mSendHoverExitDelayed.isPending()) {
+ mSendHoverExitDelayed.forceSendAndRemove();
+ }
+ if (mSendTouchExplorationEndDelayed.isPending()) {
+ mSendTouchExplorationEndDelayed.forceSendAndRemove();
+ }
+ }
+ if (!mState.isTouchInteracting()) {
+ // It makes no sense to delegate.
+ Slog.e(LOG_TAG, "Error: Trying to delegate from "
+ + mState.getStateSymbolicName(mState.getState()));
+ return;
+ }
+ mState.startDelegating();
+ MotionEvent prototype = mState.getLastReceivedEvent();
+ if (prototype == null) {
+ Slog.d(LOG_TAG, "Unable to start delegating: unable to get last received event.");
+ return;
+ }
+ int policyFlags = mState.getLastReceivedPolicyFlags();
+ mDispatcher.sendDownForAllNotInjectedPointers(prototype, policyFlags);
+ }
+ }
+
+ /** Class for delayed exiting from gesture detecting mode. */
private final class ExitGestureDetectionModeDelayed implements Runnable {
public void post() {
@@ -1283,9 +1458,7 @@
}
}
- /**
- * Class for delayed sending of hover enter and move events.
- */
+ /** Class for delayed sending of hover enter and move events. */
class SendHoverEnterAndMoveDelayed implements Runnable {
private final String LOG_TAG_SEND_HOVER_DELAYED = "SendHoverEnterAndMoveDelayed";
@@ -1362,31 +1535,46 @@
}
if (!mEvents.isEmpty() && !mRawEvents.isEmpty()) {
// Deliver a down event.
- mDispatcher.sendMotionEvent(mEvents.get(0), ACTION_HOVER_ENTER,
- mRawEvents.get(0), mPointerIdBits, mPolicyFlags);
+ mDispatcher.sendMotionEvent(
+ mEvents.get(0),
+ ACTION_HOVER_ENTER,
+ mRawEvents.get(0),
+ mPointerIdBits,
+ mPolicyFlags);
if (DEBUG) {
- Slog.d(LOG_TAG_SEND_HOVER_DELAYED,
+ Slog.d(
+ LOG_TAG_SEND_HOVER_DELAYED,
"Injecting motion event: ACTION_HOVER_ENTER");
}
// Deliver move events.
final int eventCount = mEvents.size();
for (int i = 1; i < eventCount; i++) {
- mDispatcher.sendMotionEvent(mEvents.get(i), ACTION_HOVER_MOVE,
- mRawEvents.get(i), mPointerIdBits, mPolicyFlags);
+ mDispatcher.sendMotionEvent(
+ mEvents.get(i),
+ ACTION_HOVER_MOVE,
+ mRawEvents.get(i),
+ mPointerIdBits,
+ mPolicyFlags);
if (DEBUG) {
- Slog.d(LOG_TAG_SEND_HOVER_DELAYED,
+ Slog.d(
+ LOG_TAG_SEND_HOVER_DELAYED,
"Injecting motion event: ACTION_HOVER_MOVE");
}
}
}
clear();
}
- }
- /**
- * Class for delayed sending of hover exit events.
- */
+ public void setPointerIdBits(int pointerIdBits) {
+ mPointerIdBits = pointerIdBits;
+ }
+
+ public void setPolicyFlags(int policyFlags) {
+ mPolicyFlags = policyFlags;
+ }
+ }
+ /** Class for delayed sending of hover exit events. */
class SendHoverExitDelayed implements Runnable {
private final String LOG_TAG_SEND_HOVER_DELAYED = "SendHoverExitDelayed";
@@ -1438,21 +1626,18 @@
public void run() {
if (DEBUG) {
- Slog.d(LOG_TAG_SEND_HOVER_DELAYED, "Injecting motion event:"
- + " ACTION_HOVER_EXIT");
+ Slog.d(
+ LOG_TAG_SEND_HOVER_DELAYED,
+ "Injecting motion event:" + " ACTION_HOVER_EXIT");
}
mDispatcher.sendMotionEvent(
- mPrototype,
- ACTION_HOVER_EXIT,
- mRawEvent,
- mPointerIdBits,
- mPolicyFlags);
+ mPrototype, ACTION_HOVER_EXIT, mRawEvent, mPointerIdBits, mPolicyFlags);
if (!mSendTouchExplorationEndDelayed.isPending()) {
mSendTouchExplorationEndDelayed.cancel();
mSendTouchExplorationEndDelayed.post();
}
if (mSendTouchInteractionEndDelayed.isPending()) {
- mSendTouchInteractionEndDelayed.cancel();
+ mSendTouchInteractionEndDelayed.cancel();
mSendTouchInteractionEndDelayed.post();
}
clear();
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
index 6ff0826..eb71885 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
@@ -22,9 +22,12 @@
import android.annotation.IntDef;
import android.util.Slog;
+import android.view.Display;
import android.view.MotionEvent;
import android.view.accessibility.AccessibilityEvent;
+import com.android.server.accessibility.AccessibilityManagerService;
+
/**
* This class describes the state of the touch explorer as well as the state of received and
* injected pointers. This data is accessed both for purposes of touch exploration and gesture
@@ -36,7 +39,7 @@
// This constant captures the current implementation detail that
// pointer IDs are between 0 and 31 inclusive (subject to change).
// (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
- static final int MAX_POINTER_COUNT = 32;
+ public static final int MAX_POINTER_COUNT = 32;
// Constant referring to the ids bits of all pointers.
public static final int ALL_POINTER_ID_BITS = 0xFFFFFFFF;
@@ -50,8 +53,8 @@
public static final int STATE_TOUCH_EXPLORING = 2;
// the user is dragging with two fingers.
public static final int STATE_DRAGGING = 3;
- // The user is performing some other two finger gesture which we pass through to the view
- // hierarchy as a one-finger gesture e.g. two-finger scrolling.
+ // The user is performing some other two finger gesture which we pass through to the
+ // input pipeline as a one-finger gesture e.g. two-finger pinch.
public static final int STATE_DELEGATING = 4;
// The user is performing something that might be a gesture.
public static final int STATE_GESTURE_DETECTING = 5;
@@ -75,6 +78,8 @@
private MotionEvent mLastReceivedEvent;
// The accompanying raw event without any transformations.
private MotionEvent mLastReceivedRawEvent;
+ // The policy flags of the last received event.
+ int mLastReceivedPolicyFlags;
// The id of the last touch explored window.
private int mLastTouchedWindowId;
// The last injected hover event.
@@ -85,14 +90,23 @@
private long mLastInjectedDownEventTime;
// Keep track of which pointers sent to the system are down.
private int mInjectedPointersDown;
+ private boolean mServiceDetectsGestures = false;
+ // The requested mode for mServiceDetectsGestures. This will take effect on the next touch
+ // interaction.
+ private boolean mServiceDetectsGesturesRequested = false;
+ private AccessibilityManagerService mAms;
+ private int mDisplayId = Display.INVALID_DISPLAY;
- public TouchState() {
+ public TouchState(int displayId, AccessibilityManagerService ams) {
+ mDisplayId = displayId;
+ mAms = ams;
mReceivedPointerTracker = new ReceivedPointerTracker();
}
/** Clears the internal shared state. */
public void clear() {
setState(STATE_CLEAR);
+ mServiceDetectsGestures = mServiceDetectsGesturesRequested;
// Reset the pointer trackers.
if (mLastReceivedEvent != null) {
mLastReceivedEvent.recycle();
@@ -107,14 +121,19 @@
*
* @param rawEvent The raw touch event.
*/
- public void onReceivedMotionEvent(MotionEvent rawEvent) {
+ public void onReceivedMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (isClear() && event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ clear();
+ }
if (mLastReceivedEvent != null) {
mLastReceivedEvent.recycle();
}
if (mLastReceivedRawEvent != null) {
mLastReceivedRawEvent.recycle();
}
- mLastReceivedEvent = MotionEvent.obtain(rawEvent);
+ mLastReceivedEvent = MotionEvent.obtain(event);
+ mLastReceivedRawEvent = MotionEvent.obtain(rawEvent);
+ mLastReceivedPolicyFlags = policyFlags;
mReceivedPointerTracker.onMotionEvent(rawEvent);
}
@@ -123,7 +142,7 @@
*
* @param event The event to process.
*/
- void onInjectedMotionEvent(MotionEvent event) {
+ public void onInjectedMotionEvent(MotionEvent event) {
final int action = event.getActionMasked();
final int pointerId = event.getPointerId(event.getActionIndex());
final int pointerFlag = (1 << pointerId);
@@ -183,6 +202,7 @@
}
}
+ /** Updates the state in response to an injected accessibility event. */
public void onInjectedAccessibilityEvent(int type) {
// The below state transitions go here because the related events are often sent on a
// delay.
@@ -195,7 +215,8 @@
startTouchInteracting();
break;
case AccessibilityEvent.TYPE_TOUCH_INTERACTION_END:
- clear();
+ setState(STATE_CLEAR);
+ // We will clear when we actually handle the next ACTION_DOWN.
break;
case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START:
startTouchExploring();
@@ -228,6 +249,9 @@
Slog.i(LOG_TAG, getStateSymbolicName(mState) + "->" + getStateSymbolicName(state));
}
mState = state;
+ if (mServiceDetectsGestures) {
+ mAms.onTouchStateChanged(mDisplayId, state);
+ }
}
public boolean isTouchExploring() {
@@ -314,6 +338,16 @@
return mLastReceivedEvent;
}
+ /** Gets the most recently received policy flags. */
+ public int getLastReceivedPolicyFlags() {
+ return mLastReceivedPolicyFlags;
+ }
+
+ /** Gets the most recently received raw event. */
+ public MotionEvent getLastReceivedRawEvent() {
+ return mLastReceivedRawEvent;
+ }
+
/** @return The the last injected hover event. */
public MotionEvent getLastInjectedHoverEvent() {
return mLastInjectedHoverEvent;
@@ -354,6 +388,18 @@
return mLastInjectedHoverEventForClick;
}
+ public boolean isServiceDetectingGestures() {
+ return mServiceDetectsGestures;
+ }
+
+ /** Whether the service is handling gesture detection. */
+ public void setServiceDetectsGestures(boolean mode) {
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "serviceDetectsGestures: " + mode);
+ }
+ mServiceDetectsGesturesRequested = mode;
+ }
+
/** This class tracks where and when a pointer went down. It does not track its movement. */
class ReceivedPointerTracker {
private static final String LOG_TAG_RECEIVED_POINTER_TRACKER = "ReceivedPointerTracker";
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index cdce3e6..cebabcb 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -775,12 +775,14 @@
exemptFromAutoRevoke(packageInfo.packageName, packageInfo.applicationInfo.uid);
- if (mCurrentlyConnectedDevices.contains(association.getDeviceMacAddress())) {
- grantDeviceProfile(association);
- }
+ if (!association.isManagedByCompanionApp()) {
+ if (mCurrentlyConnectedDevices.contains(association.getDeviceMacAddress())) {
+ grantDeviceProfile(association);
+ }
- if (association.isNotifyOnDeviceNearby()) {
- restartBleScan();
+ if (association.isNotifyOnDeviceNearby()) {
+ restartBleScan();
+ }
}
}
@@ -1205,14 +1207,21 @@
}
void restartBleScan() {
- mBluetoothAdapter.getBluetoothLeScanner().stopScan(mBleScanCallback);
- startBleScan();
+ if (mBluetoothAdapter.getBluetoothLeScanner() != null) {
+ mBluetoothAdapter.getBluetoothLeScanner().stopScan(mBleScanCallback);
+ startBleScan();
+ } else {
+ Slog.w(LOG_TAG, "BluetoothLeScanner is null (likely BLE isn't ON yet).");
+ }
}
private List<ScanFilter> getBleScanFilters() {
ArrayList<ScanFilter> result = new ArrayList<>();
ArraySet<String> addressesSeen = new ArraySet<>();
for (AssociationInfo association : getAllAssociations()) {
+ if (association.isManagedByCompanionApp()) {
+ continue;
+ }
String address = association.getDeviceMacAddress();
if (addressesSeen.contains(address)) {
continue;
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 8a42ddf..61d784e 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -223,7 +223,7 @@
@Override // from AbstractMasterSystemService
protected ContentCapturePerUserService newServiceLocked(@UserIdInt int resolvedUserId,
boolean disabled) {
- return new ContentCapturePerUserService(this, mLock, disabled, resolvedUserId);
+ return new ContentCapturePerUserService(this, mLock, disabled, resolvedUserId, mHandler);
}
@Override // from SystemService
@@ -1072,26 +1072,18 @@
ParcelFileDescriptor sourceOut = servicePipe.second;
ParcelFileDescriptor sinkOut = servicePipe.first;
- mParentService.mPackagesWithShareRequests.add(mDataShareRequest.getPackageName());
-
- try {
- mClientAdapter.write(sourceIn);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to call write() the client operation", e);
- sendErrorSignal(mClientAdapter, serviceAdapter,
- ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN);
- logServiceEvent(
- CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_CLIENT_PIPE_FAIL);
- return;
+ synchronized (mParentService.mLock) {
+ mParentService.mPackagesWithShareRequests.add(mDataShareRequest.getPackageName());
}
- try {
- serviceAdapter.start(sinkOut);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to call start() the service operation", e);
+
+ if (!setUpSharingPipeline(mClientAdapter, serviceAdapter, sourceIn, sinkOut)) {
sendErrorSignal(mClientAdapter, serviceAdapter,
ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN);
- logServiceEvent(
- CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_SERVICE_PIPE_FAIL);
+ bestEffortCloseFileDescriptors(sourceIn, sinkIn, sourceOut, sinkOut);
+ synchronized (mParentService.mLock) {
+ mParentService.mPackagesWithShareRequests
+ .remove(mDataShareRequest.getPackageName());
+ }
return;
}
@@ -1184,6 +1176,32 @@
}
}
+ private boolean setUpSharingPipeline(
+ IDataShareWriteAdapter clientAdapter,
+ IDataShareReadAdapter serviceAdapter,
+ ParcelFileDescriptor sourceIn,
+ ParcelFileDescriptor sinkOut) {
+ try {
+ clientAdapter.write(sourceIn);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to call write() the client operation", e);
+ logServiceEvent(
+ CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_CLIENT_PIPE_FAIL);
+ return false;
+ }
+
+ try {
+ serviceAdapter.start(sinkOut);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to call start() the service operation", e);
+ logServiceEvent(
+ CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_SERVICE_PIPE_FAIL);
+ return false;
+ }
+
+ return true;
+ }
+
private void enforceDataSharingTtl(ParcelFileDescriptor sourceIn,
ParcelFileDescriptor sinkIn,
ParcelFileDescriptor sourceOut,
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 6bd1fa6..822a42b 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -46,6 +46,7 @@
import android.content.pm.ServiceInfo;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
import android.os.UserHandle;
import android.provider.Settings;
@@ -75,6 +76,7 @@
import com.android.server.infra.AbstractPerUserSystemService;
import java.io.PrintWriter;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@@ -88,6 +90,10 @@
private static final String TAG = ContentCapturePerUserService.class.getSimpleName();
+ private static final int MAX_REBIND_COUNTS = 5;
+ // 5 minutes
+ private static final long REBIND_DURATION_MS = 5 * 60 * 1_000;
+
@GuardedBy("mLock")
private final SparseArray<ContentCaptureServerSession> mSessions = new SparseArray<>();
@@ -121,11 +127,18 @@
@GuardedBy("mLock")
private ContentCaptureServiceInfo mInfo;
+ private Instant mLastRebindTime;
+ private int mRebindCount;
+ private final Handler mHandler;
+
+ private final Runnable mReBindServiceRunnable = new RebindServiceRunnable();
+
// TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's
ContentCapturePerUserService(@NonNull ContentCaptureManagerService master,
- @NonNull Object lock, boolean disabled, @UserIdInt int userId) {
+ @NonNull Object lock, boolean disabled, @UserIdInt int userId, Handler handler) {
super(master, lock, userId);
+ mHandler = handler;
updateRemoteServiceLocked(disabled);
}
@@ -190,9 +203,43 @@
Slog.w(TAG, "remote service died: " + service);
synchronized (mLock) {
mZombie = true;
- writeServiceEvent(
- FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_REMOTE_SERVICE_DIED,
- getServiceComponentName());
+ // Reset rebindCount if over 12 hours mLastRebindTime
+ if (mLastRebindTime != null && Instant.now().isAfter(
+ mLastRebindTime.plusMillis(12 * 60 * 60 * 1000))) {
+ if (mMaster.debug) {
+ Slog.i(TAG, "The current rebind count " + mRebindCount + " is reset.");
+ }
+ mRebindCount = 0;
+ }
+ if (mRebindCount >= MAX_REBIND_COUNTS) {
+ writeServiceEvent(
+ FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_REMOTE_SERVICE_DIED,
+ getServiceComponentName());
+ }
+ if (mRebindCount < MAX_REBIND_COUNTS) {
+ mHandler.removeCallbacks(mReBindServiceRunnable);
+ mHandler.postDelayed(mReBindServiceRunnable, REBIND_DURATION_MS);
+ }
+ }
+ }
+
+ private void updateRemoteServiceAndResurrectSessionsLocked() {
+ boolean disabled = !isEnabledLocked();
+ updateRemoteServiceLocked(disabled);
+ resurrectSessionsLocked();
+ }
+
+ private final class RebindServiceRunnable implements Runnable{
+
+ @Override
+ public void run() {
+ synchronized (mLock) {
+ if (mZombie) {
+ mLastRebindTime = Instant.now();
+ mRebindCount++;
+ updateRemoteServiceAndResurrectSessionsLocked();
+ }
+ }
}
}
@@ -240,8 +287,8 @@
}
void onPackageUpdatedLocked() {
- updateRemoteServiceLocked(!isEnabledLocked());
- resurrectSessionsLocked();
+ mRebindCount = 0;
+ updateRemoteServiceAndResurrectSessionsLocked();
}
@GuardedBy("mLock")
@@ -555,6 +602,8 @@
mInfo.dump(prefix2, pw);
}
pw.print(prefix); pw.print("Zombie: "); pw.println(mZombie);
+ pw.print(prefix); pw.print("Rebind count: "); pw.println(mRebindCount);
+ pw.print(prefix); pw.print("Last rebind: "); pw.println(mLastRebindTime);
if (mRemoteService != null) {
pw.print(prefix); pw.println("remote service:");
diff --git a/services/core/Android.bp b/services/core/Android.bp
index f135fbf..16343e5 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -127,7 +127,7 @@
"services.net",
"android.hardware.common-V2-java",
"android.hardware.light-V2.0-java",
- "android.hardware.gnss-V1-java",
+ "android.hardware.gnss-V2-java",
"android.hardware.power-V1-java",
"android.hardware.power-V1.0-java",
"android.hardware.vibrator-V2-java",
@@ -157,7 +157,7 @@
"android.hardware.weaver-V1.0-java",
"android.hardware.biometrics.face-V1.0-java",
"android.hardware.biometrics.fingerprint-V2.3-java",
- "android.hardware.biometrics.fingerprint-V1-java",
+ "android.hardware.biometrics.fingerprint-V2-java",
"android.hardware.oemlock-V1.0-java",
"android.hardware.configstore-V1.1-java",
"android.hardware.contexthub-V1.0-java",
diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
index b2226d1..21fc19e 100644
--- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -16,6 +16,7 @@
package android.app.usage;
+import android.annotation.CurrentTimeMillisLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -234,6 +235,10 @@
public abstract void setLastJobRunTime(String packageName, @UserIdInt int userId,
long elapsedRealtime);
+ /** Returns the estimated time that the app will be launched, in milliseconds since epoch. */
+ @CurrentTimeMillisLong
+ public abstract long getEstimatedPackageLaunchTime(String packageName, @UserIdInt int userId);
+
/**
* Returns the time in millis since a job was executed for this app, in elapsed realtime
* timebase. This value can be larger than the current elapsed realtime if the job was executed
@@ -340,4 +345,21 @@
/** Unregister a listener from being notified of every new usage event. */
public abstract void unregisterListener(@NonNull UsageEventListener listener);
+
+ /**
+ * Listener interface for estimated launch time changes.
+ */
+ public interface EstimatedLaunchTimeChangedListener {
+ /** Callback to inform listeners when estimated launch times change. */
+ void onEstimatedLaunchTimeChanged(@UserIdInt int userId, @NonNull String packageName,
+ @CurrentTimeMillisLong long newEstimatedLaunchTime);
+ }
+
+ /** Register a listener that will be notified of every estimated launch time change. */
+ public abstract void registerLaunchTimeChangedListener(
+ @NonNull EstimatedLaunchTimeChangedListener listener);
+
+ /** Unregister a listener from being notified of every estimated launch time change. */
+ public abstract void unregisterLaunchTimeChangedListener(
+ @NonNull EstimatedLaunchTimeChangedListener listener);
}
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 9e4e1a1..4ac3b55 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -51,6 +51,7 @@
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.AndroidPackageApi;
import com.android.server.pm.pkg.PackageState;
+import com.android.server.pm.pkg.PackageStateInternal;
import java.io.IOException;
import java.lang.annotation.Retention;
@@ -657,7 +658,8 @@
@Nullable
public abstract AndroidPackageApi getAndroidPackage(@NonNull String packageName);
- public abstract @Nullable PackageSetting getPackageSetting(@NonNull String packageName);
+ @Nullable
+ public abstract PackageStateInternal getPackageStateInternal(@NonNull String packageName);
public abstract @Nullable PackageState getPackageState(@NonNull String packageName);
diff --git a/services/core/java/android/content/pm/PackageSettingsSnapshotProvider.java b/services/core/java/android/content/pm/PackageSettingsSnapshotProvider.java
index b9130d7..221f172 100644
--- a/services/core/java/android/content/pm/PackageSettingsSnapshotProvider.java
+++ b/services/core/java/android/content/pm/PackageSettingsSnapshotProvider.java
@@ -21,6 +21,7 @@
import com.android.internal.util.FunctionalUtils;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.PackageSetting;
+import com.android.server.pm.pkg.PackageStateInternal;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -29,28 +30,28 @@
public interface PackageSettingsSnapshotProvider {
/**
- * Run a function block that requires access to {@link PackageSetting} data. This will
+ * Run a function block that requires access to {@link PackageStateInternal} data. This will
* ensure the {@link PackageManagerService} lock is taken before any caller's internal lock
* to avoid deadlock. Note that this method may or may not lock. If a snapshot is available
* and valid, it will iterate the snapshot set of data.
*/
void withPackageSettingsSnapshot(
- @NonNull Consumer<Function<String, PackageSetting>> block);
+ @NonNull Consumer<Function<String, PackageStateInternal>> block);
/**
* Variant which returns a value to the caller.
* @see #withPackageSettingsSnapshot(Consumer)
*/
<Output> Output withPackageSettingsSnapshotReturning(
- @NonNull FunctionalUtils.ThrowingFunction<Function<String, PackageSetting>, Output>
- block);
+ @NonNull FunctionalUtils.ThrowingFunction<Function<String, PackageStateInternal>,
+ Output> block);
/**
* Variant which throws.
* @see #withPackageSettingsSnapshot(Consumer)
*/
<ExceptionType extends Exception> void withPackageSettingsSnapshotThrowing(
- @NonNull FunctionalUtils.ThrowingCheckedConsumer<Function<String, PackageSetting>,
+ @NonNull FunctionalUtils.ThrowingCheckedConsumer<Function<String, PackageStateInternal>,
ExceptionType> block) throws ExceptionType;
/**
@@ -60,7 +61,8 @@
<ExceptionOne extends Exception, ExceptionTwo extends Exception> void
withPackageSettingsSnapshotThrowing2(
@NonNull FunctionalUtils.ThrowingChecked2Consumer<
- Function<String, PackageSetting>, ExceptionOne, ExceptionTwo> block)
+ Function<String, PackageStateInternal>,
+ ExceptionOne, ExceptionTwo> block)
throws ExceptionOne, ExceptionTwo;
/**
@@ -70,6 +72,6 @@
<Output, ExceptionType extends Exception> Output
withPackageSettingsSnapshotReturningThrowing(
@NonNull FunctionalUtils.ThrowingCheckedFunction<
- Function<String, PackageSetting>, Output, ExceptionType> block)
+ Function<String, PackageStateInternal>, Output, ExceptionType> block)
throws ExceptionType;
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index fab58e8..4775127 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -47,6 +47,7 @@
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.telecom.TelecomManager;
+import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation;
import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SrvccState;
@@ -1164,17 +1165,6 @@
remove(r.binder);
}
}
- if (events.contains(
- TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
- updateReportSignalStrengthDecision(r.subId);
- try {
- if (mSignalStrength[r.phoneId] != null) {
- r.callback.onSignalStrengthsChanged(mSignalStrength[r.phoneId]);
- }
- } catch (RemoteException ex) {
- remove(r.binder);
- }
- }
if (validateEventAndUserLocked(
r, TelephonyCallback.EVENT_CELL_INFO_CHANGED)) {
try {
@@ -1353,27 +1343,6 @@
}
}
- private void updateReportSignalStrengthDecision(int subscriptionId) {
- synchronized (mRecords) {
- TelephonyManager telephonyManager = (TelephonyManager) mContext
- .getSystemService(Context.TELEPHONY_SERVICE);
- for (Record r : mRecords) {
- // If any of the system clients wants to always listen to signal strength,
- // we need to set it on.
- if (r.matchTelephonyCallbackEvent(
- TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
- telephonyManager.createForSubscriptionId(subscriptionId)
- .setAlwaysReportSignalStrength(true);
- return;
- }
- }
- // If none of the system clients wants to always listen to signal strength,
- // we need to set it off.
- telephonyManager.createForSubscriptionId(subscriptionId)
- .setAlwaysReportSignalStrength(false);
- }
- }
-
private String getCallIncomingNumber(Record record, int phoneId) {
// Only reveal the incoming number if the record has read call log permission.
return record.canReadCallLog() ? mCallIncomingNumber[phoneId] : "";
@@ -1457,14 +1426,6 @@
}
mRecords.remove(i);
-
- // Every time a client that is registrating to always receive the signal
- // strength is removed from registry records, we need to check if
- // the signal strength decision needs to update on its slot.
- if (r.matchTelephonyCallbackEvent(
- TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
- updateReportSignalStrengthDecision(r.subId);
- }
return;
}
}
@@ -1696,10 +1657,8 @@
log("notifySignalStrengthForPhoneId: r=" + r + " subId=" + subId
+ " phoneId=" + phoneId + " ss=" + signalStrength);
}
- if ((r.matchTelephonyCallbackEvent(
+ if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)
- || r.matchTelephonyCallbackEvent(
- TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))
&& idMatch(r, subId, phoneId)) {
try {
if (DBG) {
@@ -2003,42 +1962,8 @@
ApnSetting apnSetting = preciseState.getApnSetting();
- int apnTypes = apnSetting.getApnTypeBitmask();
- int state = preciseState.getState();
- int networkType = preciseState.getNetworkType();
-
synchronized (mRecords) {
if (validatePhoneId(phoneId)) {
- // We only call the callback when the change is for default APN type.
- if ((ApnSetting.TYPE_DEFAULT & apnTypes) != 0
- && (mDataConnectionState[phoneId] != state
- || mDataConnectionNetworkType[phoneId] != networkType)) {
- String str = "onDataConnectionStateChanged("
- + TelephonyUtils.dataStateToString(state)
- + ", " + getNetworkTypeName(networkType)
- + ") subId=" + subId + ", phoneId=" + phoneId;
- log(str);
- mLocalLog.log(str);
- for (Record r : mRecords) {
- if (r.matchTelephonyCallbackEvent(
- TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED)
- && idMatch(r, subId, phoneId)) {
- try {
- if (DBG) {
- log("Notify data connection state changed on sub: " + subId);
- }
- r.callback.onDataConnectionStateChanged(state, networkType);
- } catch (RemoteException ex) {
- mRemoveList.add(r.binder);
- }
- }
- }
- handleRemoveListLocked();
-
- mDataConnectionState[phoneId] = state;
- mDataConnectionNetworkType[phoneId] = networkType;
- }
-
Pair<Integer, ApnSetting> key = Pair.create(preciseState.getTransportType(),
preciseState.getApnSetting());
PreciseDataConnectionState oldState = mPreciseDataConnectionStates.get(phoneId)
@@ -2070,6 +1995,73 @@
if (preciseState.getState() != TelephonyManager.DATA_DISCONNECTED) {
mPreciseDataConnectionStates.get(phoneId).put(key, preciseState);
}
+
+ // Note that below is just the workaround for reporting the correct data connection
+ // state. The actual fix should be put in the new data stack in T.
+ // TODO: Remove the code below in T.
+
+ // Collect all possible candidate data connection state for internet. Key is the
+ // data connection state, value is the precise data connection state.
+ Map<Integer, PreciseDataConnectionState> internetConnections = new ArrayMap<>();
+ if (preciseState.getState() == TelephonyManager.DATA_DISCONNECTED
+ && preciseState.getApnSetting().getApnTypes()
+ .contains(ApnSetting.TYPE_DEFAULT)) {
+ internetConnections.put(TelephonyManager.DATA_DISCONNECTED, preciseState);
+ }
+ for (Map.Entry<Pair<Integer, ApnSetting>, PreciseDataConnectionState> entry :
+ mPreciseDataConnectionStates.get(phoneId).entrySet()) {
+ if (entry.getKey().first == AccessNetworkConstants.TRANSPORT_TYPE_WWAN
+ && entry.getKey().second.getApnTypes()
+ .contains(ApnSetting.TYPE_DEFAULT)) {
+ internetConnections.put(entry.getValue().getState(), entry.getValue());
+ }
+ }
+
+ // If any internet data is in connected state, then report connected, then check
+ // suspended, connecting, disconnecting, and disconnected. The order is very
+ // important.
+ int[] statesInPriority = new int[]{TelephonyManager.DATA_CONNECTED,
+ TelephonyManager.DATA_SUSPENDED, TelephonyManager.DATA_CONNECTING,
+ TelephonyManager.DATA_DISCONNECTING,
+ TelephonyManager.DATA_DISCONNECTED};
+ int state = TelephonyManager.DATA_DISCONNECTED;
+ int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ for (int s : statesInPriority) {
+ if (internetConnections.containsKey(s)) {
+ state = s;
+ networkType = internetConnections.get(s).getNetworkType();
+ break;
+ }
+ }
+
+ if (mDataConnectionState[phoneId] != state
+ || mDataConnectionNetworkType[phoneId] != networkType) {
+ String str = "onDataConnectionStateChanged("
+ + TelephonyUtils.dataStateToString(state)
+ + ", " + TelephonyManager.getNetworkTypeName(networkType)
+ + ") subId=" + subId + ", phoneId=" + phoneId;
+ log(str);
+ mLocalLog.log(str);
+ for (Record r : mRecords) {
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED)
+ && idMatch(r, subId, phoneId)) {
+ try {
+ if (DBG) {
+ log("Notify data connection state changed on sub: " + subId);
+ }
+ r.callback.onDataConnectionStateChanged(state, networkType);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+
+ mDataConnectionState[phoneId] = state;
+ mDataConnectionNetworkType[phoneId] = networkType;
+
+ handleRemoveListLocked();
+ }
}
}
}
@@ -3120,11 +3112,6 @@
android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null);
}
- if ((events.contains(TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH, null);
- }
-
if (isPrivilegedPhoneStatePermissionRequired(events)) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
@@ -3285,9 +3272,7 @@
}
}
- if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)
- || events.contains(
- TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)) {
try {
if (mSignalStrength[phoneId] != null) {
SignalStrength signalStrength = mSignalStrength[phoneId];
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 0fde6fa..b019789 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -666,7 +666,8 @@
if (doWaitedHalfDump) {
// Get critical event log before logging the half watchdog so that it doesn't
// occur in the log.
- String criticalEvents = CriticalEventLog.getInstance().logLinesForAnrFile();
+ String criticalEvents =
+ CriticalEventLog.getInstance().logLinesForSystemServerTraceFile();
CriticalEventLog.getInstance().logHalfWatchdog(subject);
// We've waited half the deadlock-detection interval. Pull a stack
@@ -693,7 +694,8 @@
// Get critical event log before logging the watchdog so that it doesn't occur in the
// log.
- String criticalEvents = CriticalEventLog.getInstance().logLinesForAnrFile();
+ String criticalEvents =
+ CriticalEventLog.getInstance().logLinesForSystemServerTraceFile();
CriticalEventLog.getInstance().logWatchdog(subject, errorId);
long anrTime = SystemClock.uptimeMillis();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 53f7575..b1a1e0d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -31,6 +31,7 @@
import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
+import static android.app.ActivityManager.StopBgUsersOnSwitch;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.AppOpsManager.OP_NONE;
@@ -4931,6 +4932,8 @@
@Override
public void bootAnimationComplete() {
+ if (DEBUG_ALL) Slog.d(TAG, "bootAnimationComplete: Callers=" + Debug.getCallers(4));
+
final boolean callFinishBooting;
synchronized (this) {
callFinishBooting = mCallFinishBooting;
@@ -7764,7 +7767,7 @@
// On Automotive, at this point the system user has already been started and unlocked,
// and some of the tasks we do here have already been done. So skip those in that case.
- // TODO(b/132262830): this workdound shouldn't be necessary once we move the
+ // TODO(b/132262830, b/203885241): this workdound shouldn't be necessary once we move the
// headless-user start logic to UserManager-land
final boolean bootingSystemUser = currentUserId == UserHandle.USER_SYSTEM;
@@ -13302,7 +13305,7 @@
mAtmInternal.removeRecentTasksByPackageName(ssp, userId);
mServices.forceStopPackageLocked(ssp, userId);
- mAtmInternal.onPackageUninstalled(ssp);
+ mAtmInternal.onPackageUninstalled(ssp, userId);
mBatteryStatsService.notePackageUninstalled(ssp);
}
} else {
@@ -13387,7 +13390,7 @@
Uri data = intent.getData();
String ssp;
if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
- mAtmInternal.onPackageDataCleared(ssp);
+ mAtmInternal.onPackageDataCleared(ssp, userId);
}
break;
}
@@ -15330,6 +15333,11 @@
}
@Override
+ public void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value) {
+ mUserController.setStopBackgroundUsersOnSwitch(value);
+ }
+
+ @Override
public int stopUser(final int userId, boolean force, final IStopUserCallback callback) {
return mUserController.stopUser(userId, force, /* allowDelayedLocking= */ false,
/* callback= */ callback, /* keyEvictedCallback= */ null);
@@ -16652,6 +16660,11 @@
@Nullable ActivityManagerInternal.VoiceInteractionManagerProvider provider) {
ActivityManagerService.this.setVoiceInteractionManagerProvider(provider);
}
+
+ @Override
+ public void setStopBackgroundUsersOnSwitch(int value) {
+ ActivityManagerService.this.setStopBackgroundUsersOnSwitch(value);
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 0f71639..25adddd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -29,6 +29,8 @@
import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.LowMemDetector.ADJ_MEM_FACTOR_NOTHING;
import android.app.ActivityManager;
@@ -100,6 +102,7 @@
import com.android.internal.util.MemInfoReader;
import com.android.server.am.LowMemDetector.MemFactor;
import com.android.server.compat.PlatformCompat;
+import com.android.server.utils.Slogf;
import java.io.BufferedReader;
import java.io.IOException;
@@ -128,6 +131,10 @@
import javax.microedition.khronos.egl.EGLSurface;
final class ActivityManagerShellCommand extends ShellCommand {
+
+ static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityManagerShellCommand" : TAG_AM;
+
+
public static final String NO_CLASS_ERROR_CODE = "Error type 3";
private static final String SHELL_PACKAGE_NAME = "com.android.shell";
@@ -325,6 +332,8 @@
return runServiceRestartBackoff(pw);
case "get-isolated-pids":
return runGetIsolatedProcesses(pw);
+ case "set-stop-user-on-switch":
+ return runSetStopBackgroundUsersOnSwitch(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -3175,6 +3184,30 @@
return 0;
}
+ private int runSetStopBackgroundUsersOnSwitch(PrintWriter pw) throws RemoteException {
+ mInternal.enforceCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "setStopBackgroundUsersOnSwitch()");
+ String arg = getNextArg();
+ if (arg == null) {
+ Slogf.i(TAG, "runSetStopBackgroundUsersOnSwitch(): resetting to default value");
+ mInternal.setStopBackgroundUsersOnSwitch(
+ ActivityManager.STOP_BG_USERS_ON_SWITCH_DEFAULT);
+ pw.println("Reset to default value");
+ return 0;
+ }
+
+ boolean stop = Boolean.parseBoolean(arg);
+ int value = stop
+ ? ActivityManager.STOP_BG_USERS_ON_SWITCH_TRUE
+ : ActivityManager.STOP_BG_USERS_ON_SWITCH_FALSE;
+
+ Slogf.i(TAG, "runSetStopBackgroundUsersOnSwitch(): setting to %d (%b)", value, stop);
+ mInternal.setStopBackgroundUsersOnSwitch(value);
+ pw.println("Set to " + stop);
+
+ return 0;
+ }
+
private Resources getResources(PrintWriter pw) throws RemoteException {
// system resources does not contain all the device configuration, construct it manually.
Configuration config = mInterface.getConfiguration();
@@ -3507,6 +3540,10 @@
pw.println(" Shows the restart backoff policy state for <PACKAGE_NAME>.");
pw.println(" get-isolated-pids <UID>");
pw.println(" Get the PIDs of isolated processes with packages in this <UID>");
+ pw.println(" set-stop-user-on-switch [true|false]");
+ pw.println(" Sets whether the current user (and its profiles) should be stopped"
+ + " when switching to a different user.");
+ pw.println(" Without arguments, it resets to the value defined by platform.");
pw.println();
Intent.printIntentArgsHelp(pw, "");
}
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index b7ea3dc..4220506 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -332,6 +332,13 @@
}
}
+ // Get critical event log before logging the ANR so that it doesn't occur in the log.
+ final String criticalEventLog =
+ CriticalEventLog.getInstance().logLinesForTraceFile(
+ mApp.getProcessClassEnum(), mApp.processName, mApp.uid);
+ CriticalEventLog.getInstance().logAnr(annotation, mApp.getProcessClassEnum(),
+ mApp.processName, mApp.uid, mApp.mPid);
+
// Log the ANR to the main log.
StringBuilder info = new StringBuilder();
info.setLength(0);
@@ -401,7 +408,6 @@
StringWriter tracesFileException = new StringWriter();
// To hold the start and end offset to the ANR trace file respectively.
final long[] offsets = new long[2];
- final String criticalEventLog = CriticalEventLog.getInstance().logLinesForAnrFile();
File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
isSilentAnr ? null : processCpuTracker, isSilentAnr ? null : lastPids,
nativePids, tracesFileException, offsets, annotation, criticalEventLog);
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 4ef36d6..b5a9ee9 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -96,6 +96,7 @@
DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
DeviceConfig.NAMESPACE_SURFACE_FLINGER_NATIVE_BOOT,
DeviceConfig.NAMESPACE_SWCODEC_NATIVE,
+ DeviceConfig.NAMESPACE_TETHERING,
DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
};
diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java
index c1bfe25..f6a1841 100644
--- a/services/core/java/com/android/server/am/UidObserverController.java
+++ b/services/core/java/com/android/server/am/UidObserverController.java
@@ -129,6 +129,10 @@
return mValidateUids;
}
+ Runnable getDispatchRunnableForTest() {
+ return mDispatchRunnable;
+ }
+
@VisibleForTesting
static int mergeWithPendingChange(int currentChange, int pendingChange) {
// If there is no change in idle or active state, then keep whatever was pending.
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index fa7eae3..0c518a0 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -19,6 +19,9 @@
import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.app.ActivityManager.STOP_BG_USERS_ON_SWITCH_DEFAULT;
+import static android.app.ActivityManager.STOP_BG_USERS_ON_SWITCH_TRUE;
+import static android.app.ActivityManager.StopBgUsersOnSwitch;
import static android.app.ActivityManager.USER_OP_ERROR_IS_SYSTEM;
import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
import static android.app.ActivityManager.USER_OP_IS_CURRENT;
@@ -368,6 +371,13 @@
@GuardedBy("mLock")
private boolean mInitialized;
+ /**
+ * Defines the behavior of whether the background users should be stopped when the foreground
+ * user is switched.
+ */
+ @GuardedBy("mLock")
+ private @StopBgUsersOnSwitch int mStopBgUsersOnSwitch = STOP_BG_USERS_ON_SWITCH_DEFAULT;
+
UserController(ActivityManagerService service) {
this(new Injector(service));
}
@@ -408,8 +418,33 @@
}
}
+ void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value) {
+ if (mInjector.checkCallingPermission(android.Manifest.permission.MANAGE_USERS)
+ == PackageManager.PERMISSION_DENIED && mInjector.checkCallingPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ == PackageManager.PERMISSION_DENIED) {
+ throw new SecurityException(
+ "You either need MANAGE_USERS or INTERACT_ACROSS_USERS_FULL permission to "
+ + "call setStopBackgroundUsersOnSwitch()");
+ }
+
+ synchronized (mLock) {
+ Slogf.i(TAG, "setStopBackgroundUsersOnSwitch(): %d -> %d",
+ mStopBgUsersOnSwitch, value);
+ mStopBgUsersOnSwitch = value;
+ }
+ }
+
private boolean shouldStopBackgroundUsersOnSwitch() {
- int property = SystemProperties.getInt("fw.stop_bg_users_on_switch", -1);
+ synchronized (mLock) {
+ if (mStopBgUsersOnSwitch != STOP_BG_USERS_ON_SWITCH_DEFAULT) {
+ final boolean value = mStopBgUsersOnSwitch == STOP_BG_USERS_ON_SWITCH_TRUE;
+ Slogf.i(TAG, "isStopBackgroundUsersOnSwitch(): returning overridden value (%b)",
+ value);
+ return value;
+ }
+ }
+ final int property = SystemProperties.getInt("fw.stop_bg_users_on_switch", -1);
return property == -1 ? mDelayUserDataLocking : property == 1;
}
@@ -2636,8 +2671,9 @@
pw.println(" mTargetUserId:" + mTargetUserId);
pw.println(" mLastActiveUsers:" + mLastActiveUsers);
pw.println(" mDelayUserDataLocking:" + mDelayUserDataLocking);
- pw.println(" shouldStopBackgroundUsersOnSwitch:"
+ pw.println(" shouldStopBackgroundUsersOnSwitch():"
+ shouldStopBackgroundUsersOnSwitch());
+ pw.println(" mStopBgUsersOnSwitch:" + mStopBgUsersOnSwitch);
pw.println(" mMaxRunningUsers:" + mMaxRunningUsers);
pw.println(" mUserSwitchUiEnabled:" + mUserSwitchUiEnabled);
pw.println(" mInitialized:" + mInitialized);
diff --git a/services/core/java/com/android/server/am/UserState.java b/services/core/java/com/android/server/am/UserState.java
index 1fe7608..40fc306 100644
--- a/services/core/java/com/android/server/am/UserState.java
+++ b/services/core/java/com/android/server/am/UserState.java
@@ -145,4 +145,11 @@
proto.write(UserStateProto.SWITCHING, switching);
proto.end(token);
}
+
+ @Override
+ public String toString() {
+ return "[UserState: id=" + mHandle.getIdentifier() + ", state=" + stateToString(state)
+ + ", lastState=" + stateToString(lastState) + ", switching=" + switching
+ + ", tokenProvided=" + tokenProvided + "]";
+ }
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 587b5d2..ed494b5 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3307,15 +3307,23 @@
Objects.requireNonNull(packageName);
try {
verifyAndGetBypass(uid, packageName, null);
- if (filterAppAccessUnlocked(packageName)) {
- return AppOpsManager.MODE_ERRORED;
+ // When the caller is the system, it's possible that the packageName is the special
+ // one (e.g., "root") which isn't actually existed.
+ if (resolveUid(packageName) == uid
+ || (isPackageExisted(packageName) && !filterAppAccessUnlocked(packageName))) {
+ return AppOpsManager.MODE_ALLOWED;
}
- return AppOpsManager.MODE_ALLOWED;
+ return AppOpsManager.MODE_ERRORED;
} catch (SecurityException ignored) {
return AppOpsManager.MODE_ERRORED;
}
}
+ private boolean isPackageExisted(String packageName) {
+ return LocalServices.getService(PackageManagerInternal.class)
+ .getPackageStateInternal(packageName) != null;
+ }
+
/**
* This method will check with PackageManager to determine if the package provided should
* be visible to the {@link Binder#getCallingUid()}.
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 2cd63b5..34191fa 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -5037,9 +5037,19 @@
}
}
- /** @see AudioManager#playSoundEffect(int) */
- public void playSoundEffect(int effectType) {
- playSoundEffectVolume(effectType, -1.0f);
+ /** @see AudioManager#playSoundEffect(int, int) */
+ public void playSoundEffect(int effectType, int userId) {
+ if (querySoundEffectsEnabled(userId)) {
+ playSoundEffectVolume(effectType, -1.0f);
+ }
+ }
+
+ /**
+ * Settings has an in memory cache, so this is fast.
+ */
+ private boolean querySoundEffectsEnabled(int user) {
+ return Settings.System.getIntForUser(getContentResolver(),
+ Settings.System.SOUND_EFFECTS_ENABLED, 0, user) != 0;
}
/** @see AudioManager#playSoundEffect(int, float) */
@@ -7656,7 +7666,7 @@
break;
case MSG_INIT_HEADTRACKING_SENSORS:
- mSpatializerHelper.onInitSensors(/*init*/ msg.arg1 == 1);
+ mSpatializerHelper.onInitSensors();
break;
case MSG_CHECK_MUSIC_ACTIVE:
@@ -8590,14 +8600,13 @@
/**
* post a message to schedule init/release of head tracking sensors
- * @param init initialization if true, release if false
+ * whether to initialize or release sensors is based on the state of spatializer
*/
- void postInitSpatializerHeadTrackingSensors(boolean init) {
+ void postInitSpatializerHeadTrackingSensors() {
sendMsg(mAudioHandler,
MSG_INIT_HEADTRACKING_SENSORS,
SENDMSG_REPLACE,
- /*arg1*/ init ? 1 : 0,
- 0, TAG, /*delay*/ 0);
+ /*arg1*/ 0, /*arg2*/ 0, TAG, /*delay*/ 0);
}
//==========================================================================================
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 7cd027c..6a26bea 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -63,6 +63,14 @@
private @Nullable SensorManager mSensorManager;
//------------------------------------------------------------
+ /** head tracker sensor name */
+ // TODO: replace with generic head tracker sensor name.
+ // the current implementation refers to the "google" namespace but will be replaced
+ // by an android name at the next API level revision, it is not Google-specific.
+ // Also see "TODO-HT" in onInitSensors() method
+ private static final String HEADTRACKER_SENSOR =
+ "com.google.hardware.sensor.hid_dynamic.headtracker";
+
// Spatializer state machine
private static final int STATE_UNINITIALIZED = 0;
private static final int STATE_NOT_SUPPORTED = 1;
@@ -81,7 +89,7 @@
private @Nullable ISpatializer mSpat;
private @Nullable SpatializerCallback mSpatCallback;
private @Nullable SpatializerHeadTrackingCallback mSpatHeadTrackingCallback;
-
+ private @Nullable HelperDynamicSensorCallback mDynSensorCallback;
// default attributes and format that determine basic availability of spatialization
private static final AudioAttributes DEFAULT_ATTRIBUTES = new AudioAttributes.Builder()
@@ -209,11 +217,7 @@
// TODO use reported spat level to change state
// init sensors
- if (level == SpatializationLevel.NONE) {
- initSensors(/*init*/false);
- } else {
- postInitSensors(true);
- }
+ postInitSensors();
}
public void onOutputChanged(int output) {
@@ -229,6 +233,7 @@
}
};
+ //------------------------------------------------------
// spatializer head tracking callback from native
private final class SpatializerHeadTrackingCallback
extends ISpatializerHeadTrackingCallback.Stub {
@@ -269,6 +274,20 @@
};
//------------------------------------------------------
+ // dynamic sensor callback
+ private final class HelperDynamicSensorCallback extends SensorManager.DynamicSensorCallback {
+ @Override
+ public void onDynamicSensorConnected(Sensor sensor) {
+ postInitSensors();
+ }
+
+ @Override
+ public void onDynamicSensorDisconnected(Sensor sensor) {
+ postInitSensors();
+ }
+ }
+
+ //------------------------------------------------------
// compatible devices
/**
* @return a shallow copy of the list of compatible audio devices
@@ -851,48 +870,12 @@
//------------------------------------------------------
// sensors
- private void initSensors(boolean init) {
- if (mSensorManager == null) {
- mSensorManager = (SensorManager)
- mAudioService.mContext.getSystemService(Context.SENSOR_SERVICE);
- }
- final int headHandle;
- final int screenHandle;
- if (init) {
- if (mSensorManager == null) {
- Log.e(TAG, "Null SensorManager, can't init sensors");
- return;
- }
- // TODO replace with dynamic association of sensor for headtracker
- Sensor headSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR);
- headHandle = headSensor.getHandle();
- //Sensor screenSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
- //screenHandle = deviceSensor.getHandle();
- screenHandle = -1;
- } else {
- // -1 is disable value
- screenHandle = -1;
- headHandle = -1;
- }
- try {
- Log.i(TAG, "setScreenSensor:" + screenHandle);
- mSpat.setScreenSensor(screenHandle);
- } catch (Exception e) {
- Log.e(TAG, "Error calling setScreenSensor:" + screenHandle, e);
- }
- try {
- Log.i(TAG, "setHeadSensor:" + headHandle);
- mSpat.setHeadSensor(headHandle);
- } catch (Exception e) {
- Log.e(TAG, "Error calling setHeadSensor:" + headHandle, e);
- }
+ private void postInitSensors() {
+ mAudioService.postInitSpatializerHeadTrackingSensors();
}
- private void postInitSensors(boolean init) {
- mAudioService.postInitSpatializerHeadTrackingSensors(init);
- }
-
- synchronized void onInitSensors(boolean init) {
+ synchronized void onInitSensors() {
+ final boolean init = (mSpatLevel != SpatializationLevel.NONE);
final String action = init ? "initializing" : "releasing";
if (mSpat == null) {
Log.e(TAG, "not " + action + " sensors, null spatializer");
@@ -907,7 +890,53 @@
Log.e(TAG, "not " + action + " sensors, error querying headtracking", e);
return;
}
- initSensors(init);
+ int headHandle = -1;
+ int screenHandle = -1;
+ if (init) {
+ if (mSensorManager == null) {
+ try {
+ mSensorManager = (SensorManager)
+ mAudioService.mContext.getSystemService(Context.SENSOR_SERVICE);
+ mDynSensorCallback = new HelperDynamicSensorCallback();
+ mSensorManager.registerDynamicSensorCallback(mDynSensorCallback);
+ } catch (Exception e) {
+ Log.e(TAG, "Error with SensorManager, can't initialize sensors", e);
+ mSensorManager = null;
+ mDynSensorCallback = null;
+ return;
+ }
+ }
+ // initialize sensor handles
+ // TODO-HT update to non-private sensor once head tracker sensor is defined
+ for (Sensor sensor : mSensorManager.getDynamicSensorList(
+ Sensor.TYPE_DEVICE_PRIVATE_BASE)) {
+ if (sensor.getStringType().equals(HEADTRACKER_SENSOR)) {
+ headHandle = sensor.getHandle();
+ break;
+ }
+ }
+ Sensor screenSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
+ screenHandle = screenSensor.getHandle();
+ } else {
+ if (mSensorManager != null && mDynSensorCallback != null) {
+ mSensorManager.unregisterDynamicSensorCallback(mDynSensorCallback);
+ mSensorManager = null;
+ mDynSensorCallback = null;
+ }
+ // -1 is disable value for both screen and head tracker handles
+ }
+ try {
+ Log.i(TAG, "setScreenSensor:" + screenHandle);
+ mSpat.setScreenSensor(screenHandle);
+ } catch (Exception e) {
+ Log.e(TAG, "Error calling setScreenSensor:" + screenHandle, e);
+ }
+ try {
+ Log.i(TAG, "setHeadSensor:" + headHandle);
+ mSpat.setHeadSensor(headHandle);
+ } catch (Exception e) {
+ Log.e(TAG, "Error calling setHeadSensor:" + headHandle, e);
+ }
}
//------------------------------------------------------
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 031f6ee..61b8ded 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -168,6 +168,10 @@
return Utils.isKeyguard(getContext(), getOwnerString());
}
+ private boolean isSettings() {
+ return Utils.isSettings(getContext(), getOwnerString());
+ }
+
@Override
protected boolean isCryptoOperation() {
return mOperationId != 0;
@@ -499,6 +503,8 @@
protected int getShowOverlayReason() {
if (isKeyguard()) {
return BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
+ } else if (isSettings()) {
+ return BiometricOverlayConstants.REASON_AUTH_SETTINGS;
} else if (isBiometricPrompt()) {
return BiometricOverlayConstants.REASON_AUTH_BP;
} else {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java
index c364dbb..2b5f495 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java
@@ -22,6 +22,7 @@
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
import android.hardware.biometrics.face.ISessionCallback;
+import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
@@ -52,6 +53,7 @@
try {
final ISession newSession = getFreshDaemon().createSession(getSensorId(),
getTargetUserId(), mSessionCallback);
+ Binder.allowBlocking(newSession.asBinder());
mUserStartedCallback.onUserStarted(getTargetUserId(), newSession);
getCallback().onClientFinished(this, true /* success */);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 0defc3f..734b173 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -72,8 +72,10 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
/**
* Provider for a single instance of the {@link IFingerprint} HAL.
@@ -169,11 +171,13 @@
prop.sensorType,
true /* resetLockoutRequiresHardwareAuthToken */,
!workaroundLocations.isEmpty() ? workaroundLocations :
- List.of(new SensorLocationInternal(
- "" /* displayId */,
- prop.sensorLocations[0].sensorLocationX,
- prop.sensorLocations[0].sensorLocationY,
- prop.sensorLocations[0].sensorRadius)));
+ Arrays.stream(prop.sensorLocations).map(location ->
+ new SensorLocationInternal(
+ location.display,
+ location.sensorLocationX,
+ location.sensorLocationY,
+ location.sensorRadius))
+ .collect(Collectors.toList()));
final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
internalProp, lockoutResetDispatcher, gestureAvailabilityDispatcher);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java
index 2d40c91..ee81620 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java
@@ -22,6 +22,7 @@
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.biometrics.fingerprint.ISessionCallback;
+import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
@@ -53,6 +54,7 @@
try {
final ISession newSession = getFreshDaemon().createSession(getSensorId(),
getTargetUserId(), mSessionCallback);
+ Binder.allowBlocking(newSession.asBinder());
mUserStartedCallback.onUserStarted(getTargetUserId(), newSession);
getCallback().onClientFinished(this, true /* success */);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/communal/CommunalManagerService.java b/services/core/java/com/android/server/communal/CommunalManagerService.java
index d77e3b5..2fda4a5 100644
--- a/services/core/java/com/android/server/communal/CommunalManagerService.java
+++ b/services/core/java/com/android/server/communal/CommunalManagerService.java
@@ -17,27 +17,39 @@
package com.android.server.communal;
import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static com.android.server.wm.ActivityInterceptorCallback.COMMUNAL_MODE_ORDERED_ID;
import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.TestApi;
import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.communal.ICommunalManager;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
+import android.compat.annotation.Overridable;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentSender;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
-import android.database.ContentObserver;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
import android.os.UserHandle;
import android.provider.Settings;
+import android.service.dreams.DreamManagerInternal;
import android.text.TextUtils;
+import android.util.Log;
+import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.LaunchAfterAuthenticationActivity;
@@ -47,6 +59,7 @@
import com.android.server.wm.ActivityTaskManagerInternal;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -55,21 +68,47 @@
* System service for handling Communal Mode state.
*/
public final class CommunalManagerService extends SystemService {
+ private static final String TAG = CommunalManagerService.class.getSimpleName();
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String DELIMITER = ",";
private final Context mContext;
private final ActivityTaskManagerInternal mAtmInternal;
private final KeyguardManager mKeyguardManager;
private final AtomicBoolean mCommunalViewIsShowing = new AtomicBoolean(false);
- private final Handler mHandler = new Handler(Looper.getMainLooper());
- private final Set<String> mEnabledApps = new HashSet<>();
- private final SettingsObserver mSettingsObserver;
+ private final BinderService mBinderService;
+ private final PackageReceiver mPackageReceiver;
+ private final PackageManager mPackageManager;
+ private final DreamManagerInternal mDreamManagerInternal;
+
+ /**
+ * This change id is used to annotate packages which are allowed to run in communal mode.
+ *
+ * @hide
+ */
+ @ChangeId
+ @Overridable
+ @Disabled
+ @TestApi
+ public static final long ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT = 200324021L;
+
+ /**
+ * This change id is used to annotate packages which can run in communal mode by default,
+ * without requiring user opt-in.
+ *
+ * @hide
+ */
+ @ChangeId
+ @Overridable
+ @Disabled
+ @TestApi
+ public static final long ALLOW_COMMUNAL_MODE_BY_DEFAULT = 203673428L;
private final ActivityInterceptorCallback mActivityInterceptorCallback =
new ActivityInterceptorCallback() {
@Nullable
@Override
public Intent intercept(ActivityInterceptorInfo info) {
- if (isActivityAllowed(info.aInfo)) {
+ if (!shouldIntercept(info.aInfo)) {
return null;
}
@@ -96,63 +135,108 @@
public CommunalManagerService(Context context) {
super(context);
mContext = context;
- mSettingsObserver = new SettingsObserver();
+ mPackageManager = mContext.getPackageManager();
mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
+ mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
+ mBinderService = new BinderService();
+ mPackageReceiver = new PackageReceiver(mContext);
+ }
+
+ @VisibleForTesting
+ BinderService getBinderServiceInstance() {
+ return mBinderService;
}
@Override
public void onStart() {
- publishBinderService(Context.COMMUNAL_MANAGER_SERVICE, new BinderService());
- mAtmInternal.registerActivityStartInterceptor(COMMUNAL_MODE_ORDERED_ID,
- mActivityInterceptorCallback);
-
-
- updateSelectedApps();
- mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.COMMUNAL_MODE_PACKAGES), false, mSettingsObserver,
- UserHandle.USER_SYSTEM);
+ publishBinderService(Context.COMMUNAL_MANAGER_SERVICE, mBinderService);
}
- @VisibleForTesting
- void updateSelectedApps() {
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase != SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) return;
+ mAtmInternal.registerActivityStartInterceptor(
+ COMMUNAL_MODE_ORDERED_ID,
+ mActivityInterceptorCallback);
+ mPackageReceiver.register();
+ removeUninstalledPackagesFromSettings();
+ }
+
+ @Override
+ public void finalize() {
+ mPackageReceiver.unregister();
+ }
+
+ private Set<String> getUserEnabledApps() {
final String encodedApps = Settings.Secure.getStringForUser(
mContext.getContentResolver(),
Settings.Secure.COMMUNAL_MODE_PACKAGES,
UserHandle.USER_SYSTEM);
- mEnabledApps.clear();
+ return TextUtils.isEmpty(encodedApps)
+ ? Collections.emptySet()
+ : new HashSet<>(Arrays.asList(encodedApps.split(DELIMITER)));
+ }
- if (!TextUtils.isEmpty(encodedApps)) {
- mEnabledApps.addAll(Arrays.asList(encodedApps.split(DELIMITER)));
+ private void removeUninstalledPackagesFromSettings() {
+ for (String packageName : getUserEnabledApps()) {
+ if (!isPackageInstalled(packageName, mPackageManager)) {
+ removePackageFromSettings(packageName);
+ }
}
}
- private boolean isActivityAllowed(ActivityInfo activityInfo) {
- return true;
- // TODO(b/191994709): Uncomment the lines below once Dreams and Assistant have been fixed.
-// if (!mCommunalViewIsShowing.get() || !mKeyguardManager.isKeyguardLocked()) return true;
-//
-// // If the activity doesn't have showWhenLocked enabled, disallow the activity.
-// final boolean showWhenLocked =
-// (activityInfo.flags & ActivityInfo.FLAG_SHOW_WHEN_LOCKED) != 0;
-// if (!showWhenLocked) {
-// return false;
-// }
-//
-// // Check the cached user preferences to see if the user has allowed this app.
-// return mEnabledApps.contains(activityInfo.applicationInfo.packageName);
+ private void removePackageFromSettings(String packageName) {
+ Set<String> enabledPackages = getUserEnabledApps();
+ if (enabledPackages.remove(packageName)) {
+ Settings.Secure.putStringForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.COMMUNAL_MODE_PACKAGES,
+ String.join(DELIMITER, enabledPackages),
+ UserHandle.USER_SYSTEM);
+ }
}
- private final class SettingsObserver extends ContentObserver {
- SettingsObserver() {
- super(mHandler);
+ @VisibleForTesting
+ static boolean isPackageInstalled(String packageName, PackageManager packageManager) {
+ if (packageManager == null) return false;
+ try {
+ return packageManager.getPackageInfo(packageName, 0) != null;
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ private boolean isAppAllowed(ApplicationInfo appInfo) {
+ if (isActiveDream(appInfo) || isChangeEnabled(ALLOW_COMMUNAL_MODE_BY_DEFAULT, appInfo)) {
+ return true;
}
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- mContext.getMainExecutor().execute(CommunalManagerService.this::updateSelectedApps);
- }
+ return isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, appInfo)
+ && getUserEnabledApps().contains(appInfo.packageName);
+ }
+
+ private boolean isActiveDream(ApplicationInfo appInfo) {
+ final ComponentName activeDream = mDreamManagerInternal.getActiveDreamComponent(
+ /* doze= */ false);
+ final ComponentName activeDoze = mDreamManagerInternal.getActiveDreamComponent(
+ /* doze= */ true);
+ return isFromPackage(activeDream, appInfo) || isFromPackage(activeDoze, appInfo);
+ }
+
+ private static boolean isFromPackage(ComponentName componentName, ApplicationInfo appInfo) {
+ if (componentName == null) return false;
+ return TextUtils.equals(appInfo.packageName, componentName.getPackageName());
+ }
+
+ private static boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
+ return CompatChanges.isChangeEnabled(changeId, appInfo.packageName, UserHandle.SYSTEM);
+ }
+
+ private boolean shouldIntercept(ActivityInfo activityInfo) {
+ if (!mCommunalViewIsShowing.get() || !mKeyguardManager.isKeyguardLocked()) return false;
+ return !isAppAllowed(activityInfo.applicationInfo);
}
private final class BinderService extends ICommunalManager.Stub {
@@ -168,4 +252,49 @@
mCommunalViewIsShowing.set(isShowing);
}
}
+
+ /**
+ * A {@link BroadcastReceiver} that listens on package removed events and updates any stored
+ * package state in Settings.
+ */
+ private final class PackageReceiver extends BroadcastReceiver {
+ private final Context mContext;
+ private final IntentFilter mIntentFilter;
+
+ private PackageReceiver(Context context) {
+ mContext = context;
+ mIntentFilter = new IntentFilter();
+ mIntentFilter.addAction(ACTION_PACKAGE_REMOVED);
+ mIntentFilter.addDataScheme("package");
+ }
+
+ private void register() {
+ mContext.registerReceiverAsUser(
+ this,
+ UserHandle.SYSTEM,
+ mIntentFilter,
+ /* broadcastPermission= */null,
+ /* scheduler= */ null);
+ }
+
+ private void unregister() {
+ mContext.unregisterReceiver(this);
+ }
+
+ @Override
+ public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
+ final Uri data = intent.getData();
+ if (data == null) {
+ Slog.w(TAG, "Failed to get package name in package receiver");
+ return;
+ }
+ final String packageName = data.getSchemeSpecificPart();
+ final String action = intent.getAction();
+ if (ACTION_PACKAGE_REMOVED.equals(action)) {
+ removePackageFromSettings(packageName);
+ } else {
+ Slog.w(TAG, "Unsupported action in package receiver: " + action);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/communal/OWNERS b/services/core/java/com/android/server/communal/OWNERS
new file mode 100644
index 0000000..e499b160
--- /dev/null
+++ b/services/core/java/com/android/server/communal/OWNERS
@@ -0,0 +1,3 @@
+brycelee@google.com
+justinkoh@google.com
+lusilva@google.com
\ No newline at end of file
diff --git a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
index d5fe9c9..30b3524 100644
--- a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
+++ b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
@@ -18,6 +18,7 @@
import android.os.Handler;
import android.os.HandlerThread;
+import android.server.ServerProtoEnums;
import android.util.Slog;
import com.android.framework.protobuf.nano.MessageNanoPrinter;
@@ -26,6 +27,7 @@
import com.android.server.criticalevents.nano.CriticalEventLogProto;
import com.android.server.criticalevents.nano.CriticalEventLogStorageProto;
import com.android.server.criticalevents.nano.CriticalEventProto;
+import com.android.server.criticalevents.nano.CriticalEventProto.AppNotResponding;
import com.android.server.criticalevents.nano.CriticalEventProto.HalfWatchdog;
import com.android.server.criticalevents.nano.CriticalEventProto.Watchdog;
@@ -49,6 +51,9 @@
public class CriticalEventLog {
private static final String TAG = CriticalEventLog.class.getSimpleName();
+ /** UID for system_server. */
+ private static final int AID_SYSTEM = 1000;
+
private static CriticalEventLog sInstance;
/** Name of the file the log is saved to. */
@@ -153,6 +158,27 @@
log(event);
}
+ /**
+ * Logs an ANR.
+ *
+ * @param subject the ANR subject line.
+ * @param processClassEnum {@link android.server.ServerProtoEnums} value for the ANRing process.
+ * @param processName name of the ANRing process.
+ * @param uid uid of the ANRing process.
+ * @param pid pid of the ANRing process.
+ */
+ public void logAnr(String subject, int processClassEnum, String processName, int uid, int pid) {
+ AppNotResponding anr = new AppNotResponding();
+ anr.subject = subject;
+ anr.processClass = processClassEnum;
+ anr.process = processName;
+ anr.uid = uid;
+ anr.pid = pid;
+ CriticalEventProto event = new CriticalEventProto();
+ event.setAnr(anr);
+ log(event);
+ }
+
private void log(CriticalEventProto event) {
event.timestampMs = getWallTimeMillis();
mEvents.append(event);
@@ -160,14 +186,37 @@
}
/**
- * Returns recent critical events in text format to include in logs such as ANR files.
+ * Returns recent critical events in text format to include in system server ANR stack trace
+ * file.
*
* Includes all events in the ring buffer with age less than or equal to {@code mWindowMs}.
*/
- public String logLinesForAnrFile() {
+ public String logLinesForSystemServerTraceFile() {
+ return logLinesForTraceFile(ServerProtoEnums.SYSTEM_SERVER, "AID_SYSTEM", AID_SYSTEM);
+ }
+
+ /**
+ * Returns recent critical events in text format to include in logs such as ANR stack trace
+ * files.
+ *
+ * Includes all events in the ring buffer with age less than or equal to {@code mWindowMs}.
+ *
+ * Some data in the returned log may be redacted for privacy. For example, a log for a data
+ * app will not include specific crash information for a different data app. See
+ * {@link LogSanitizer} for more information.
+ *
+ * @param traceProcessClassEnum {@link android.server.ServerProtoEnums} value for the process
+ * the ANR trace file is for.
+ * @param traceProcessName name of the process the ANR trace file is for.
+ * @param traceUid uid of the process the ANR trace file is for.
+ */
+ public String logLinesForTraceFile(int traceProcessClassEnum, String traceProcessName,
+ int traceUid) {
+ CriticalEventLogProto outputLogProto = getOutputLogProto(traceProcessClassEnum,
+ traceProcessName, traceUid);
return new StringBuilder()
.append("--- CriticalEventLog ---\n")
- .append(MessageNanoPrinter.print(getRecentEvents()))
+ .append(MessageNanoPrinter.print(outputLogProto))
.append('\n').toString();
}
@@ -177,12 +226,20 @@
* Includes all events in the ring buffer with age less than or equal to {@code mWindowMs}.
*/
@VisibleForTesting
- protected CriticalEventLogProto getRecentEvents() {
+ protected CriticalEventLogProto getOutputLogProto(int traceProcessClassEnum,
+ String traceProcessName, int traceUid) {
CriticalEventLogProto log = new CriticalEventLogProto();
log.timestampMs = getWallTimeMillis();
log.windowMs = mWindowMs;
log.capacity = mEvents.capacity();
- log.events = recentEventsWithMinTimestamp(log.timestampMs - mWindowMs);
+
+ CriticalEventProto[] events = recentEventsWithMinTimestamp(log.timestampMs - mWindowMs);
+ LogSanitizer sanitizer = new LogSanitizer(traceProcessClassEnum, traceProcessName,
+ traceUid);
+ for (int i = 0; i < events.length; i++) {
+ events[i] = sanitizer.process(events[i]);
+ }
+ log.events = events;
return log;
}
@@ -325,4 +382,68 @@
}
}
}
+
+ /**
+ * Redacts private data app fields from the critical event protos.
+ *
+ * When a critical event log is requested, this class is used to redact specific information
+ * so that the trace file for a data app does not leak private information about other data
+ * apps.
+ */
+ private static class LogSanitizer {
+ /**
+ * The {@link CriticalEventProto.ProcessClass} of the process the output trace file is for.
+ */
+ int mTraceProcessClassEnum;
+
+ /** The name of the process that the output trace file is for. */
+ String mTraceProcessName;
+
+ /** The uid of the process that the output trace file is for. */
+ int mTraceUid;
+
+ LogSanitizer(int traceProcessClassEnum, String traceProcessName, int traceUid) {
+ mTraceProcessClassEnum = traceProcessClassEnum;
+ mTraceProcessName = traceProcessName;
+ mTraceUid = traceUid;
+ }
+
+ /**
+ * Redacts information from a critical event proto where necessary.
+ *
+ * This function does not mutate its input. If redaction happens, it returns a new proto.
+ * Otherwise, it returns the original proto.
+ */
+ CriticalEventProto process(CriticalEventProto event) {
+ if (event.hasAnr()) {
+ AppNotResponding anr = event.getAnr();
+ if (shouldSanitize(anr.processClass, anr.process, anr.uid)) {
+ return sanitizeAnr(event);
+ }
+ }
+ return event;
+ }
+
+ private boolean shouldSanitize(int processClassEnum, String processName, int uid) {
+ boolean sameApp = processName != null && processName.equals(mTraceProcessName)
+ && mTraceUid == uid;
+
+ // Only sanitize when both the ANR event and trace file are for different data apps.
+ return processClassEnum == CriticalEventProto.DATA_APP
+ && mTraceProcessClassEnum == CriticalEventProto.DATA_APP
+ && !sameApp;
+ }
+
+ private static CriticalEventProto sanitizeAnr(CriticalEventProto base) {
+ CriticalEventProto sanitized = new CriticalEventProto();
+ sanitized.timestampMs = base.timestampMs;
+ AppNotResponding anr = new AppNotResponding();
+ sanitized.setAnr(anr);
+ // Do not set subject and process.
+ anr.processClass = base.getAnr().processClass;
+ anr.uid = base.getAnr().uid;
+ anr.pid = base.getAnr().pid;
+ return sanitized;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 768587a..2f3342f 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -378,6 +378,7 @@
private float mInitialAutoBrightness;
// The controller for the automatic brightness level.
+ @Nullable
private AutomaticBrightnessController mAutomaticBrightnessController;
private Sensor mLightSensor;
@@ -608,7 +609,7 @@
mPendingRbcOnOrChanged = strengthChanged || justActivated;
// Reset model if strength changed OR rbc is turned off
- if (strengthChanged || !justActivated && mAutomaticBrightnessController != null) {
+ if ((strengthChanged || !justActivated) && mAutomaticBrightnessController != null) {
mAutomaticBrightnessController.resetShortTermModel();
}
}
@@ -1567,7 +1568,9 @@
sendUpdatePowerStateLocked();
mHandler.post(mOnBrightnessChangeRunnable);
// TODO(b/192258832): Switch the HBMChangeCallback to a listener pattern.
- mAutomaticBrightnessController.update();
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.update();
+ }
}, mContext);
}
diff --git a/services/core/java/com/android/server/health/OWNERS b/services/core/java/com/android/server/health/OWNERS
new file mode 100644
index 0000000..81522fc
--- /dev/null
+++ b/services/core/java/com/android/server/health/OWNERS
@@ -0,0 +1 @@
+file:platform/hardware/interfaces:/health/aidl/OWNERS
diff --git a/services/core/java/com/android/server/input/InputShellCommand.java b/services/core/java/com/android/server/input/InputShellCommand.java
index c34538c..9fa6fad 100644
--- a/services/core/java/com/android/server/input/InputShellCommand.java
+++ b/services/core/java/com/android/server/input/InputShellCommand.java
@@ -18,10 +18,34 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.KeyEvent.KEYCODE_ALT_LEFT;
+import static android.view.KeyEvent.KEYCODE_ALT_RIGHT;
+import static android.view.KeyEvent.KEYCODE_CTRL_LEFT;
+import static android.view.KeyEvent.KEYCODE_CTRL_RIGHT;
+import static android.view.KeyEvent.KEYCODE_META_LEFT;
+import static android.view.KeyEvent.KEYCODE_META_RIGHT;
+import static android.view.KeyEvent.KEYCODE_SHIFT_LEFT;
+import static android.view.KeyEvent.KEYCODE_SHIFT_RIGHT;
+import static android.view.KeyEvent.META_ALT_LEFT_ON;
+import static android.view.KeyEvent.META_ALT_ON;
+import static android.view.KeyEvent.META_ALT_RIGHT_ON;
+import static android.view.KeyEvent.META_CTRL_LEFT_ON;
+import static android.view.KeyEvent.META_CTRL_ON;
+import static android.view.KeyEvent.META_CTRL_RIGHT_ON;
+import static android.view.KeyEvent.META_META_LEFT_ON;
+import static android.view.KeyEvent.META_META_ON;
+import static android.view.KeyEvent.META_META_RIGHT_ON;
+import static android.view.KeyEvent.META_SHIFT_LEFT_ON;
+import static android.view.KeyEvent.META_SHIFT_ON;
+import static android.view.KeyEvent.META_SHIFT_RIGHT_ON;
+
+import static java.util.Collections.unmodifiableMap;
import android.hardware.input.InputManager;
import android.os.ShellCommand;
import android.os.SystemClock;
+import android.util.ArrayMap;
+import android.util.IntArray;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
@@ -29,8 +53,6 @@
import android.view.ViewConfiguration;
import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
import java.util.Map;
/**
@@ -52,18 +74,39 @@
private static final int DEFAULT_BUTTON_STATE = 0;
private static final int DEFAULT_FLAGS = 0;
- private static final Map<String, Integer> SOURCES = new HashMap<String, Integer>() {{
- put("keyboard", InputDevice.SOURCE_KEYBOARD);
- put("dpad", InputDevice.SOURCE_DPAD);
- put("gamepad", InputDevice.SOURCE_GAMEPAD);
- put("touchscreen", InputDevice.SOURCE_TOUCHSCREEN);
- put("mouse", InputDevice.SOURCE_MOUSE);
- put("stylus", InputDevice.SOURCE_STYLUS);
- put("trackball", InputDevice.SOURCE_TRACKBALL);
- put("touchpad", InputDevice.SOURCE_TOUCHPAD);
- put("touchnavigation", InputDevice.SOURCE_TOUCH_NAVIGATION);
- put("joystick", InputDevice.SOURCE_JOYSTICK);
- }};
+ /** Modifier key to meta state */
+ private static final Map<Integer, Integer> MODIFIER;
+ static {
+ final Map<Integer, Integer> map = new ArrayMap<>();
+ map.put(KEYCODE_CTRL_LEFT, META_CTRL_LEFT_ON | META_CTRL_ON);
+ map.put(KEYCODE_CTRL_RIGHT, META_CTRL_RIGHT_ON | META_CTRL_ON);
+ map.put(KEYCODE_ALT_LEFT, META_ALT_LEFT_ON | META_ALT_ON);
+ map.put(KEYCODE_ALT_RIGHT, META_ALT_RIGHT_ON | META_ALT_ON);
+ map.put(KEYCODE_SHIFT_LEFT, META_SHIFT_LEFT_ON | META_SHIFT_ON);
+ map.put(KEYCODE_SHIFT_RIGHT, META_SHIFT_RIGHT_ON | META_SHIFT_ON);
+ map.put(KEYCODE_META_LEFT, META_META_LEFT_ON | META_META_ON);
+ map.put(KEYCODE_META_RIGHT, META_META_RIGHT_ON | META_META_ON);
+
+ MODIFIER = unmodifiableMap(map);
+ }
+
+ /** String to device source */
+ private static final Map<String, Integer> SOURCES;
+ static {
+ final Map<String, Integer> map = new ArrayMap<>();
+ map.put("keyboard", InputDevice.SOURCE_KEYBOARD);
+ map.put("dpad", InputDevice.SOURCE_DPAD);
+ map.put("gamepad", InputDevice.SOURCE_GAMEPAD);
+ map.put("touchscreen", InputDevice.SOURCE_TOUCHSCREEN);
+ map.put("mouse", InputDevice.SOURCE_MOUSE);
+ map.put("stylus", InputDevice.SOURCE_STYLUS);
+ map.put("trackball", InputDevice.SOURCE_TRACKBALL);
+ map.put("touchpad", InputDevice.SOURCE_TOUCHPAD);
+ map.put("touchnavigation", InputDevice.SOURCE_TOUCH_NAVIGATION);
+ map.put("joystick", InputDevice.SOURCE_JOYSTICK);
+
+ SOURCES = unmodifiableMap(map);
+ }
private void injectKeyEvent(KeyEvent event) {
InputManager.getInstance().injectInputEvent(event,
@@ -237,8 +280,8 @@
out.println(" press (Default: trackball)");
out.println(" roll <dx> <dy> (Default: trackball)");
out.println(" motionevent <DOWN|UP|MOVE|CANCEL> <x> <y> (Default: touchscreen)");
- out.println(" keycombination <key code 1> <key code 2> ..."
- + " (Default: keyboard)");
+ out.println(" keycombination [-t duration(ms)] <key code 1> <key code 2> ..."
+ + " (Default: keyboard, the key order is important here.)");
}
}
@@ -459,8 +502,16 @@
private void runKeyCombination(int inputSource, int displayId) {
String arg = getNextArgRequired();
- ArrayList<Integer> keyCodes = new ArrayList<>();
+ // Get duration (optional).
+ long duration = 0;
+ if ("-t".equals(arg)) {
+ arg = getNextArgRequired();
+ duration = Integer.parseInt(arg);
+ arg = getNextArgRequired();
+ }
+
+ IntArray keyCodes = new IntArray();
while (arg != null) {
final int keyCode = KeyEvent.keyCodeFromString(arg);
if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
@@ -475,7 +526,7 @@
throw new IllegalArgumentException("keycombination requires at least 2 keycodes");
}
- sendKeyCombination(inputSource, keyCodes, displayId);
+ sendKeyCombination(inputSource, keyCodes, displayId, duration);
}
private void injectKeyEventAsync(KeyEvent event) {
@@ -483,16 +534,21 @@
InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}
- private void sendKeyCombination(int inputSource, ArrayList<Integer> keyCodes, int displayId) {
+ private void sendKeyCombination(int inputSource, IntArray keyCodes, int displayId,
+ long duration) {
final long now = SystemClock.uptimeMillis();
final int count = keyCodes.size();
final KeyEvent[] events = new KeyEvent[count];
+ int metaState = 0;
for (int i = 0; i < count; i++) {
- final KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCodes.get(i), 0,
- 0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/,
+ final int keyCode = keyCodes.get(i);
+ final KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0,
+ metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/,
inputSource);
event.setDisplayId(displayId);
events[i] = event;
+ // The order is important here, metaState could be updated and applied to the next key.
+ metaState |= MODIFIER.getOrDefault(keyCode, 0);
}
for (KeyEvent event: events) {
@@ -501,7 +557,7 @@
injectKeyEventAsync(event);
}
- sleep(ViewConfiguration.getTapTimeout());
+ sleep(duration);
for (KeyEvent event: events) {
injectKeyEventAsync(KeyEvent.changeAction(event, KeyEvent.ACTION_UP));
@@ -513,7 +569,7 @@
*
* @param milliseconds The time to sleep in milliseconds.
*/
- private void sleep(int milliseconds) {
+ private void sleep(long milliseconds) {
try {
Thread.sleep(milliseconds);
} catch (InterruptedException e) {
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 6e2fe52..09780f3 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -48,7 +48,6 @@
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
-import android.content.pm.pkg.PackageUserState;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -70,6 +69,7 @@
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.pkg.PackageUserState;
import java.io.ByteArrayInputStream;
import java.io.File;
diff --git a/services/core/java/com/android/server/locales/TEST_MAPPING b/services/core/java/com/android/server/locales/TEST_MAPPING
new file mode 100644
index 0000000..72b9c48
--- /dev/null
+++ b/services/core/java/com/android/server/locales/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.locales."
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 1c04d8c..389adb0 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -72,6 +72,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
/**
* @hide
@@ -146,6 +147,8 @@
private final SensorPrivacyManagerInternal mSensorPrivacyManagerInternal;
+ private final Map<Integer, AtomicLong> mLastRestartTimestampMap = new HashMap<>();
+
/**
* Class extending the callback to register with a Context Hub.
*/
@@ -223,6 +226,9 @@
HashMap<Integer, IContextHubClient> defaultClientMap = new HashMap<>();
for (int contextHubId : mContextHubIdToInfoMap.keySet()) {
+ mLastRestartTimestampMap.put(contextHubId,
+ new AtomicLong(SystemClock.elapsedRealtimeNanos()));
+
ContextHubInfo contextHubInfo = mContextHubIdToInfoMap.get(contextHubId);
IContextHubClient client = mClientManager.registerClient(
contextHubInfo, createDefaultClientCallback(contextHubId),
@@ -700,6 +706,12 @@
*/
private void handleHubEventCallback(int contextHubId, int eventType) {
if (eventType == CONTEXT_HUB_EVENT_RESTARTED) {
+ long now = SystemClock.elapsedRealtimeNanos();
+ long lastRestartTimeNs = mLastRestartTimestampMap.get(contextHubId).getAndSet(now);
+ ContextHubStatsLog.write(
+ ContextHubStatsLog.CONTEXT_HUB_RESTARTED, now - lastRestartTimeNs,
+ contextHubId);
+
sendLocationSettingUpdate();
sendWifiSettingUpdate(true /* forceUpdate */);
sendAirplaneModeSettingUpdate();
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index ddaaa1e..7d31287 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -114,9 +114,10 @@
static final String ATT_VERSION = "version";
static final String ATT_DEFAULTS = "defaults";
static final String ATT_USER_SET = "user_set_services";
+ static final String ATT_USER_SET_OLD = "user_set";
static final String ATT_USER_CHANGED = "user_changed";
- static final int DB_VERSION = 4;
+ static final String DB_VERSION = "4";
static final int APPROVAL_BY_PACKAGE = 0;
static final int APPROVAL_BY_COMPONENT = 1;
@@ -482,7 +483,7 @@
public void writeXml(TypedXmlSerializer out, boolean forBackup, int userId) throws IOException {
out.startTag(null, getConfig().xmlTag);
- out.attributeInt(null, ATT_VERSION, DB_VERSION);
+ out.attributeInt(null, ATT_VERSION, Integer.parseInt(DB_VERSION));
writeDefaults(out);
@@ -615,6 +616,7 @@
// read grants
int type;
String version = XmlUtils.readStringAttribute(parser, ATT_VERSION);
+ boolean needUpgradeUserset = false;
readDefaults(parser);
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
String tag = parser.getName();
@@ -633,13 +635,42 @@
final boolean isPrimary =
parser.getAttributeBoolean(null, ATT_IS_PRIMARY, true);
+ // Load three different userSet attributes from xml
+ // user_changed, not null if version == 4 and is NAS setting
final String isUserChanged = XmlUtils.readStringAttribute(parser,
ATT_USER_CHANGED);
- String userSetComponent = null;
- if (isUserChanged == null) {
- userSetComponent = XmlUtils.readStringAttribute(parser, ATT_USER_SET);
+ // user_set, not null if version <= 3
+ final String isUserChanged_Old = XmlUtils.readStringAttribute(parser,
+ ATT_USER_SET_OLD);
+ // user_set_services, not null if version >= 3 and is non-NAS setting
+ String userSetComponent = XmlUtils.readStringAttribute(parser, ATT_USER_SET);
+
+ // since the same xml version may have different userSet attributes,
+ // we need to check both xml version and userSet values to know how to set
+ // the userSetComponent/mIsUserChanged to the correct value
+ if (DB_VERSION.equals(version)) {
+ // version 4, NAS contains user_changed and
+ // NLS/others contain user_set_services
+ if (isUserChanged == null) { //NLS
+ userSetComponent = TextUtils.emptyIfNull(userSetComponent);
+ } else { //NAS
+ mIsUserChanged.put(resolvedUserId, Boolean.valueOf(isUserChanged));
+ userSetComponent = Boolean.valueOf(isUserChanged) ? approved : "";
+ }
} else {
- mIsUserChanged.put(resolvedUserId, Boolean.valueOf(isUserChanged));
+ // version 3 may contain user_set (R) or user_set_services (S)
+ // version 2 or older contain user_set or nothing
+ needUpgradeUserset = true;
+ if (userSetComponent == null) { //contains user_set
+ if (isUserChanged_Old != null && Boolean.valueOf(isUserChanged_Old)) {
+ //user_set = true
+ userSetComponent = approved;
+ mIsUserChanged.put(resolvedUserId, true);
+ needUpgradeUserset = false;
+ } else {
+ userSetComponent = "";
+ }
+ }
}
readExtraAttributes(tag, parser, resolvedUserId);
if (allowedManagedServicePackages == null || allowedManagedServicePackages.test(
@@ -659,7 +690,6 @@
|| DB_VERSION_1.equals(version)
|| DB_VERSION_2.equals(version)
|| DB_VERSION_3.equals(version);
- boolean needUpgradeUserset = DB_VERSION_3.equals(version);
if (isOldVersion) {
upgradeDefaultsXmlVersion();
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f701c2a..5516109 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2647,7 +2647,12 @@
@Override
public void addAutoGroupSummary(int userId, String pkg, String triggeringKey) {
- createAutoGroupSummary(userId, pkg, triggeringKey);
+ NotificationRecord r = createAutoGroupSummary(userId, pkg, triggeringKey);
+ if (r != null) {
+ final boolean isAppForeground =
+ mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
+ mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground));
+ }
}
@Override
@@ -5083,6 +5088,33 @@
@Override
public boolean matchesCallFilter(Bundle extras) {
+ // Because matchesCallFilter may use contact data to filter calls, the callers of this
+ // method need to either have notification listener access or permission to read
+ // contacts.
+ boolean systemAccess = false;
+ try {
+ enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
+ systemAccess = true;
+ } catch (SecurityException e) {
+ }
+
+ boolean listenerAccess = false;
+ try {
+ String[] pkgNames = mPackageManager.getPackagesForUid(Binder.getCallingUid());
+ for (int i = 0; i < pkgNames.length; i++) {
+ // in most cases there should only be one package here
+ listenerAccess |= mListeners.hasAllowedListener(pkgNames[i],
+ Binder.getCallingUserHandle().getIdentifier());
+ }
+ } catch (RemoteException e) {
+ } finally {
+ if (!systemAccess && !listenerAccess) {
+ getContext().enforceCallingPermission(Manifest.permission.READ_CONTACTS,
+ "matchesCallFilter requires listener permission, contacts read access,"
+ + " or system level access");
+ }
+ }
+
return mZenModeHelper.matchesCallFilter(
Binder.getCallingUserHandle(),
extras,
@@ -5559,6 +5591,12 @@
return isPackagePausedOrSuspended(pkg, Binder.getCallingUid());
}
+ @Override
+ public boolean isPermissionFixed(String pkg, @UserIdInt int userId) {
+ enforceSystemOrSystemUI("isPermissionFixed");
+ return mPermissionHelper.isPermissionFixed(pkg, userId);
+ }
+
private void verifyPrivilegedListener(INotificationListener token, UserHandle user,
boolean assistantAllowed) {
ManagedServiceInfo info;
@@ -5757,18 +5795,23 @@
return summaries != null && summaries.containsKey(sbn.getPackageName());
}
- // Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
- private void createAutoGroupSummary(int userId, String pkg, String triggeringKey) {
+ // Creates a 'fake' summary for a package that has exceeded the solo-notification limit.
+ NotificationRecord createAutoGroupSummary(int userId, String pkg, String triggeringKey) {
NotificationRecord summaryRecord = null;
- final boolean isAppForeground =
- mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
+ boolean isPermissionFixed = mPermissionHelper.isMigrationEnabled()
+ ? mPermissionHelper.isPermissionFixed(pkg, userId) : false;
synchronized (mNotificationLock) {
NotificationRecord notificationRecord = mNotificationsByKey.get(triggeringKey);
if (notificationRecord == null) {
// The notification could have been cancelled again already. A successive
// adjustment will post a summary if needed.
- return;
+ return null;
}
+ NotificationChannel channel = notificationRecord.getChannel();
+ boolean isImportanceFixed = mPermissionHelper.isMigrationEnabled()
+ ? isPermissionFixed
+ : (channel.isImportanceLockedByOEM()
+ || channel.isImportanceLockedByCriticalDeviceFunction());
final StatusBarNotification adjustedSbn = notificationRecord.getSbn();
userId = adjustedSbn.getUser().getIdentifier();
ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
@@ -5812,6 +5855,7 @@
System.currentTimeMillis());
summaryRecord = new NotificationRecord(getContext(), summarySbn,
notificationRecord.getChannel());
+ summaryRecord.setImportanceFixed(isImportanceFixed);
summaryRecord.setIsAppImportanceLocked(
notificationRecord.getIsAppImportanceLocked());
summaries.put(pkg, summarySbn.getKey());
@@ -5819,9 +5863,10 @@
if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
summaryRecord.getSbn().getId(), summaryRecord.getSbn().getTag(), summaryRecord,
true)) {
- mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord, isAppForeground));
+ return summaryRecord;
}
}
+ return null;
}
private String disableNotificationEffects(NotificationRecord record) {
@@ -6336,6 +6381,11 @@
r.setPostSilently(postSilently);
r.setFlagBubbleRemoved(false);
r.setPkgAllowedAsConvo(mMsgPkgsAllowedAsConvos.contains(pkg));
+ boolean isImportanceFixed = mPermissionHelper.isMigrationEnabled()
+ ? mPermissionHelper.isPermissionFixed(pkg, userId)
+ : (channel.isImportanceLockedByOEM()
+ || channel.isImportanceLockedByCriticalDeviceFunction());
+ r.setImportanceFixed(isImportanceFixed);
if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
final boolean fgServiceShown = channel.isFgServiceShown();
@@ -10954,6 +11004,23 @@
}
return false;
}
+
+ // Returns whether there is a component with listener access granted that is associated
+ // with the given package name / user ID.
+ boolean hasAllowedListener(String packageName, int userId) {
+ if (packageName == null) {
+ return false;
+ }
+
+ // Loop through allowed components to compare package names
+ List<ComponentName> allowedComponents = getAllowedComponents(userId);
+ for (int i = 0; i < allowedComponents.size(); i++) {
+ if (allowedComponents.get(i).getPackageName().equals(packageName)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
// TODO (b/194833441): remove when we've fully migrated to a permission
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 64abe2e..96d7521 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -193,6 +193,7 @@
private boolean mHasSentValidMsg;
private boolean mAppDemotedFromConvo;
private boolean mPkgAllowedAsConvo;
+ private boolean mImportanceFixed;
/**
* Whether this notification (and its channels) should be considered user locked. Used in
* conjunction with user sentiment calculation.
@@ -823,6 +824,14 @@
return mAssistantImportance;
}
+ public void setImportanceFixed(boolean fixed) {
+ mImportanceFixed = fixed;
+ }
+
+ public boolean isImportanceFixed() {
+ return mImportanceFixed;
+ }
+
/**
* Recalculates the importance of the record after fields affecting importance have changed,
* and records an explanation.
@@ -834,8 +843,7 @@
// Consider Notification Assistant and system overrides to importance. If both, system wins.
if (!getChannel().hasUserSetImportance()
&& mAssistantImportance != IMPORTANCE_UNSPECIFIED
- && !getChannel().isImportanceLockedByOEM()
- && !getChannel().isImportanceLockedByCriticalDeviceFunction()) {
+ && !mImportanceFixed) {
mImportance = mAssistantImportance;
mImportanceExplanationCode = MetricsEvent.IMPORTANCE_EXPLANATION_ASST;
}
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index a3c009a..f53bb75 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -21,20 +21,27 @@
import static android.permission.PermissionManager.PERMISSION_GRANTED;
import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.os.RemoteException;
import android.permission.IPermissionManager;
+import android.util.ArrayMap;
+import android.util.Pair;
import android.util.Slog;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
/**
* NotificationManagerService helper for querying/setting the app-level notification permission
@@ -42,7 +49,7 @@
public final class PermissionHelper {
private static final String TAG = "PermissionHelper";
- private static String NOTIFICATION_PERMISSION = Manifest.permission.POST_NOTIFICATIONS;
+ private static final String NOTIFICATION_PERMISSION = Manifest.permission.POST_NOTIFICATIONS;
private final PermissionManagerServiceInternal mPmi;
private final IPackageManager mPackageManager;
@@ -75,9 +82,9 @@
* Returns all of the apps that have requested the notification permission in a given user.
* Must not be called with a lock held. Format: uid, packageName
*/
- public Map<Integer, String> getAppsRequestingPermission(int userId) {
+ Set<Pair<Integer, String>> getAppsRequestingPermission(int userId) {
assertFlag();
- Map<Integer, String> requested = new HashMap<>();
+ Set<Pair<Integer, String>> requested = new HashSet<>();
List<PackageInfo> pkgs = getInstalledPackages(userId);
for (PackageInfo pi : pkgs) {
// when data was stored in PreferencesHelper, we only had data for apps that
@@ -88,7 +95,7 @@
}
for (String perm : pi.requestedPermissions) {
if (NOTIFICATION_PERMISSION.equals(perm)) {
- requested.put(pi.applicationInfo.uid, pi.packageName);
+ requested.add(new Pair<>(pi.applicationInfo.uid, pi.packageName));
break;
}
}
@@ -113,9 +120,9 @@
* Returns a list of apps that hold the notification permission. Must not be called
* with a lock held. Format: uid, packageName.
*/
- public Map<Integer, String> getAppsGrantedPermission(int userId) {
+ Set<Pair<Integer, String>> getAppsGrantedPermission(int userId) {
assertFlag();
- Map<Integer, String> granted = new HashMap<>();
+ Set<Pair<Integer, String>> granted = new HashSet<>();
ParceledListSlice<PackageInfo> parceledList = null;
try {
parceledList = mPackageManager.getPackagesHoldingPermissions(
@@ -127,11 +134,24 @@
return granted;
}
for (PackageInfo pi : parceledList.getList()) {
- granted.put(pi.applicationInfo.uid, pi.packageName);
+ granted.add(new Pair<>(pi.applicationInfo.uid, pi.packageName));
}
return granted;
}
+ public @NonNull
+ ArrayMap<Pair<Integer, String>, Boolean> getNotificationPermissionValues(
+ int userId) {
+ assertFlag();
+ ArrayMap<Pair<Integer, String>, Boolean> notifPermissions = new ArrayMap<>();
+ Set<Pair<Integer, String>> allRequestingUids = getAppsRequestingPermission(userId);
+ Set<Pair<Integer, String>> allApprovedUids = getAppsGrantedPermission(userId);
+ for (Pair<Integer, String> pair : allRequestingUids) {
+ notifPermissions.put(pair, allApprovedUids.contains(pair));
+ }
+ return notifPermissions;
+ }
+
/**
* Grants or revokes the notification permission for a given package/user. UserSet should
* only be true if this method is being called to migrate existing user choice, because it
@@ -157,9 +177,66 @@
}
}
+ public void setNotificationPermission(PackagePermission pkgPerm) {
+ assertFlag();
+ setNotificationPermission(
+ pkgPerm.packageName, pkgPerm.userId, pkgPerm.granted, pkgPerm.userSet);
+ }
+
+ public boolean isPermissionFixed(String packageName, @UserIdInt int userId) {
+ assertFlag();
+ try {
+ int flags = mPermManager.getPermissionFlags(packageName, NOTIFICATION_PERMISSION,
+ userId);
+ return (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0
+ || (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not reach system server", e);
+ }
+ return false;
+ }
+
private void assertFlag() {
if (!mMigrationEnabled) {
throw new IllegalStateException("Method called without checking flag value");
}
}
+
+ public static class PackagePermission {
+ public final String packageName;
+ public final @UserIdInt int userId;
+ public final boolean granted;
+ public final boolean userSet;
+
+ public PackagePermission(String pkg, int userId, boolean granted, boolean userSet) {
+ this.packageName = pkg;
+ this.userId = userId;
+ this.granted = granted;
+ this.userSet = userSet;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PackagePermission that = (PackagePermission) o;
+ return userId == that.userId && granted == that.granted && userSet == that.userSet
+ && Objects.equals(packageName, that.packageName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(packageName, userId, granted, userSet);
+ }
+
+ @Override
+ public String toString() {
+ return "PackagePermission{" +
+ "packageName='" + packageName + '\'' +
+ ", userId=" + userId +
+ ", granted=" + granted +
+ ", userSet=" + userSet +
+ '}';
+ }
+ }
}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index c74813a..16fe4b6 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -21,6 +21,7 @@
import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
@@ -68,6 +69,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
+import com.android.server.notification.PermissionHelper.PackagePermission;
import org.json.JSONArray;
import org.json.JSONException;
@@ -89,7 +91,7 @@
public class PreferencesHelper implements RankingConfig {
private static final String TAG = "NotificationPrefHelper";
- private static final int XML_VERSION = 2;
+ private final int XML_VERSION;
/** What version to check to do the upgrade for bubbles. */
private static final int XML_VERSION_BUBBLES_UPGRADE = 1;
@VisibleForTesting
@@ -201,6 +203,12 @@
mAppOps = appOpsManager;
mStatsEventBuilderFactory = statsEventBuilderFactory;
+ if (mPermissionHelper.isMigrationEnabled()) {
+ XML_VERSION = 3;
+ } else {
+ XML_VERSION = 2;
+ }
+
updateBadgingEnabled();
updateBubblesEnabled();
updateMediaNotificationFilteringEnabled();
@@ -217,11 +225,13 @@
final int xmlVersion = parser.getAttributeInt(null, ATT_VERSION, -1);
boolean upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE;
+ boolean migrateToPermission = (xmlVersion < XML_VERSION);
+ ArrayList<PermissionHelper.PackagePermission> pkgPerms = new ArrayList<>();
synchronized (mPackagePreferences) {
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
tag = parser.getName();
if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) {
- return;
+ break;
}
if (type == XmlPullParser.START_TAG) {
if (TAG_STATUS_ICONS.equals(tag)) {
@@ -252,11 +262,12 @@
? BUBBLE_PREFERENCE_ALL
: parser.getAttributeInt(
null, ATT_ALLOW_BUBBLE, DEFAULT_BUBBLE_PREFERENCE);
+ int appImportance = parser.getAttributeInt(
+ null, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
PackagePreferences r = getOrCreatePackagePreferencesLocked(
name, userId, uid,
- parser.getAttributeInt(
- null, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
+ appImportance,
parser.getAttributeInt(
null, ATT_PRIORITY, DEFAULT_PRIORITY),
parser.getAttributeInt(
@@ -264,8 +275,6 @@
parser.getAttributeBoolean(
null, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE),
bubblePref);
- r.importance = parser.getAttributeInt(
- null, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
r.priority = parser.getAttributeInt(
null, ATT_PRIORITY, DEFAULT_PRIORITY);
r.visibility = parser.getAttributeInt(
@@ -324,12 +333,14 @@
} else {
channel.populateFromXml(parser);
}
- channel.setImportanceLockedByCriticalDeviceFunction(
- r.defaultAppLockedImportance);
- channel.setImportanceLockedByOEM(r.oemLockedImportance);
- if (!channel.isImportanceLockedByOEM()) {
- if (r.oemLockedChannels.contains(channel.getId())) {
- channel.setImportanceLockedByOEM(true);
+ if (!mPermissionHelper.isMigrationEnabled()) {
+ channel.setImportanceLockedByCriticalDeviceFunction(
+ r.defaultAppLockedImportance);
+ channel.setImportanceLockedByOEM(r.oemLockedImportance);
+ if (!channel.isImportanceLockedByOEM()) {
+ if (r.oemLockedChannels.contains(channel.getId())) {
+ channel.setImportanceLockedByOEM(true);
+ }
}
}
@@ -338,6 +349,7 @@
}
}
}
+
// Delegate
if (TAG_DELEGATE.equals(tagName)) {
int delegateId =
@@ -365,12 +377,30 @@
} catch (PackageManager.NameNotFoundException e) {
Slog.e(TAG, "deleteDefaultChannelIfNeededLocked - Exception: " + e);
}
+
+ if (migrateToPermission) {
+ boolean hasChangedChannel = false;
+ for (NotificationChannel channel : r.channels.values()) {
+ if (channel.getUserLockedFields() != 0) {
+ hasChangedChannel = true;
+ break;
+ }
+ }
+ PackagePermission pkgPerm = new PackagePermission(
+ r.pkg, userId, appImportance != IMPORTANCE_NONE,
+ hasChangedChannel || appImportance == IMPORTANCE_NONE);
+ pkgPerms.add(pkgPerm);
+ } else {
+ r.importance = appImportance;
+ }
}
}
}
}
}
- throw new IllegalStateException("Failed to reach END_DOCUMENT");
+ for (PackagePermission p : pkgPerms) {
+ mPermissionHelper.setNotificationPermission(p);
+ }
}
private boolean isShortcutOk(NotificationChannel channel) {
@@ -400,19 +430,13 @@
private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg,
int uid) {
+ // TODO (b/194833441): use permissionhelper instead of DEFAULT_IMPORTANCE
return getOrCreatePackagePreferencesLocked(pkg, UserHandle.getUserId(uid), uid,
DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE,
DEFAULT_BUBBLE_PREFERENCE);
}
private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg,
- @UserIdInt int userId, int uid) {
- return getOrCreatePackagePreferencesLocked(pkg, userId, uid,
- DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE,
- DEFAULT_BUBBLE_PREFERENCE);
- }
-
- private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg,
@UserIdInt int userId, int uid, int importance, int priority, int visibility,
boolean showBadge, int bubblePreference) {
final String key = packagePreferencesKey(pkg, uid);
@@ -533,6 +557,10 @@
out.attributeBoolean(null, ATT_HIDE_SILENT, mHideSilentStatusBarIcons);
out.endTag(null, TAG_STATUS_ICONS);
}
+ ArrayMap<Pair<Integer, String>, Boolean> notifPermissions = new ArrayMap<>();
+ if (mPermissionHelper.isMigrationEnabled() && forBackup) {
+ notifPermissions = mPermissionHelper.getNotificationPermissionValues(userId);
+ }
synchronized (mPackagePreferences) {
final int N = mPackagePreferences.size();
@@ -541,78 +569,81 @@
if (forBackup && UserHandle.getUserId(r.uid) != userId) {
continue;
}
- final boolean hasNonDefaultSettings =
- r.importance != DEFAULT_IMPORTANCE
- || r.priority != DEFAULT_PRIORITY
- || r.visibility != DEFAULT_VISIBILITY
- || r.showBadge != DEFAULT_SHOW_BADGE
- || r.lockedAppFields != DEFAULT_LOCKED_APP_FIELDS
- || r.channels.size() > 0
- || r.groups.size() > 0
- || r.delegate != null
- || r.bubblePreference != DEFAULT_BUBBLE_PREFERENCE
- || r.hasSentInvalidMessage
- || r.userDemotedMsgApp
- || r.hasSentValidMessage;
- if (hasNonDefaultSettings) {
- out.startTag(null, TAG_PACKAGE);
- out.attribute(null, ATT_NAME, r.pkg);
+ out.startTag(null, TAG_PACKAGE);
+ out.attribute(null, ATT_NAME, r.pkg);
+ if (!notifPermissions.isEmpty()) {
+ Pair<Integer, String> app = new Pair(r.uid, r.pkg);
+ out.attributeInt(null, ATT_IMPORTANCE, notifPermissions.get(app)
+ ? IMPORTANCE_DEFAULT
+ : IMPORTANCE_NONE);
+ notifPermissions.remove(app);
+ } else {
if (r.importance != DEFAULT_IMPORTANCE) {
out.attributeInt(null, ATT_IMPORTANCE, r.importance);
}
- if (r.priority != DEFAULT_PRIORITY) {
- out.attributeInt(null, ATT_PRIORITY, r.priority);
- }
- if (r.visibility != DEFAULT_VISIBILITY) {
- out.attributeInt(null, ATT_VISIBILITY, r.visibility);
- }
- if (r.bubblePreference != DEFAULT_BUBBLE_PREFERENCE) {
- out.attributeInt(null, ATT_ALLOW_BUBBLE, r.bubblePreference);
- }
- out.attributeBoolean(null, ATT_SHOW_BADGE, r.showBadge);
- out.attributeInt(null, ATT_APP_USER_LOCKED_FIELDS,
- r.lockedAppFields);
- out.attributeBoolean(null, ATT_SENT_INVALID_MESSAGE,
- r.hasSentInvalidMessage);
- out.attributeBoolean(null, ATT_SENT_VALID_MESSAGE,
- r.hasSentValidMessage);
- out.attributeBoolean(null, ATT_USER_DEMOTED_INVALID_MSG_APP,
- r.userDemotedMsgApp);
-
- if (!forBackup) {
- out.attributeInt(null, ATT_UID, r.uid);
- }
-
- if (r.delegate != null) {
- out.startTag(null, TAG_DELEGATE);
-
- out.attribute(null, ATT_NAME, r.delegate.mPkg);
- out.attributeInt(null, ATT_UID, r.delegate.mUid);
- if (r.delegate.mEnabled != Delegate.DEFAULT_ENABLED) {
- out.attributeBoolean(null, ATT_ENABLED, r.delegate.mEnabled);
- }
- if (r.delegate.mUserAllowed != Delegate.DEFAULT_USER_ALLOWED) {
- out.attributeBoolean(null, ATT_USER_ALLOWED, r.delegate.mUserAllowed);
- }
- out.endTag(null, TAG_DELEGATE);
- }
-
- for (NotificationChannelGroup group : r.groups.values()) {
- group.writeXml(out);
- }
-
- for (NotificationChannel channel : r.channels.values()) {
- if (forBackup) {
- if (!channel.isDeleted()) {
- channel.writeXmlForBackup(out, mContext);
- }
- } else {
- channel.writeXml(out);
- }
- }
-
- out.endTag(null, TAG_PACKAGE);
}
+ if (r.priority != DEFAULT_PRIORITY) {
+ out.attributeInt(null, ATT_PRIORITY, r.priority);
+ }
+ if (r.visibility != DEFAULT_VISIBILITY) {
+ out.attributeInt(null, ATT_VISIBILITY, r.visibility);
+ }
+ if (r.bubblePreference != DEFAULT_BUBBLE_PREFERENCE) {
+ out.attributeInt(null, ATT_ALLOW_BUBBLE, r.bubblePreference);
+ }
+ out.attributeBoolean(null, ATT_SHOW_BADGE, r.showBadge);
+ out.attributeInt(null, ATT_APP_USER_LOCKED_FIELDS,
+ r.lockedAppFields);
+ out.attributeBoolean(null, ATT_SENT_INVALID_MESSAGE,
+ r.hasSentInvalidMessage);
+ out.attributeBoolean(null, ATT_SENT_VALID_MESSAGE,
+ r.hasSentValidMessage);
+ out.attributeBoolean(null, ATT_USER_DEMOTED_INVALID_MSG_APP,
+ r.userDemotedMsgApp);
+
+ if (!forBackup) {
+ out.attributeInt(null, ATT_UID, r.uid);
+ }
+
+ if (r.delegate != null) {
+ out.startTag(null, TAG_DELEGATE);
+
+ out.attribute(null, ATT_NAME, r.delegate.mPkg);
+ out.attributeInt(null, ATT_UID, r.delegate.mUid);
+ if (r.delegate.mEnabled != Delegate.DEFAULT_ENABLED) {
+ out.attributeBoolean(null, ATT_ENABLED, r.delegate.mEnabled);
+ }
+ if (r.delegate.mUserAllowed != Delegate.DEFAULT_USER_ALLOWED) {
+ out.attributeBoolean(null, ATT_USER_ALLOWED, r.delegate.mUserAllowed);
+ }
+ out.endTag(null, TAG_DELEGATE);
+ }
+
+ for (NotificationChannelGroup group : r.groups.values()) {
+ group.writeXml(out);
+ }
+
+ for (NotificationChannel channel : r.channels.values()) {
+ if (forBackup) {
+ if (!channel.isDeleted()) {
+ channel.writeXmlForBackup(out, mContext);
+ }
+ } else {
+ channel.writeXml(out);
+ }
+ }
+
+ out.endTag(null, TAG_PACKAGE);
+ }
+ }
+ // Some apps have permissions set but don't have expanded notification settings
+ if (!notifPermissions.isEmpty()) {
+ for (Pair<Integer, String> app : notifPermissions.keySet()) {
+ out.startTag(null, TAG_PACKAGE);
+ out.attribute(null, ATT_NAME, app.second);
+ out.attributeInt(null, ATT_IMPORTANCE,
+ notifPermissions.get(app) ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE);
+ out.endTag(null, TAG_PACKAGE);
}
}
out.endTag(null, TAG_RANKING);
@@ -942,13 +973,16 @@
: NotificationChannel.DEFAULT_ALLOW_BUBBLE);
}
clearLockedFieldsLocked(channel);
- channel.setImportanceLockedByOEM(r.oemLockedImportance);
- if (!channel.isImportanceLockedByOEM()) {
- if (r.oemLockedChannels.contains(channel.getId())) {
- channel.setImportanceLockedByOEM(true);
+ if (!mPermissionHelper.isMigrationEnabled()) {
+ channel.setImportanceLockedByOEM(r.oemLockedImportance);
+ if (!channel.isImportanceLockedByOEM()) {
+ if (r.oemLockedChannels.contains(channel.getId())) {
+ channel.setImportanceLockedByOEM(true);
+ }
}
+ channel.setImportanceLockedByCriticalDeviceFunction(
+ r.defaultAppLockedImportance);
}
- channel.setImportanceLockedByCriticalDeviceFunction(r.defaultAppLockedImportance);
if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
channel.setLockscreenVisibility(
NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
@@ -1028,16 +1062,23 @@
} else {
updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
}
- // no importance updates are allowed if OEM blocked it
- updatedChannel.setImportanceLockedByOEM(channel.isImportanceLockedByOEM());
- if (updatedChannel.isImportanceLockedByOEM()) {
- updatedChannel.setImportance(channel.getImportance());
- }
- updatedChannel.setImportanceLockedByCriticalDeviceFunction(
- r.defaultAppLockedImportance);
- if (updatedChannel.isImportanceLockedByCriticalDeviceFunction()
- && updatedChannel.getImportance() == IMPORTANCE_NONE) {
- updatedChannel.setImportance(channel.getImportance());
+
+ if (mPermissionHelper.isMigrationEnabled()) {
+ if (mPermissionHelper.isPermissionFixed(r.pkg, UserHandle.getUserId(r.uid))) {
+ updatedChannel.setImportance(channel.getImportance());
+ }
+ } else {
+ // no importance updates are allowed if OEM blocked it
+ updatedChannel.setImportanceLockedByOEM(channel.isImportanceLockedByOEM());
+ if (updatedChannel.isImportanceLockedByOEM()) {
+ updatedChannel.setImportance(channel.getImportance());
+ }
+ updatedChannel.setImportanceLockedByCriticalDeviceFunction(
+ r.defaultAppLockedImportance);
+ if (updatedChannel.isImportanceLockedByCriticalDeviceFunction()
+ && updatedChannel.getImportance() == IMPORTANCE_NONE) {
+ updatedChannel.setImportance(channel.getImportance());
+ }
}
r.channels.put(updatedChannel.getId(), updatedChannel);
@@ -1217,6 +1258,9 @@
}
public void lockChannelsForOEM(String[] appOrChannelList) {
+ if (mPermissionHelper.isMigrationEnabled()) {
+ return;
+ }
if (appOrChannelList == null) {
return;
}
@@ -1265,6 +1309,9 @@
public void updateDefaultApps(int userId, ArraySet<String> toRemove,
ArraySet<Pair<String, Integer>> toAdd) {
+ if (mPermissionHelper.isMigrationEnabled()) {
+ return;
+ }
synchronized (mPackagePreferences) {
for (PackagePreferences p : mPackagePreferences.values()) {
if (userId == UserHandle.getUserId(p.uid)) {
@@ -1278,7 +1325,8 @@
}
if (toAdd != null) {
for (Pair<String, Integer> approvedApp : toAdd) {
- PackagePreferences p = getOrCreatePackagePreferencesLocked(approvedApp.first,
+ PackagePreferences p = getOrCreatePackagePreferencesLocked(
+ approvedApp.first,
approvedApp.second);
p.defaultAppLockedImportance = true;
for (NotificationChannel channel : p.channels.values()) {
@@ -1885,7 +1933,7 @@
public void dump(PrintWriter pw, String prefix,
@NonNull NotificationManagerService.DumpFilter filter) {
pw.print(prefix);
- pw.println("per-package config:");
+ pw.println("per-package config version: " + XML_VERSION);
pw.println("PackagePreferences:");
synchronized (mPackagePreferences) {
@@ -1905,7 +1953,7 @@
mRestoredWithoutUids);
}
- private static void dumpPackagePreferencesLocked(PrintWriter pw, String prefix,
+ private void dumpPackagePreferencesLocked(PrintWriter pw, String prefix,
@NonNull NotificationManagerService.DumpFilter filter,
ArrayMap<String, PackagePreferences> packagePreferences) {
final int N = packagePreferences.size();
@@ -1918,7 +1966,7 @@
pw.print(" (");
pw.print(r.uid == UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
pw.print(')');
- if (r.importance != DEFAULT_IMPORTANCE) {
+ if (!mPermissionHelper.isMigrationEnabled() && r.importance != DEFAULT_IMPORTANCE) {
pw.print(" importance=");
pw.print(NotificationListenerService.Ranking.importanceToString(r.importance));
}
diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
index 14affe7..92cdce7 100644
--- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
+++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
@@ -40,6 +40,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.notification.NotificationManagerService.DumpFilter;
+import com.android.server.pm.PackageManagerService;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -218,12 +219,7 @@
private void updateAlarm(long now, long time) {
final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
- final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext,
- REQUEST_CODE_EVALUATE,
- new Intent(ACTION_EVALUATE)
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
- .putExtra(EXTRA_TIME, time),
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ final PendingIntent pendingIntent = getPendingIntent(time);
alarms.cancel(pendingIntent);
if (time > now) {
if (DEBUG) Slog.d(TAG, String.format("Scheduling evaluate for %s, in %s, now=%s",
@@ -234,6 +230,17 @@
}
}
+ @VisibleForTesting
+ PendingIntent getPendingIntent(long time) {
+ return PendingIntent.getBroadcast(mContext,
+ REQUEST_CODE_EVALUATE,
+ new Intent(ACTION_EVALUATE)
+ .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ .putExtra(EXTRA_TIME, time),
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ }
+
public long getNextAlarm() {
final AlarmManager.AlarmClockInfo info = mAlarmManager.getNextAlarmClock(
ActivityManager.getCurrentUser());
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 27b1648..ee0b3d5 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -71,6 +71,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
@@ -81,7 +82,6 @@
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.SystemService;
-
import com.android.server.pm.UserManagerService;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -285,6 +285,12 @@
restoreSettings();
+ // Wipe all shell overlays on boot, to recover from a potentially broken device
+ String shellPkgName = TextUtils.emptyIfNull(
+ getContext().getString(android.R.string.config_systemShell));
+ mSettings.removeIf(overlayInfo -> overlayInfo.isFabricated
+ && shellPkgName.equals(overlayInfo.packageName));
+
initIfNeeded();
onSwitchUser(UserHandle.USER_SYSTEM);
@@ -891,6 +897,16 @@
throw new IllegalArgumentException(request.typeToString()
+ " unsupported for user " + request.userId);
}
+
+ // Normal apps are blocked from accessing OMS via SELinux, so to block non-root,
+ // non privileged callers, a simple check against the shell UID is sufficient, since
+ // that's the only exception from the other categories. This is enough while OMS
+ // is not a public API, but this will have to be changed if it's ever exposed.
+ if (callingUid == Process.SHELL_UID) {
+ EventLog.writeEvent(0x534e4554, "202768292", -1, "");
+ throw new IllegalArgumentException("Non-root shell cannot fabricate overlays");
+ }
+
realUserId = UserHandle.USER_ALL;
// Enforce that the calling process can only register and unregister fabricated
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index 9696d3d..6ee1981 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -26,8 +26,10 @@
import android.content.pm.PackageManager;
import android.content.pm.SELinuxUtil;
import android.content.pm.UserInfo;
+import android.os.CreateAppDataArgs;
import android.os.Environment;
import android.os.FileUtils;
+import android.os.Process;
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManager;
@@ -86,12 +88,20 @@
* <p>
* Verifies that directories exist and that ownership and labeling is
* correct for all installed apps. If there is an ownership mismatch, it
- * will try recovering system apps by wiping data; third-party app data is
- * left intact.
+ * will wipe and recreate the data.
* <p>
* <em>Note: To avoid a deadlock, do not call this method with {@code mLock} lock held</em>
*/
public void prepareAppDataAfterInstallLIF(AndroidPackage pkg) {
+ prepareAppDataPostCommitLIF(pkg, 0 /* previousAppId */);
+ }
+
+ /**
+ * For more details about data verification and previousAppId, check
+ * {@link #prepareAppData(Installer.Batch, AndroidPackage, int, int, int)}
+ * @see #prepareAppDataAfterInstallLIF(AndroidPackage)
+ */
+ public void prepareAppDataPostCommitLIF(AndroidPackage pkg, int previousAppId) {
final PackageSetting ps;
synchronized (mPm.mLock) {
ps = mPm.mSettings.getPackageLPr(pkg.getPackageName());
@@ -113,13 +123,9 @@
continue;
}
- // TODO@ashfall check ScanResult.mNeedsNewAppId, and if true instead
- // of creating app data, migrate / change ownership of existing
- // data.
-
if (ps.getInstalled(user.id)) {
// TODO: when user data is locked, mark that we're still dirty
- prepareAppData(batch, pkg, user.id, flags).thenRun(() -> {
+ prepareAppData(batch, pkg, previousAppId, user.id, flags).thenRun(() -> {
// Note: this code block is executed with the Installer lock
// already held, since it's invoked as a side-effect of
// executeBatchLI()
@@ -147,22 +153,26 @@
* Prepare app data for the given app.
* <p>
* Verifies that directories exist and that ownership and labeling is
- * correct for all installed apps. If there is an ownership mismatch, this
- * will try recovering system apps by wiping data; third-party app data is
- * left intact.
+ * correct for all installed apps. If there is an ownership mismatch:
+ * <ul>
+ * <li>If previousAppId < 0, app data will be migrated to the new app ID
+ * <li>If previousAppId == 0, no migration will happen and data will be wiped and recreated
+ * <li>If previousAppId > 0, it will migrate all data owned by previousAppId
+ * to the new app ID
+ * </ul>
*/
private @NonNull CompletableFuture<?> prepareAppData(@NonNull Installer.Batch batch,
- @Nullable AndroidPackage pkg, int userId, int flags) {
+ @Nullable AndroidPackage pkg, int previousAppId, int userId, int flags) {
if (pkg == null) {
Slog.wtf(TAG, "Package was null!", new Throwable());
return CompletableFuture.completedFuture(null);
}
- return prepareAppDataLeaf(batch, pkg, userId, flags);
+ return prepareAppDataLeaf(batch, pkg, previousAppId, userId, flags);
}
private void prepareAppDataAndMigrate(@NonNull Installer.Batch batch,
@NonNull AndroidPackage pkg, int userId, int flags, boolean maybeMigrateAppData) {
- prepareAppData(batch, pkg, userId, flags).thenRun(() -> {
+ prepareAppData(batch, pkg, Process.INVALID_UID, userId, flags).thenRun(() -> {
// Note: this code block is executed with the Installer lock
// already held, since it's invoked as a side-effect of
// executeBatchLI()
@@ -170,14 +180,14 @@
// We may have just shuffled around app data directories, so
// prepare them one more time
final Installer.Batch batchInner = new Installer.Batch();
- prepareAppData(batchInner, pkg, userId, flags);
+ prepareAppData(batchInner, pkg, Process.INVALID_UID, userId, flags);
executeBatchLI(batchInner);
}
});
}
private @NonNull CompletableFuture<?> prepareAppDataLeaf(@NonNull Installer.Batch batch,
- @NonNull AndroidPackage pkg, int userId, int flags) {
+ @NonNull AndroidPackage pkg, int previousAppId, int userId, int flags) {
if (DEBUG_APP_DATA) {
Slog.v(TAG, "prepareAppData for " + pkg.getPackageName() + " u" + userId + " 0x"
+ Integer.toHexString(flags));
@@ -200,65 +210,64 @@
final String seInfo = pkgSeInfo + seInfoUser;
final int targetSdkVersion = pkg.getTargetSdkVersion();
+ final CreateAppDataArgs args = Installer.buildCreateAppDataArgs(volumeUuid, packageName,
+ userId, flags, appId, seInfo, targetSdkVersion);
+ args.previousAppId = previousAppId;
- return batch.createAppData(volumeUuid, packageName, userId, flags, appId, seInfo,
- targetSdkVersion).whenComplete((ceDataInode, e) -> {
- // Note: this code block is executed with the Installer lock
- // already held, since it's invoked as a side-effect of
- // executeBatchLI()
- if (e != null) {
- logCriticalInfo(Log.WARN, "Failed to create app data for " + packageName
- + ", but trying to recover: " + e);
- destroyAppDataLeafLIF(pkg, userId, flags);
- try {
- ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId,
- flags, appId, seInfo, pkg.getTargetSdkVersion());
- logCriticalInfo(Log.DEBUG, "Recovery succeeded!");
- } catch (Installer.InstallerException e2) {
- logCriticalInfo(Log.DEBUG, "Recovery failed!");
- }
- }
+ return batch.createAppData(args).whenComplete((ceDataInode, e) -> {
+ // Note: this code block is executed with the Installer lock
+ // already held, since it's invoked as a side-effect of
+ // executeBatchLI()
+ if (e != null) {
+ logCriticalInfo(Log.WARN, "Failed to create app data for " + packageName
+ + ", but trying to recover: " + e);
+ destroyAppDataLeafLIF(pkg, userId, flags);
+ try {
+ ceDataInode = mInstaller.createAppData(args).ceDataInode;
+ logCriticalInfo(Log.DEBUG, "Recovery succeeded!");
+ } catch (Installer.InstallerException e2) {
+ logCriticalInfo(Log.DEBUG, "Recovery failed!");
+ }
+ }
- // Prepare the application profiles only for upgrades and
- // first boot (so that we don't repeat the same operation at
- // each boot).
- //
- // We only have to cover the upgrade and first boot here
- // because for app installs we prepare the profiles before
- // invoking dexopt (in installPackageLI).
- //
- // We also have to cover non system users because we do not
- // call the usual install package methods for them.
- //
- // NOTE: in order to speed up first boot time we only create
- // the current profile and do not update the content of the
- // reference profile. A system image should already be
- // configured with the right profile keys and the profiles
- // for the speed-profile prebuilds should already be copied.
- // That's done in #performDexOptUpgrade.
- //
- // TODO(calin, mathieuc): We should use .dm files for
- // prebuilds profiles instead of manually copying them in
- // #performDexOptUpgrade. When we do that we should have a
- // more granular check here and only update the existing
- // profiles.
- if (mPm.isDeviceUpgrading() || mPm.isFirstBoot()
- || (userId != UserHandle.USER_SYSTEM)) {
- mArtManagerService.prepareAppProfiles(pkg, userId,
- /* updateReferenceProfileContent= */ false);
- }
+ // Prepare the application profiles only for upgrades and
+ // first boot (so that we don't repeat the same operation at
+ // each boot).
+ //
+ // We only have to cover the upgrade and first boot here
+ // because for app installs we prepare the profiles before
+ // invoking dexopt (in installPackageLI).
+ //
+ // We also have to cover non system users because we do not
+ // call the usual install package methods for them.
+ //
+ // NOTE: in order to speed up first boot time we only create
+ // the current profile and do not update the content of the
+ // reference profile. A system image should already be
+ // configured with the right profile keys and the profiles
+ // for the speed-profile prebuilds should already be copied.
+ // That's done in #performDexOptUpgrade.
+ //
+ // TODO(calin, mathieuc): We should use .dm files for
+ // prebuilds profiles instead of manually copying them in
+ // #performDexOptUpgrade. When we do that we should have a
+ // more granular check here and only update the existing
+ // profiles.
+ if (mPm.isDeviceUpgrading() || mPm.isFirstBoot()
+ || (userId != UserHandle.USER_SYSTEM)) {
+ mArtManagerService.prepareAppProfiles(pkg, userId,
+ /* updateReferenceProfileContent= */ false);
+ }
- if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
- // TODO: mark this structure as dirty so we persist it!
- synchronized (mPm.mLock) {
- if (ps != null) {
- ps.setCeDataInode(ceDataInode, userId);
- }
- }
- }
+ if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
+ // TODO: mark this structure as dirty so we persist it!
+ synchronized (mPm.mLock) {
+ ps.setCeDataInode(ceDataInode, userId);
+ }
+ }
- prepareAppDataContentsLeafLIF(pkg, ps, userId, flags);
- });
+ prepareAppDataContentsLeafLIF(pkg, ps, userId, flags);
+ });
}
public void prepareAppDataContentsLIF(AndroidPackage pkg, @Nullable PackageSetting pkgSetting,
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index d86a4dc..6f54625 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -61,6 +61,7 @@
import com.android.server.om.OverlayReferenceMapper;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.utils.Snappable;
import com.android.server.utils.SnapshotCache;
import com.android.server.utils.Snapshots;
@@ -166,7 +167,7 @@
/**
* This structure maps uid -> uid and indicates whether access from the first should be
* filtered to the second. It's essentially a cache of the
- * {@link #shouldFilterApplicationInternal(int, SettingBase, PackageSetting, int)} call.
+ * {@link #shouldFilterApplicationInternal(int, Object, PackageStateInternal, int)} call.
* NOTE: It can only be relied upon after the system is ready to avoid unnecessary update on
* initial scam and is null until {@link #onSystemReady()} is called.
*/
@@ -311,7 +312,8 @@
void runWithState(CurrentStateCallback callback);
interface CurrentStateCallback {
- void currentState(ArrayMap<String, PackageSetting> settings, UserInfo[] users);
+ void currentState(ArrayMap<String, ? extends PackageStateInternal> settings,
+ UserInfo[] users);
}
}
@@ -343,7 +345,7 @@
*
* @param removed true if adding, false if removing
*/
- void updatePackageState(PackageSetting setting, boolean removed);
+ void updatePackageState(PackageStateInternal setting, boolean removed);
}
private static class FeatureConfigImpl implements FeatureConfig, CompatChange.ChangeListener {
@@ -467,7 +469,7 @@
}
@Override
- public void updatePackageState(PackageSetting setting, boolean removed) {
+ public void updatePackageState(PackageStateInternal setting, boolean removed) {
final boolean enableLogging = setting.getPkg() != null &&
!removed && (setting.getPkg().isTestOnly() || setting.getPkg().isDebuggable());
enableLogging(setting.getAppId(), enableLogging);
@@ -538,7 +540,7 @@
&& querying.getQueriesPackages().contains(potentialTarget.getPackageName());
}
- private static boolean canQueryAsInstaller(PackageSetting querying,
+ private static boolean canQueryAsInstaller(PackageStateInternal querying,
AndroidPackage potentialTarget) {
final InstallSource installSource = querying.getInstallSource();
if (potentialTarget.getPackageName().equals(installSource.installerPackageName)) {
@@ -689,7 +691,7 @@
* @param newPkgSetting the new setting being added
* @param isReplace if the package is being replaced and may need extra cleanup.
*/
- public void addPackage(PackageSetting newPkgSetting, boolean isReplace) {
+ public void addPackage(PackageStateInternal newPkgSetting, boolean isReplace) {
if (DEBUG_TRACING) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "filter.addPackage");
}
@@ -708,7 +710,8 @@
if (additionalChangedPackages != null) {
for (int index = 0; index < additionalChangedPackages.size(); index++) {
String changedPackage = additionalChangedPackages.valueAt(index);
- PackageSetting changedPkgSetting = settings.get(changedPackage);
+ PackageStateInternal changedPkgSetting =
+ settings.get(changedPackage);
if (changedPkgSetting == null) {
// It's possible for the overlay mapper to know that an actor
// package changed via an explicit reference, even if the actor
@@ -737,14 +740,14 @@
* to be updated in the cache. Returns null if there are no additional packages.
*/
@Nullable
- private ArraySet<String> addPackageInternal(PackageSetting newPkgSetting,
- ArrayMap<String, PackageSetting> existingSettings) {
+ private ArraySet<String> addPackageInternal(PackageStateInternal newPkgSetting,
+ ArrayMap<String, ? extends PackageStateInternal> existingSettings) {
if (Objects.equals("android", newPkgSetting.getPackageName())) {
// let's set aside the framework signatures
mSystemSigningDetails = newPkgSetting.getSigningDetails();
// and since we add overlays before we add the framework, let's revisit already added
// packages for signature matches
- for (PackageSetting setting : existingSettings.values()) {
+ for (PackageStateInternal setting : existingSettings.values()) {
if (isSystemSigned(mSystemSigningDetails, setting)) {
mForceQueryable.add(setting.getAppId());
}
@@ -775,7 +778,7 @@
}
for (int i = existingSettings.size() - 1; i >= 0; i--) {
- final PackageSetting existingSetting = existingSettings.valueAt(i);
+ final PackageStateInternal existingSetting = existingSettings.valueAt(i);
if (existingSetting.getAppId() == newPkgSetting.getAppId() || existingSetting.getPkg()
== null) {
continue;
@@ -823,7 +826,7 @@
int existingSize = existingSettings.size();
ArrayMap<String, AndroidPackage> existingPkgs = new ArrayMap<>(existingSize);
for (int index = 0; index < existingSize; index++) {
- PackageSetting pkgSetting = existingSettings.valueAt(index);
+ PackageStateInternal pkgSetting = existingSettings.valueAt(index);
if (pkgSetting.getPkg() != null) {
existingPkgs.put(pkgSetting.getPackageName(), pkgSetting.getPkg());
}
@@ -881,7 +884,8 @@
}
private WatchedSparseBooleanMatrix updateEntireShouldFilterCacheInner(
- ArrayMap<String, PackageSetting> settings, UserInfo[] users, int subjectUserId) {
+ ArrayMap<String, ? extends PackageStateInternal> settings, UserInfo[] users,
+ int subjectUserId) {
final WatchedSparseBooleanMatrix cache;
if (subjectUserId == USER_ALL) {
cache = new WatchedSparseBooleanMatrix(users.length * settings.size());
@@ -900,7 +904,7 @@
private void updateEntireShouldFilterCacheAsync() {
mBackgroundExecutor.execute(() -> {
- final ArrayMap<String, PackageSetting> settingsCopy = new ArrayMap<>();
+ final ArrayMap<String, PackageStateInternal> settingsCopy = new ArrayMap<>();
final ArrayMap<String, AndroidPackage> packagesCache = new ArrayMap<>();
final UserInfo[][] usersRef = new UserInfo[1][];
mStateProvider.runWithState((settings, users) -> {
@@ -977,10 +981,11 @@
}
private void updateShouldFilterCacheForPackage(WatchedSparseBooleanMatrix cache,
- @Nullable String skipPackageName, PackageSetting subjectSetting, ArrayMap<String,
- PackageSetting> allSettings, UserInfo[] allUsers, int subjectUserId, int maxIndex) {
+ @Nullable String skipPackageName, PackageStateInternal subjectSetting, ArrayMap<String,
+ ? extends PackageStateInternal> allSettings, UserInfo[] allUsers, int subjectUserId,
+ int maxIndex) {
for (int i = Math.min(maxIndex, allSettings.size() - 1); i >= 0; i--) {
- PackageSetting otherSetting = allSettings.valueAt(i);
+ PackageStateInternal otherSetting = allSettings.valueAt(i);
if (subjectSetting.getAppId() == otherSetting.getAppId()) {
continue;
}
@@ -1002,8 +1007,8 @@
}
private void updateShouldFilterCacheForUser(WatchedSparseBooleanMatrix cache,
- PackageSetting subjectSetting, UserInfo[] allUsers, PackageSetting otherSetting,
- int subjectUserId) {
+ PackageStateInternal subjectSetting, UserInfo[] allUsers,
+ PackageStateInternal otherSetting, int subjectUserId) {
for (int ou = 0; ou < allUsers.length; ou++) {
int otherUser = allUsers[ou].id;
int subjectUid = UserHandle.getUid(subjectUserId, subjectSetting.getAppId());
@@ -1041,16 +1046,17 @@
}
private static boolean isSystemSigned(@NonNull SigningDetails sysSigningDetails,
- PackageSetting pkgSetting) {
+ PackageStateInternal pkgSetting) {
return pkgSetting.isSystem()
&& pkgSetting.getSigningDetails().signaturesMatchExactly(sysSigningDetails);
}
private ArraySet<String> collectProtectedBroadcasts(
- ArrayMap<String, PackageSetting> existingSettings, @Nullable String excludePackage) {
+ ArrayMap<String, ? extends PackageStateInternal> existingSettings,
+ @Nullable String excludePackage) {
ArraySet<String> ret = new ArraySet<>();
for (int i = existingSettings.size() - 1; i >= 0; i--) {
- PackageSetting setting = existingSettings.valueAt(i);
+ PackageStateInternal setting = existingSettings.valueAt(i);
if (setting.getPkg() == null || setting.getPkg().getPackageName().equals(
excludePackage)) {
continue;
@@ -1065,13 +1071,13 @@
/**
* This method recomputes all component / intent-based visibility and is intended to match the
- * relevant logic of {@link #addPackageInternal(PackageSetting, ArrayMap)}
+ * relevant logic of {@link #addPackageInternal(PackageStateInternal, ArrayMap)}
*/
private void recomputeComponentVisibility(
- ArrayMap<String, PackageSetting> existingSettings) {
+ ArrayMap<String, ? extends PackageStateInternal> existingSettings) {
mQueriesViaComponent.clear();
for (int i = existingSettings.size() - 1; i >= 0; i--) {
- PackageSetting setting = existingSettings.valueAt(i);
+ PackageStateInternal setting = existingSettings.valueAt(i);
if (setting.getPkg() == null || requestsQueryAllPackages(setting.getPkg())) {
continue;
}
@@ -1079,7 +1085,7 @@
if (i == j) {
continue;
}
- final PackageSetting otherSetting = existingSettings.valueAt(j);
+ final PackageStateInternal otherSetting = existingSettings.valueAt(j);
if (otherSetting.getPkg() == null || mForceQueryable.contains(
otherSetting.getAppId())) {
continue;
@@ -1108,8 +1114,8 @@
* applied.
*/
@Nullable
- public SparseArray<int[]> getVisibilityAllowList(PackageSetting setting, int[] users,
- ArrayMap<String, PackageSetting> existingSettings) {
+ public SparseArray<int[]> getVisibilityAllowList(PackageStateInternal setting, int[] users,
+ ArrayMap<String, ? extends PackageStateInternal> existingSettings) {
if (mForceQueryable.contains(setting.getAppId())) {
return null;
}
@@ -1121,7 +1127,7 @@
int[] buffer = null;
int allowListSize = 0;
for (int i = existingSettings.size() - 1; i >= 0; i--) {
- final PackageSetting existingSetting = existingSettings.valueAt(i);
+ final PackageStateInternal existingSetting = existingSettings.valueAt(i);
final int existingAppId = existingSetting.getAppId();
if (existingAppId < Process.FIRST_APPLICATION_UID) {
continue;
@@ -1152,17 +1158,17 @@
*/
@VisibleForTesting(visibility = PRIVATE)
@Nullable
- SparseArray<int[]> getVisibilityAllowList(PackageSetting setting, int[] users,
- WatchedArrayMap<String, PackageSetting> existingSettings) {
+ SparseArray<int[]> getVisibilityAllowList(PackageStateInternal setting, int[] users,
+ WatchedArrayMap<String, ? extends PackageStateInternal> existingSettings) {
return getVisibilityAllowList(setting, users, existingSettings.untrackedStorage());
}
/**
- * Equivalent to calling {@link #addPackage(PackageSetting, boolean)} with {@code isReplace}
- * equal to {@code false}.
- * @see AppsFilter#addPackage(PackageSetting, boolean)
+ * Equivalent to calling {@link #addPackage(PackageStateInternal, boolean)} with
+ * {@code isReplace} equal to {@code false}.
+ * @see AppsFilter#addPackage(PackageStateInternal, boolean)
*/
- public void addPackage(PackageSetting newPkgSetting) {
+ public void addPackage(PackageStateInternal newPkgSetting) {
addPackage(newPkgSetting, false /* isReplace */);
}
@@ -1172,7 +1178,7 @@
* @param setting the setting of the package being removed.
* @param isReplace if the package is being replaced.
*/
- public void removePackage(PackageSetting setting, boolean isReplace) {
+ public void removePackage(PackageStateInternal setting, boolean isReplace) {
mStateProvider.runWithState((settings, users) -> {
final int userCount = users.length;
for (int u = 0; u < userCount; u++) {
@@ -1244,7 +1250,8 @@
removeAppIdFromVisibilityCache(setting.getAppId());
if (mShouldFilterCache != null && setting.getSharedUser() != null) {
for (int i = setting.getSharedUser().packages.size() - 1; i >= 0; i--) {
- PackageSetting siblingSetting = setting.getSharedUser().packages.valueAt(i);
+ PackageStateInternal siblingSetting =
+ setting.getSharedUser().packages.valueAt(i);
if (siblingSetting == setting) {
continue;
}
@@ -1258,7 +1265,7 @@
if (additionalChangedPackages != null) {
for (int index = 0; index < additionalChangedPackages.size(); index++) {
String changedPackage = additionalChangedPackages.valueAt(index);
- PackageSetting changedPkgSetting = settings.get(changedPackage);
+ PackageStateInternal changedPkgSetting = settings.get(changedPackage);
if (changedPkgSetting == null) {
// It's possible for the overlay mapper to know that an actor
// package changed via an explicit reference, even if the actor
@@ -1287,8 +1294,8 @@
* @param targetPkgSetting the package being accessed
* @param userId the user in which this access is being attempted
*/
- public boolean shouldFilterApplication(int callingUid, @Nullable SettingBase callingSetting,
- PackageSetting targetPkgSetting, int userId) {
+ public boolean shouldFilterApplication(int callingUid, @Nullable Object callingSetting,
+ PackageStateInternal targetPkgSetting, int userId) {
if (DEBUG_TRACING) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplication");
}
@@ -1333,8 +1340,8 @@
}
}
- private boolean shouldFilterApplicationInternal(int callingUid, SettingBase callingSetting,
- PackageSetting targetPkgSetting, int targetUserId) {
+ private boolean shouldFilterApplicationInternal(int callingUid, Object callingSetting,
+ PackageStateInternal targetPkgSetting, int targetUserId) {
if (DEBUG_TRACING) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplicationInternal");
}
@@ -1350,19 +1357,19 @@
Slog.wtf(TAG, "No setting found for non system uid " + callingUid);
return true;
}
- final PackageSetting callingPkgSetting;
- final ArraySet<PackageSetting> callingSharedPkgSettings;
+ final PackageStateInternal callingPkgSetting;
+ final ArraySet<? extends PackageStateInternal> callingSharedPkgSettings;
if (DEBUG_TRACING) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "callingSetting instanceof");
}
- if (callingSetting instanceof PackageSetting) {
- if (((PackageSetting) callingSetting).getSharedUser() == null) {
- callingPkgSetting = (PackageSetting) callingSetting;
+ if (callingSetting instanceof PackageStateInternal) {
+ if (((PackageStateInternal) callingSetting).getSharedUser() == null) {
+ callingPkgSetting = (PackageStateInternal) callingSetting;
callingSharedPkgSettings = null;
} else {
callingPkgSetting = null;
callingSharedPkgSettings =
- ((PackageSetting) callingSetting).getSharedUser().packages;
+ ((PackageStateInternal) callingSetting).getSharedUser().packages;
}
} else {
callingPkgSetting = null;
@@ -1544,7 +1551,7 @@
if (callingSharedPkgSettings != null) {
int size = callingSharedPkgSettings.size();
for (int index = 0; index < size; index++) {
- PackageSetting pkgSetting = callingSharedPkgSettings.valueAt(index);
+ PackageStateInternal pkgSetting = callingSharedPkgSettings.valueAt(index);
if (mOverlayReferenceMapper.isValidActor(targetName,
pkgSetting.getPackageName())) {
if (DEBUG_LOGGING) {
@@ -1642,7 +1649,7 @@
}
}
- private static void log(SettingBase callingSetting, PackageSetting targetPkgSetting,
+ private static void log(Object callingSetting, PackageStateInternal targetPkgSetting,
String description) {
Slog.i(TAG,
"interaction: " + (callingSetting == null ? "system" : callingSetting) + " -> "
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 9251f65..dca8654 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -44,7 +44,6 @@
import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedProviderImpl;
import android.content.pm.parsing.component.ParsedService;
-import android.content.pm.pkg.PackageUserState;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -62,6 +61,8 @@
import com.android.server.pm.parsing.PackageInfoUtils.CachedApplicationInfoGenerator;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageUserState;
import com.android.server.utils.Snappable;
import com.android.server.utils.SnapshotCache;
import com.android.server.utils.WatchableImpl;
@@ -363,9 +364,8 @@
continue;
}
- final PackageSetting ps =
- (PackageSetting) sPackageManagerInternal.getPackageSetting(
- p.getPackageName());
+ final PackageStateInternal ps =
+ sPackageManagerInternal.getPackageStateInternal(p.getPackageName());
if (ps == null) {
continue;
}
@@ -387,7 +387,7 @@
if (appInfoGenerator == null) {
appInfoGenerator = new CachedApplicationInfoGenerator();
}
- final PackageUserState state = ps.readUserState(userId);
+ final PackageUserState state = ps.getUserStateOrDefault(userId);
final ApplicationInfo appInfo =
appInfoGenerator.generate(pkg, flags, state, userId, ps);
if (appInfo == null) {
@@ -415,8 +415,8 @@
if (p == null) {
return null;
}
- final PackageSetting ps = (PackageSetting) sPackageManagerInternal.getPackageSetting(
- p.getPackageName());
+ final PackageStateInternal ps =
+ sPackageManagerInternal.getPackageStateInternal(p.getPackageName());
if (ps == null) {
return null;
}
@@ -424,7 +424,7 @@
if (pkg == null) {
return null;
}
- final PackageUserState state = ps.readUserState(userId);
+ final PackageUserState state = ps.getUserStateOrDefault(userId);
ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
pkg, flags, state, userId, ps);
if (appInfo == null) {
@@ -444,9 +444,8 @@
continue;
}
- final PackageSetting ps =
- (PackageSetting) sPackageManagerInternal.getPackageSetting(
- p.getPackageName());
+ final PackageStateInternal ps =
+ sPackageManagerInternal.getPackageStateInternal(p.getPackageName());
if (ps == null) {
continue;
}
@@ -462,7 +461,7 @@
if (appInfoGenerator == null) {
appInfoGenerator = new CachedApplicationInfoGenerator();
}
- final PackageUserState state = ps.readUserState(userId);
+ final PackageUserState state = ps.getUserStateOrDefault(userId);
final ApplicationInfo appInfo =
appInfoGenerator.generate(pkg, 0, state, userId, ps);
if (appInfo == null) {
@@ -1530,15 +1529,15 @@
}
return null;
}
- PackageSetting ps = (PackageSetting) sPackageManagerInternal.getPackageSetting(
- activity.getPackageName());
+ PackageStateInternal ps =
+ sPackageManagerInternal.getPackageStateInternal(activity.getPackageName());
if (ps == null) {
if (DEBUG) {
log("info.activity.owner.mExtras == null", info, match, userId);
}
return null;
}
- final PackageUserState userState = ps.readUserState(userId);
+ final PackageUserState userState = ps.getUserStateOrDefault(userId);
ActivityInfo ai = PackageInfoUtils.generateActivityInfo(pkg, activity, mFlags,
userState, userId, ps);
if (ai == null) {
@@ -1850,12 +1849,12 @@
return null;
}
- PackageSetting ps = (PackageSetting) sPackageManagerInternal.getPackageSetting(
- provider.getPackageName());
+ PackageStateInternal ps =
+ sPackageManagerInternal.getPackageStateInternal(provider.getPackageName());
if (ps == null) {
return null;
}
- final PackageUserState userState = ps.readUserState(userId);
+ final PackageUserState userState = ps.getUserStateOrDefault(userId);
final boolean matchVisibleToInstantApp = (mFlags
& PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
final boolean isInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0;
@@ -2095,12 +2094,12 @@
return null;
}
- PackageSetting ps = (PackageSetting) sPackageManagerInternal.getPackageSetting(
- service.getPackageName());
+ PackageStateInternal ps =
+ sPackageManagerInternal.getPackageStateInternal(service.getPackageName());
if (ps == null) {
return null;
}
- final PackageUserState userState = ps.readUserState(userId);
+ final PackageUserState userState = ps.getUserStateOrDefault(userId);
ServiceInfo si = PackageInfoUtils.generateServiceInfo(pkg, service, mFlags,
userState, userId, ps);
if (si == null) {
@@ -2294,8 +2293,8 @@
return false;
}
- PackageSetting ps = (PackageSetting) sPackageManagerInternal.getPackageSetting(
- pair.first.getPackageName());
+ PackageStateInternal ps =
+ sPackageManagerInternal.getPackageStateInternal(pair.first.getPackageName());
if (ps == null) {
return false;
}
@@ -2303,7 +2302,7 @@
// System apps are never considered stopped for purposes of
// filtering, because there may be no way for the user to
// actually re-launch them.
- return !ps.isSystem() && ps.getStopped(userId);
+ return !ps.isSystem() && ps.getUserStateOrDefault(userId).isStopped();
}
/**
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index 5b7013b..5e9ec88 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -37,6 +37,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
+import com.android.server.pm.pkg.PackageStateInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -164,18 +165,18 @@
String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
boolean resolveForStart, int userId, Intent intent);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
- PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId);
+ PackageInfo generatePackageInfo(PackageStateInternal ps, int flags, int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
PackageInfo getPackageInfo(String packageName, int flags, int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
PackageInfo getPackageInfoInternal(String packageName, long versionCode, int flags,
int filterCallingUid, int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
- PackageSetting getPackageSetting(String packageName);
+ PackageStateInternal getPackageStateInternal(String packageName);
@Computer.LiveImplementation(override = Computer.LiveImplementation.MANDATORY)
- PackageSetting getPackageSettingInternal(String packageName, int callingUid);
+ PackageStateInternal getPackageStateInternal(String packageName, int callingUid);
@Computer.LiveImplementation(override = Computer.LiveImplementation.MANDATORY)
- @Nullable PackageState getPackageState(@NonNull String packageName);
+ @Nullable PackageState getPackageStateCopied(@NonNull String packageName);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
@@ -198,7 +199,7 @@
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
boolean canViewInstantApps(int callingUid, int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
- boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId,
+ boolean filterSharedLibPackageLPr(@Nullable PackageStateInternal ps, int uid, int userId,
int flags);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
boolean isCallerSameApp(String packageName, int uid);
@@ -217,11 +218,11 @@
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
- boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid,
+ boolean shouldFilterApplicationLocked(@Nullable PackageStateInternal ps, int callingUid,
@Nullable ComponentName component, @PackageManager.ComponentType int componentType,
int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
- boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid,
+ boolean shouldFilterApplicationLocked(@Nullable PackageStateInternal ps, int callingUid,
int userId);
@Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
boolean shouldFilterApplicationLocked(@NonNull SharedUserSetting sus, int callingUid,
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 569d651..93c92c0 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -78,9 +78,9 @@
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.content.pm.parsing.component.ParsedMainComponent;
import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedService;
-import android.content.pm.pkg.PackageUserState;
import android.content.pm.pkg.PackageUserStateUtils;
import android.os.Binder;
import android.os.PatternMatcher;
@@ -110,6 +110,9 @@
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateImpl;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageStateUtils;
+import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.verify.domain.DomainVerificationUtils;
import com.android.server.utils.WatchedArrayMap;
@@ -128,6 +131,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.UUID;
@@ -139,6 +143,93 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
public class ComputerEngine implements Computer {
+ // TODO: Move this to its own interface implemented by the pm.Settings implementation
+ protected class Settings {
+
+ @NonNull
+ private com.android.server.pm.Settings mSettings;
+
+ public Map<String, ? extends PackageStateInternal> getPackages() {
+ return mSettings.getPackagesLocked();
+ }
+
+ public Settings(@NonNull com.android.server.pm.Settings settings) {
+ mSettings = settings;
+ }
+
+ @Nullable
+ public PackageStateInternal getPackage(@NonNull String packageName) {
+ return mSettings.getPackageLPr(packageName);
+ }
+
+ @Nullable
+ public PackageStateInternal getDisabledSystemPkg(@NonNull String packageName) {
+ return mSettings.getDisabledSystemPkgLPr(packageName);
+ }
+
+ public boolean isEnabledAndMatch(ComponentInfo componentInfo, int flags, int userId) {
+ PackageStateInternal pkgState = getPackage(componentInfo.packageName);
+ if (pkgState == null) {
+ return false;
+ }
+
+ return PackageUserStateUtils.isMatch(pkgState.getUserStateOrDefault(userId),
+ componentInfo, flags);
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public boolean isEnabledAndMatch(AndroidPackage pkg, ParsedMainComponent component,
+ int flags, int userId) {
+ PackageStateInternal pkgState = getPackage(component.getPackageName());
+ if (pkgState == null) {
+ return false;
+ }
+
+ return PackageUserStateUtils.isMatch(pkgState.getUserStateOrDefault(userId),
+ pkg.isSystem(), pkg.isEnabled(), component, flags);
+ }
+
+ @Nullable
+ public CrossProfileIntentResolver getCrossProfileIntentResolver(@UserIdInt int userId) {
+ return mSettings.getCrossProfileIntentResolver(userId);
+ }
+
+ // TODO: Find replacement
+ @Nullable
+ public SettingBase getSettingBase(int appId) {
+ return mSettings.getSettingLPr(appId);
+ }
+
+ @Nullable
+ public String getRenamedPackageLPr(String packageName) {
+ return mSettings.getRenamedPackageLPr(packageName);
+ }
+
+ @Nullable
+ public PersistentPreferredIntentResolver getPersistentPreferredActivities(
+ @UserIdInt int userId) {
+ return mSettings.getPersistentPreferredActivities(userId);
+ }
+
+ public void dumpVersionLPr(@NonNull IndentingPrintWriter indentingPrintWriter) {
+ mSettings.dumpVersionLPr(indentingPrintWriter);
+ }
+
+ public void dumpPreferred(PrintWriter pw, DumpState dumpState, String packageName) {
+ mSettings.dumpPreferred(pw, dumpState, packageName);
+ }
+
+ // TODO: Move to separate utility
+ public void writePreferredActivitiesLPr(@NonNull TypedXmlSerializer serializer, int userId,
+ boolean full) throws IllegalArgumentException, IllegalStateException, IOException {
+ mSettings.writePreferredActivitiesLPr(serializer, userId, full);
+ }
+
+ public PreferredIntentResolver getPreferredActivities(@UserIdInt int userId) {
+ return mSettings.getPreferredActivities(userId);
+ }
+ }
+
// The administrative use counter.
private int mUsed = 0;
@@ -198,7 +289,7 @@
}
ComputerEngine(PackageManagerService.Snapshot args) {
- mSettings = args.settings;
+ mSettings = new Settings(args.settings);
mIsolatedOwners = args.isolatedOwners;
mPackages = args.packages;
mSharedLibraries = args.sharedLibs;
@@ -315,7 +406,7 @@
final boolean blockNormalResolution =
!resolveForStart && !isTargetInstantApp && !isCallerInstantApp
&& shouldFilterApplicationLocked(
- getPackageSettingInternal(ai.applicationInfo.packageName,
+ getPackageStateInternal(ai.applicationInfo.packageName,
Process.SYSTEM_UID), filterCallingUid, userId);
if (!blockInstantResolution && !blockNormalResolution) {
final ResolveInfo ri = new ResolveInfo();
@@ -419,7 +510,7 @@
final boolean blockNormalResolution = !isTargetInstantApp && !isCallerInstantApp
&& shouldFilterApplicationLocked(
- getPackageSettingInternal(si.applicationInfo.packageName,
+ getPackageStateInternal(si.applicationInfo.packageName,
Process.SYSTEM_UID), callingUid, userId);
if (!blockInstantResolution && !blockNormalResolution) {
final ResolveInfo ri = new ResolveInfo();
@@ -556,14 +647,14 @@
}
}
} else {
- final PackageSetting setting =
- getPackageSettingInternal(pkgName, Process.SYSTEM_UID);
+ final PackageStateInternal setting =
+ getPackageStateInternal(pkgName, Process.SYSTEM_UID);
result = null;
- if (setting != null && setting.getPkg() != null && (resolveForStart
+ if (setting != null && setting.getAndroidPackage() != null && (resolveForStart
|| !shouldFilterApplicationLocked(setting, filterCallingUid, userId))) {
result = filterIfNotSystemUser(mComponentResolver.queryActivities(
- intent, resolvedType, flags, setting.getPkg().getActivities(), userId),
- userId);
+ intent, resolvedType, flags, setting.getAndroidPackage().getActivities(),
+ userId), userId);
}
if (result == null || result.size() == 0) {
// the caller wants to resolve for a particular package; however, there
@@ -638,15 +729,15 @@
if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
AndroidPackage pkg = a == null ? null : mPackages.get(a.getPackageName());
- if (pkg != null && mSettings.isEnabledAndMatchLPr(pkg, a, flags, userId)) {
- PackageSetting ps = mSettings.getPackageLPr(component.getPackageName());
+ if (pkg != null && mSettings.isEnabledAndMatch(pkg, a, flags, userId)) {
+ PackageStateInternal ps = mSettings.getPackage(component.getPackageName());
if (ps == null) return null;
if (shouldFilterApplicationLocked(
ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) {
return null;
}
return PackageInfoUtils.generateActivityInfo(pkg,
- a, flags, ps.readUserState(userId), userId, ps);
+ a, flags, ps.getUserStateOrDefault(userId), userId, ps);
}
if (resolveComponentName().equals(component)) {
return PackageInfoWithoutStateUtils.generateDelegateActivityInfo(mResolveActivity,
@@ -674,7 +765,7 @@
public final ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName,
int flags, int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
- PackageSetting ps = mSettings.getPackageLPr(packageName);
+ PackageStateInternal ps = mSettings.getPackage(packageName);
if (ps != null) {
if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
return null;
@@ -682,15 +773,15 @@
if (shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
return null;
}
- if (ps.getPkg() == null) {
+ if (ps.getAndroidPackage() == null) {
final PackageInfo pInfo = generatePackageInfo(ps, flags, userId);
if (pInfo != null) {
return pInfo.applicationInfo;
}
return null;
}
- ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(ps.getPkg(), flags,
- ps.readUserState(userId), userId, ps);
+ ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(ps.getPkg(),
+ flags, ps.getUserStateOrDefault(userId), userId, ps);
if (ai != null) {
ai.packageName = resolveExternalPackageNameLPr(ps.getPkg());
}
@@ -737,7 +828,7 @@
+ ": " + p);
}
if (p != null) {
- PackageSetting ps = mSettings.getPackageLPr(packageName);
+ PackageStateInternal ps = mSettings.getPackage(packageName);
if (ps == null) return null;
if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
return null;
@@ -747,7 +838,7 @@
}
// Note: isEnabledLP() does not apply here - always return info
ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(
- p, flags, ps.readUserState(userId), userId, ps);
+ p, flags, ps.getUserStateOrDefault(userId), userId, ps);
if (ai != null) {
ai.packageName = resolveExternalPackageNameLPr(p);
}
@@ -825,7 +916,7 @@
} else {
Pair<List<ResolveInfo>, Integer> infosAndLevel = mDomainVerificationManager
.filterToApprovedApp(intent, undefinedList, userId,
- mSettings::getPackageLPr);
+ mSettings::getPackage);
List<ResolveInfo> approvedInfos = infosAndLevel.first;
Integer highestApproval = infosAndLevel.second;
@@ -1001,7 +1092,7 @@
continue;
}
String packageName = riTargetUser.activityInfo.packageName;
- PackageSetting ps = mSettings.getPackageLPr(packageName);
+ PackageStateInternal ps = mSettings.getPackage(packageName);
if (ps == null) {
continue;
}
@@ -1111,9 +1202,9 @@
if (ephemeralPkgName == null) {
// caller is a full app
SettingBase callingSetting =
- mSettings.getSettingLPr(UserHandle.getAppId(filterCallingUid));
- PackageSetting resolvedSetting =
- getPackageSettingInternal(info.activityInfo.packageName, 0);
+ mSettings.getSettingBase(UserHandle.getAppId(filterCallingUid));
+ PackageStateInternal resolvedSetting =
+ getPackageStateInternal(info.activityInfo.packageName, 0);
if (resolveForStart
|| !mAppsFilter.shouldFilterApplication(
filterCallingUid, callingSetting, resolvedSetting, userId)) {
@@ -1146,9 +1237,9 @@
final ResolveInfo info = resolveInfos.get(i);
if (instantAppPkgName == null) {
SettingBase callingSetting =
- mSettings.getSettingLPr(UserHandle.getAppId(filterCallingUid));
- PackageSetting resolvedSetting =
- getPackageSettingInternal(info.serviceInfo.packageName, 0);
+ mSettings.getSettingBase(UserHandle.getAppId(filterCallingUid));
+ PackageStateInternal resolvedSetting =
+ getPackageStateInternal(info.serviceInfo.packageName, 0);
if (!mAppsFilter.shouldFilterApplication(
filterCallingUid, callingSetting, resolvedSetting, userId)) {
continue;
@@ -1262,8 +1353,8 @@
for (int i = instantApps.size() - 1; i >= 0; --i) {
final ResolveInfo info = instantApps.get(i);
final String packageName = info.activityInfo.packageName;
- final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps.getInstantApp(userId)) {
+ final PackageStateInternal ps = mSettings.getPackage(packageName);
+ if (ps.getUserStateOrDefault(userId).isInstantApp()) {
if (PackageManagerServiceUtils.hasAnyDomainApproval(
mDomainVerificationManager, ps, intent, flags, userId)) {
if (DEBUG_INSTANT) {
@@ -1315,17 +1406,17 @@
if (intent.isWebIntent() && auxiliaryResponse == null) {
return result;
}
- final PackageSetting ps =
- mSettings.getPackageLPr(instantAppInstallerActivity().packageName);
- if (ps == null || !PackageUserStateUtils.isEnabled(ps.readUserState(userId),
+ final PackageStateInternal ps =
+ mSettings.getPackage(instantAppInstallerActivity().packageName);
+ if (ps == null || !PackageUserStateUtils.isEnabled(ps.getUserStateOrDefault(userId),
instantAppInstallerActivity(), 0)) {
return result;
}
final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo);
ephemeralInstaller.activityInfo =
PackageInfoWithoutStateUtils.generateDelegateActivityInfo(
- instantAppInstallerActivity(), 0 /*flags*/, ps.readUserState(userId),
- userId);
+ instantAppInstallerActivity(), 0 /*flags*/,
+ ps.getUserStateOrDefault(userId), userId);
ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
| IntentFilter.MATCH_ADJUSTMENT_NORMAL;
// add a non-generic filter
@@ -1349,7 +1440,7 @@
return result;
}
- public final PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) {
+ public final PackageInfo generatePackageInfo(PackageStateInternal ps, int flags, int userId) {
if (!mUserManager.exists(userId)) return null;
if (ps == null) {
return null;
@@ -1370,7 +1461,7 @@
flags |= MATCH_ANY_USER;
}
- final PackageUserState state = ps.readUserState(userId);
+ final PackageUserState state = ps.getUserStateOrDefault(userId);
AndroidPackage p = ps.getPkg();
if (p != null) {
// Compute GIDs only if requested
@@ -1408,8 +1499,8 @@
ai.primaryCpuAbi = ps.getPrimaryCpuAbi();
ai.secondaryCpuAbi = ps.getSecondaryCpuAbi();
ai.setVersionCode(ps.getVersionCode());
- ai.flags = ps.pkgFlags;
- ai.privateFlags = ps.pkgPrivateFlags;
+ ai.flags = ps.getPkgFlags();
+ ai.privateFlags = ps.getPkgPrivateFlags();
pi.applicationInfo = PackageInfoWithoutStateUtils.generateDelegateApplicationInfo(
ai, flags, state, userId);
@@ -1458,7 +1549,7 @@
return mApexManager.getPackageInfo(packageName,
ApexManager.MATCH_FACTORY_PACKAGE);
}
- final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
+ final PackageStateInternal ps = mSettings.getDisabledSystemPkg(packageName);
if (ps != null) {
if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
return null;
@@ -1478,7 +1569,7 @@
Log.v(TAG, "getPackageInfo " + packageName + ": " + p);
}
if (p != null) {
- final PackageSetting ps = getPackageSetting(p.getPackageName());
+ final PackageStateInternal ps = getPackageStateInternal(p.getPackageName());
if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
return null;
}
@@ -1489,7 +1580,7 @@
return generatePackageInfo(ps, flags, userId);
}
if (!matchFactoryOnly && (flags & MATCH_KNOWN_PACKAGES) != 0) {
- final PackageSetting ps = mSettings.getPackageLPr(packageName);
+ final PackageStateInternal ps = mSettings.getPackage(packageName);
if (ps == null) return null;
if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
return null;
@@ -1506,22 +1597,23 @@
}
@Nullable
- public final PackageSetting getPackageSetting(String packageName) {
- return getPackageSettingInternal(packageName, Binder.getCallingUid());
+ public final PackageStateInternal getPackageStateInternal(String packageName) {
+ return getPackageStateInternal(packageName, Binder.getCallingUid());
}
- public PackageSetting getPackageSettingInternal(String packageName, int callingUid) {
+ public PackageStateInternal getPackageStateInternal(String packageName,
+ int callingUid) {
packageName = resolveInternalPackageNameInternalLocked(
packageName, PackageManager.VERSION_CODE_HIGHEST, callingUid);
- return mSettings.getPackageLPr(packageName);
+ return mSettings.getPackage(packageName);
}
@Nullable
- public PackageState getPackageState(@NonNull String packageName) {
+ public PackageState getPackageStateCopied(@NonNull String packageName) {
int callingUid = Binder.getCallingUid();
packageName = resolveInternalPackageNameInternalLocked(
packageName, PackageManager.VERSION_CODE_HIGHEST, callingUid);
- PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
+ PackageStateInternal pkgSetting = mSettings.getPackage(packageName);
return pkgSetting == null ? null : PackageStateImpl.copy(pkgSetting);
}
@@ -1548,13 +1640,14 @@
ArrayList<PackageInfo> list;
if (listUninstalled) {
- list = new ArrayList<>(mSettings.getPackagesLocked().size());
- for (PackageSetting ps : mSettings.getPackagesLocked().values()) {
+ list = new ArrayList<>(mSettings.getPackages().size());
+ for (PackageStateInternal ps : mSettings.getPackages().values()) {
if (listFactory) {
if (!ps.isSystem()) {
continue;
}
- PackageSetting psDisabled = mSettings.getDisabledSystemPkgLPr(ps);
+ PackageStateInternal psDisabled =
+ mSettings.getDisabledSystemPkg(ps.getPackageName());
if (psDisabled != null) {
ps = psDisabled;
}
@@ -1573,12 +1666,13 @@
} else {
list = new ArrayList<>(mPackages.size());
for (AndroidPackage p : mPackages.values()) {
- PackageSetting ps = getPackageSetting(p.getPackageName());
+ PackageStateInternal ps = getPackageStateInternal(p.getPackageName());
if (listFactory) {
if (!p.isSystem()) {
continue;
}
- PackageSetting psDisabled = mSettings.getDisabledSystemPkgLPr(ps);
+ PackageStateInternal psDisabled =
+ ps == null ? null : mSettings.getDisabledSystemPkg(ps.getPackageName());
if (psDisabled != null) {
ps = psDisabled;
}
@@ -1653,7 +1747,7 @@
continue;
}
String packageName = riTargetUser.activityInfo.packageName;
- PackageSetting ps = mSettings.getPackageLPr(packageName);
+ PackageStateInternal ps = mSettings.getPackage(packageName);
if (ps == null) {
continue;
}
@@ -1798,15 +1892,15 @@
}
AndroidPackage pkg = mPackages.get(s.getPackageName());
- if (mSettings.isEnabledAndMatchLPr(pkg, s, flags, userId)) {
- PackageSetting ps = mSettings.getPackageLPr(component.getPackageName());
+ if (mSettings.isEnabledAndMatch(pkg, s, flags, userId)) {
+ PackageStateInternal ps = mSettings.getPackage(component.getPackageName());
if (ps == null) return null;
if (shouldFilterApplicationLocked(
ps, callingUid, component, TYPE_SERVICE, userId)) {
return null;
}
return PackageInfoUtils.generateServiceInfo(pkg,
- s, flags, ps.readUserState(userId), userId, ps);
+ s, flags, ps.getUserStateOrDefault(userId), userId, ps);
}
return null;
}
@@ -1827,10 +1921,11 @@
callingUid = getIsolatedOwner(callingUid);
}
final int appId = UserHandle.getAppId(callingUid);
- final Object obj = mSettings.getSettingLPr(appId);
- if (obj instanceof PackageSetting) {
- final PackageSetting ps = (PackageSetting) obj;
- final boolean isInstantApp = ps.getInstantApp(UserHandle.getUserId(callingUid));
+ final Object obj = mSettings.getSettingBase(appId);
+ if (obj instanceof PackageStateInternal) {
+ final PackageStateInternal ps = (PackageStateInternal) obj;
+ final boolean isInstantApp = ps.getUserStateOrDefault(UserHandle.getUserId(callingUid))
+ .isInstantApp();
return isInstantApp ? ps.getPkg().getPackageName() : null;
}
return null;
@@ -1879,10 +1974,10 @@
String[] uidPackages = getPackagesForUidInternal(callingUid, callingUid);
if (uidPackages != null) {
for (String uidPackage : uidPackages) {
- PackageSetting ps = mSettings.getPackageLPr(uidPackage);
- final int libIdx = ArrayUtils.indexOf(ps.usesStaticLibraries, libName);
+ PackageStateInternal ps = mSettings.getPackage(uidPackage);
+ final int libIdx = ArrayUtils.indexOf(ps.getUsesStaticLibraries(), libName);
if (libIdx >= 0) {
- final long libVersion = ps.usesStaticLibrariesVersions[libIdx];
+ final long libVersion = ps.getUsesStaticLibrariesVersions()[libIdx];
versionsCallerCanSee.append(libVersion, libVersion);
}
}
@@ -1958,7 +2053,7 @@
protected String[] getPackagesForUidInternalBody(int callingUid, int userId, int appId,
boolean isCallerInstantApp) {
// reader
- final Object obj = mSettings.getSettingLPr(appId);
+ final Object obj = mSettings.getSettingBase(appId);
if (obj instanceof SharedUserSetting) {
if (isCallerInstantApp) {
return null;
@@ -1968,16 +2063,16 @@
String[] res = new String[n];
int i = 0;
for (int index = 0; index < n; index++) {
- final PackageSetting ps = sus.packages.valueAt(index);
- if (ps.getInstalled(userId)
+ final PackageStateInternal ps = sus.packages.valueAt(index);
+ if (ps.getUserStateOrDefault(userId).isInstalled()
&& !shouldFilterApplicationLocked(ps, callingUid, userId)) {
res[i++] = ps.getPackageName();
}
}
return ArrayUtils.trimToSize(res, i);
- } else if (obj instanceof PackageSetting) {
- final PackageSetting ps = (PackageSetting) obj;
- if (ps.getInstalled(userId)
+ } else if (obj instanceof PackageStateInternal) {
+ final PackageStateInternal ps = (PackageStateInternal) obj;
+ if (ps.getUserStateOrDefault(userId).isInstalled()
&& !shouldFilterApplicationLocked(ps, callingUid, userId)) {
return new String[]{ps.getPackageName()};
}
@@ -2037,7 +2132,7 @@
return false;
}
- public final boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid,
+ public final boolean filterSharedLibPackageLPr(@Nullable PackageStateInternal ps, int uid,
int userId, int flags) {
// Callers can access only the libs they depend on, otherwise they need to explicitly
// ask for the shared libraries given the caller is allowed to access all static libs.
@@ -2076,9 +2171,9 @@
if (ps.getPackageName().equals(uidPackageName)) {
return false;
}
- PackageSetting uidPs = mSettings.getPackageLPr(uidPackageName);
+ PackageStateInternal uidPs = mSettings.getPackage(uidPackageName);
if (uidPs != null) {
- final int index = ArrayUtils.indexOf(uidPs.usesStaticLibraries,
+ final int index = ArrayUtils.indexOf(uidPs.getUsesStaticLibraries(),
libraryInfo.getName());
if (index < 0) {
continue;
@@ -2212,7 +2307,7 @@
if (Process.isIsolated(callingUid)) {
callingUid = getIsolatedOwner(callingUid);
}
- final PackageSetting ps = mSettings.getPackageLPr(packageName);
+ final PackageStateInternal ps = mSettings.getPackage(packageName);
final boolean returnAllowed =
ps != null
&& (isCallerSameApp(packageName, callingUid)
@@ -2220,7 +2315,7 @@
|| mInstantAppRegistry.isInstantAccessGranted(
userId, UserHandle.getAppId(callingUid), ps.getAppId()));
if (returnAllowed) {
- return ps.getInstantApp(userId);
+ return ps.getUserStateOrDefault(userId).isInstantApp();
}
return false;
}
@@ -2272,7 +2367,7 @@
for (int n = 0; n < count; n++) {
final ResolveInfo info = resolvedActivities.get(n);
final String packageName = info.activityInfo.packageName;
- final PackageSetting ps = mSettings.getPackageLPr(packageName);
+ final PackageStateInternal ps = mSettings.getPackage(packageName);
if (ps != null) {
// only check domain verification status if the app is not a browser
if (!info.handleAllWebDataURI) {
@@ -2285,7 +2380,7 @@
return false;
}
}
- if (ps.getInstantApp(userId)) {
+ if (ps.getUserStateOrDefault(userId).isInstantApp()) {
if (DEBUG_INSTANT) {
Slog.v(TAG, "DENY instant app installed;"
+ " pkg: " + packageName);
@@ -2361,7 +2456,7 @@
*
* @see #canViewInstantApps(int, int)
*/
- public final boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps,
+ public final boolean shouldFilterApplicationLocked(@Nullable PackageStateInternal ps,
int callingUid, @Nullable ComponentName component,
@PackageManager.ComponentType int componentType, int userId) {
// if we're in an isolated process, get the real calling UID
@@ -2380,7 +2475,7 @@
}
if (callerIsInstantApp) {
// both caller and target are both instant, but, different applications, filter
- if (ps.getInstantApp(userId)) {
+ if (ps.getUserStateOrDefault(userId).isInstantApp()) {
return true;
}
// request for a specific component; if it hasn't been explicitly exposed through
@@ -2397,7 +2492,7 @@
// request for application; if no components have been explicitly exposed, filter
return !ps.getPkg().isVisibleToInstantApps();
}
- if (ps.getInstantApp(userId)) {
+ if (ps.getUserStateOrDefault(userId).isInstantApp()) {
// caller can see all components of all instant applications, don't filter
if (canViewInstantApps(callingUid, userId)) {
return false;
@@ -2412,20 +2507,20 @@
userId, UserHandle.getAppId(callingUid), ps.getAppId());
}
int appId = UserHandle.getAppId(callingUid);
- final SettingBase callingPs = mSettings.getSettingLPr(appId);
+ final SettingBase callingPs = mSettings.getSettingBase(appId);
return mAppsFilter.shouldFilterApplication(callingUid, callingPs, ps, userId);
}
/**
- * @see #shouldFilterApplicationLocked(PackageSetting, int, ComponentName, int, int)
+ * @see #shouldFilterApplicationLocked(PackageStateInternal, int, ComponentName, int, int)
*/
public final boolean shouldFilterApplicationLocked(
- @Nullable PackageSetting ps, int callingUid, int userId) {
+ @Nullable PackageStateInternal ps, int callingUid, int userId) {
return shouldFilterApplicationLocked(ps, callingUid, null, TYPE_UNKNOWN, userId);
}
/**
- * @see #shouldFilterApplicationLocked(PackageSetting, int, ComponentName, int, int)
+ * @see #shouldFilterApplicationLocked(PackageStateInternal, int, ComponentName, int, int)
*/
public final boolean shouldFilterApplicationLocked(@NonNull SharedUserSetting sus,
int callingUid, int userId) {
@@ -2461,15 +2556,15 @@
// reader
final AndroidPackage p = mPackages.get(packageName);
if (p != null && AndroidPackageUtils.isMatchForSystemOnly(p, flags)) {
- final PackageSetting ps = getPackageSettingInternal(p.getPackageName(), callingUid);
- if (ps != null && ps.getInstalled(userId)
+ final PackageStateInternal ps = getPackageStateInternal(p.getPackageName(), callingUid);
+ if (ps != null && ps.getUserStateOrDefault(userId).isInstalled()
&& !shouldFilterApplicationLocked(ps, callingUid, userId)) {
return UserHandle.getUid(userId, p.getUid());
}
}
if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
- final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps != null && ps.isMatch(flags)
+ final PackageStateInternal ps = mSettings.getPackage(packageName);
+ if (ps != null && PackageStateUtils.isMatch(ps, flags)
&& !shouldFilterApplicationLocked(ps, callingUid, userId)) {
return UserHandle.getUid(userId, ps.getAppId());
}
@@ -2736,12 +2831,12 @@
public SigningDetails getSigningDetails(int uid) {
final int appId = UserHandle.getAppId(uid);
- final Object obj = mSettings.getSettingLPr(appId);
+ final Object obj = mSettings.getSettingBase(appId);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
return ((SharedUserSetting) obj).signatures.mSigningDetails;
- } else if (obj instanceof PackageSetting) {
- final PackageSetting ps = (PackageSetting) obj;
+ } else if (obj instanceof PackageStateInternal) {
+ final PackageStateInternal ps = (PackageStateInternal) obj;
return ps.getSigningDetails();
}
}
@@ -2749,13 +2844,13 @@
}
public boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) {
- PackageSetting ps = getPackageSetting(pkg.getPackageName());
+ PackageStateInternal ps = getPackageStateInternal(pkg.getPackageName());
return shouldFilterApplicationLocked(ps, callingUid,
userId);
}
public boolean filterAppAccess(String packageName, int callingUid, int userId) {
- PackageSetting ps = getPackageSetting(packageName);
+ PackageStateInternal ps = getPackageStateInternal(packageName);
return shouldFilterApplicationLocked(ps, callingUid,
userId);
}
@@ -2763,22 +2858,22 @@
public boolean filterAppAccess(int uid, int callingUid) {
final int userId = UserHandle.getUserId(uid);
final int appId = UserHandle.getAppId(uid);
- final Object setting = mSettings.getSettingLPr(appId);
+ final Object setting = mSettings.getSettingBase(appId);
if (setting instanceof SharedUserSetting) {
return shouldFilterApplicationLocked(
(SharedUserSetting) setting, callingUid, userId);
} else if (setting == null
- || setting instanceof PackageSetting) {
+ || setting instanceof PackageStateInternal) {
return shouldFilterApplicationLocked(
- (PackageSetting) setting, callingUid, userId);
+ (PackageStateInternal) setting, callingUid, userId);
}
return false;
}
public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
final String packageName = dumpState.getTargetPackageName();
- final PackageSetting setting = mSettings.getPackageLPr(packageName);
+ final PackageStateInternal setting = mSettings.getPackage(packageName);
final boolean checkin = dumpState.isCheckIn();
// Return if the package doesn't exist.
@@ -2896,7 +2991,7 @@
writer.increaseIndent();
try {
mDomainVerificationManager.printState(writer, packageName,
- UserHandle.USER_ALL, mSettings::getPackageLPr);
+ UserHandle.USER_ALL, mSettings::getPackage);
} catch (PackageManager.NameNotFoundException e) {
pw.println("Failure printing domain verification information");
Slog.e(TAG, "Failure printing domain verification information", e);
@@ -2913,14 +3008,14 @@
}
ipw.println("Dexopt state:");
ipw.increaseIndent();
- Collection<PackageSetting> pkgSettings;
+ Collection<? extends PackageStateInternal> pkgSettings;
if (setting != null) {
pkgSettings = Collections.singletonList(setting);
} else {
- pkgSettings = mSettings.getPackagesLocked().values();
+ pkgSettings = mSettings.getPackages().values();
}
- for (PackageSetting pkgSetting : pkgSettings) {
+ for (PackageStateInternal pkgSetting : pkgSettings) {
final AndroidPackage pkg = pkgSetting.getPkg();
if (pkg == null) {
continue;
@@ -2948,14 +3043,14 @@
}
ipw.println("Compiler stats:");
ipw.increaseIndent();
- Collection<PackageSetting> pkgSettings;
+ Collection<? extends PackageStateInternal> pkgSettings;
if (setting != null) {
pkgSettings = Collections.singletonList(setting);
} else {
- pkgSettings = mSettings.getPackagesLocked().values();
+ pkgSettings = mSettings.getPackages().values();
}
- for (PackageSetting pkgSetting : pkgSettings) {
+ for (PackageStateInternal pkgSetting : pkgSettings) {
final AndroidPackage pkg = pkgSetting.getPkg();
if (pkg == null) {
continue;
diff --git a/services/core/java/com/android/server/pm/ComputerLocked.java b/services/core/java/com/android/server/pm/ComputerLocked.java
index b83f987..5f91e68 100644
--- a/services/core/java/com/android/server/pm/ComputerLocked.java
+++ b/services/core/java/com/android/server/pm/ComputerLocked.java
@@ -32,6 +32,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
+import com.android.server.pm.pkg.PackageStateInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -121,16 +122,16 @@
filterCallingUid, userId);
}
}
- public PackageSetting getPackageSettingInternal(String packageName, int callingUid) {
+ public PackageStateInternal getPackageStateInternal(String packageName, int callingUid) {
synchronized (mLock) {
- return super.getPackageSettingInternal(packageName, callingUid);
+ return super.getPackageStateInternal(packageName, callingUid);
}
}
@Nullable
- public PackageState getPackageState(@NonNull String packageName) {
+ public PackageState getPackageStateCopied(@NonNull String packageName) {
synchronized (mLock) {
- return super.getPackageState(packageName);
+ return super.getPackageStateCopied(packageName);
}
}
diff --git a/services/core/java/com/android/server/pm/ComputerTracker.java b/services/core/java/com/android/server/pm/ComputerTracker.java
index 5c25f67..4f2127c 100644
--- a/services/core/java/com/android/server/pm/ComputerTracker.java
+++ b/services/core/java/com/android/server/pm/ComputerTracker.java
@@ -35,6 +35,7 @@
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
+import com.android.server.pm.pkg.PackageStateInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -249,7 +250,7 @@
current.release();
}
}
- public PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) {
+ public PackageInfo generatePackageInfo(PackageStateInternal ps, int flags, int userId) {
ThreadComputer current = live();
try {
return current.mComputer.generatePackageInfo(ps, flags, userId);
@@ -275,28 +276,28 @@
current.release();
}
}
- public PackageSetting getPackageSetting(String packageName) {
+ public PackageStateInternal getPackageStateInternal(String packageName) {
ThreadComputer current = snapshot();
try {
- return current.mComputer.getPackageSetting(packageName);
+ return current.mComputer.getPackageStateInternal(packageName);
} finally {
current.release();
}
}
- public PackageSetting getPackageSettingInternal(String packageName, int callingUid) {
+ public PackageStateInternal getPackageStateInternal(String packageName, int callingUid) {
ThreadComputer current = live();
try {
- return current.mComputer.getPackageSettingInternal(packageName, callingUid);
+ return current.mComputer.getPackageStateInternal(packageName, callingUid);
} finally {
current.release();
}
}
@Nullable
- public PackageState getPackageState(@NonNull String packageName) {
+ public PackageState getPackageStateCopied(@NonNull String packageName) {
ThreadComputer current = live();
try {
- return current.mComputer.getPackageState(packageName);
+ return current.mComputer.getPackageStateCopied(packageName);
} finally {
current.release();
}
@@ -424,7 +425,7 @@
current.release();
}
}
- public boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid,
+ public boolean filterSharedLibPackageLPr(@Nullable PackageStateInternal ps, int uid,
int userId, int flags) {
ThreadComputer current = live();
try {
@@ -503,7 +504,7 @@
current.release();
}
}
- public boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps,
+ public boolean shouldFilterApplicationLocked(@Nullable PackageStateInternal ps,
int callingUid, @Nullable ComponentName component,
@PackageManager.ComponentType int componentType, int userId) {
ThreadComputer current = live();
@@ -514,7 +515,7 @@
current.release();
}
}
- public boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps,
+ public boolean shouldFilterApplicationLocked(@Nullable PackageStateInternal ps,
int callingUid, int userId) {
ThreadComputer current = live();
try {
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 0feb9c5..d8107fc 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -44,7 +44,6 @@
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.VersionedPackage;
-import android.content.pm.pkg.PackageUserState;
import android.net.Uri;
import android.os.Binder;
import android.os.Process;
@@ -63,6 +62,7 @@
import com.android.internal.util.Preconditions;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.pkg.PackageUserState;
import com.android.server.wm.ActivityTaskManagerInternal;
import java.util.Collections;
@@ -156,9 +156,9 @@
}
if (versionCode != PackageManager.VERSION_CODE_HIGHEST
- && uninstalledPs.getLongVersionCode() != versionCode) {
+ && uninstalledPs.getVersionCode() != versionCode) {
Slog.w(TAG, "Not removing package " + packageName + " with versionCode "
- + uninstalledPs.getLongVersionCode() + " != " + versionCode);
+ + uninstalledPs.getVersionCode() + " != " + versionCode);
return PackageManager.DELETE_FAILED_INTERNAL_ERROR;
}
@@ -252,7 +252,7 @@
for (int i = 0; i < allUsers.length; i++) {
TempUserState priorUserState = priorUserStates.get(allUsers[i]);
int enabledState = priorUserState.enabledState;
- PackageSetting pkgSetting = mPm.getPackageSetting(packageName);
+ PackageSetting pkgSetting = mPm.getPackageSettingForMutation(packageName);
pkgSetting.setEnabled(enabledState, allUsers[i],
priorUserState.lastDisableAppCaller);
@@ -730,6 +730,9 @@
Log.i(TAG, "Observer no longer exists.");
} //end catch
notifyPackageChangeObserversOnDelete(packageName, versionCode);
+
+ // Prune unused static shared libraries which have been cached a period of time
+ mPm.schedulePruneUnusedStaticSharedLibraries(true /* delay */);
});
}
diff --git a/services/core/java/com/android/server/pm/DomainVerificationConnection.java b/services/core/java/com/android/server/pm/DomainVerificationConnection.java
index 1a3f422..0ee0989 100644
--- a/services/core/java/com/android/server/pm/DomainVerificationConnection.java
+++ b/services/core/java/com/android/server/pm/DomainVerificationConnection.java
@@ -29,6 +29,7 @@
import com.android.internal.util.FunctionalUtils;
import com.android.server.DeviceIdleInternal;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.verify.domain.DomainVerificationService;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2;
@@ -116,20 +117,20 @@
@Override
public void withPackageSettingsSnapshot(
- @NonNull Consumer<Function<String, PackageSetting>> block) {
+ @NonNull Consumer<Function<String, PackageStateInternal>> block) {
mPmInternal.withPackageSettingsSnapshot(block);
}
@Override
public <Output> Output withPackageSettingsSnapshotReturning(
- @NonNull FunctionalUtils.ThrowingFunction<Function<String, PackageSetting>, Output>
- block) {
+ @NonNull FunctionalUtils.ThrowingFunction<Function<String, PackageStateInternal>,
+ Output> block) {
return mPmInternal.withPackageSettingsSnapshotReturning(block);
}
@Override
public <ExceptionType extends Exception> void withPackageSettingsSnapshotThrowing(
- @NonNull FunctionalUtils.ThrowingCheckedConsumer<Function<String, PackageSetting>,
+ @NonNull FunctionalUtils.ThrowingCheckedConsumer<Function<String, PackageStateInternal>,
ExceptionType> block) throws ExceptionType {
mPmInternal.withPackageSettingsSnapshotThrowing(block);
}
@@ -138,7 +139,8 @@
public <ExceptionOne extends Exception, ExceptionTwo extends Exception> void
withPackageSettingsSnapshotThrowing2(
@NonNull FunctionalUtils.ThrowingChecked2Consumer<
- Function<String, PackageSetting>, ExceptionOne, ExceptionTwo> block)
+ Function<String, PackageStateInternal>, ExceptionOne,
+ ExceptionTwo> block)
throws ExceptionOne, ExceptionTwo {
mPmInternal.withPackageSettingsSnapshotThrowing2(block);
}
@@ -147,7 +149,7 @@
public <Output, ExceptionType extends Exception> Output
withPackageSettingsSnapshotReturningThrowing(
@NonNull FunctionalUtils.ThrowingCheckedFunction<
- Function<String, PackageSetting>, Output, ExceptionType> block)
+ Function<String, PackageStateInternal>, Output, ExceptionType> block)
throws ExceptionType {
return mPmInternal.withPackageSettingsSnapshotReturningThrowing(block);
}
diff --git a/services/core/java/com/android/server/pm/DumpHelper.java b/services/core/java/com/android/server/pm/DumpHelper.java
index d43b681..c670e1f 100644
--- a/services/core/java/com/android/server/pm/DumpHelper.java
+++ b/services/core/java/com/android/server/pm/DumpHelper.java
@@ -241,7 +241,7 @@
// Return if the package doesn't exist.
if (packageName != null
- && mPm.getPackageSetting(packageName) == null
+ && mPm.getPackageStateInternal(packageName) == null
&& !mPm.mApexManager.isApexPackage(packageName)) {
pw.println("Unable to find package: " + packageName);
return;
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 4c70cda..dc5b85a 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -70,6 +70,7 @@
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.pkg.PackageUserState;
import com.android.server.rollback.RollbackManagerInternal;
import com.android.server.utils.WatchedLongSparseArray;
diff --git a/services/core/java/com/android/server/pm/InstallParams.java b/services/core/java/com/android/server/pm/InstallParams.java
index e138188..56cb4ad 100644
--- a/services/core/java/com/android/server/pm/InstallParams.java
+++ b/services/core/java/com/android/server/pm/InstallParams.java
@@ -123,6 +123,7 @@
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.permission.Permission;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.pkg.PackageStateInternal;
import libcore.io.IoUtils;
@@ -1550,7 +1551,7 @@
AndroidPackage oldPackage = mPm.mPackages.get(packageName);
// Set the update and install times
- PackageSetting deletedPkgSetting = mPm.getPackageSetting(
+ PackageStateInternal deletedPkgSetting = mPm.getPackageStateInternal(
oldPackage.getPackageName());
reconciledPkg.mPkgSetting
.setFirstInstallTime(deletedPkgSetting.getFirstInstallTime())
@@ -1859,7 +1860,12 @@
}
incrementalStorages.add(storage);
}
- appDataHelper.prepareAppDataAfterInstallLIF(pkg);
+ int previousAppId = 0;
+ if (reconciledPkg.mScanResult.needsNewAppId()) {
+ // Only set previousAppId if the app is migrating out of shared UID
+ previousAppId = reconciledPkg.mScanResult.mPreviousAppId;
+ }
+ appDataHelper.prepareAppDataPostCommitLIF(pkg, previousAppId);
if (reconciledPkg.mPrepareResult.mClearCodeCache) {
appDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
@@ -1989,7 +1995,7 @@
PackageChangeEvent pkgChangeEvent = new PackageChangeEvent();
pkgChangeEvent.packageName = pkgSetting.getPkg().getPackageName();
- pkgChangeEvent.version = pkgSetting.getLongVersionCode();
+ pkgChangeEvent.version = pkgSetting.getVersionCode();
pkgChangeEvent.lastUpdateTimeMillis = pkgSetting.getLastUpdateTime();
pkgChangeEvent.newInstalled = (pkgRemovedInfo == null || !pkgRemovedInfo.mIsUpdate);
pkgChangeEvent.dataRemoved = (pkgRemovedInfo != null && pkgRemovedInfo.mDataRemoved);
diff --git a/services/core/java/com/android/server/pm/InstallSource.java b/services/core/java/com/android/server/pm/InstallSource.java
index f13a0f5..2b56c71 100644
--- a/services/core/java/com/android/server/pm/InstallSource.java
+++ b/services/core/java/com/android/server/pm/InstallSource.java
@@ -26,7 +26,7 @@
* Immutable class holding information about where the request to install or update an app
* came from.
*/
-final class InstallSource {
+public final class InstallSource {
/**
* An instance of InstallSource representing an absence of knowledge of the source of
* a package. Used in preference to null.
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 7e002bf..55355d8 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -26,7 +26,6 @@
import android.os.CreateAppDataArgs;
import android.os.CreateAppDataResult;
import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
import android.os.IInstalld;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -148,12 +147,9 @@
IBinder binder = ServiceManager.getService("installd");
if (binder != null) {
try {
- binder.linkToDeath(new DeathRecipient() {
- @Override
- public void binderDied() {
- Slog.w(TAG, "installd died; reconnecting");
- connect();
- }
+ binder.linkToDeath(() -> {
+ Slog.w(TAG, "installd died; reconnecting");
+ connect();
}, 0);
} catch (RemoteException e) {
binder = null;
@@ -168,9 +164,7 @@
}
} else {
Slog.w(TAG, "installd not found; trying again");
- BackgroundThread.getHandler().postDelayed(() -> {
- connect();
- }, DateUtils.SECOND_IN_MILLIS);
+ BackgroundThread.getHandler().postDelayed(this::connect, DateUtils.SECOND_IN_MILLIS);
}
}
@@ -192,7 +186,9 @@
}
}
- private static CreateAppDataArgs buildCreateAppDataArgs(String uuid, String packageName,
+ // We explicitly do NOT set previousAppId because the default value should always be 0.
+ // Manually override previousAppId after building CreateAppDataArgs for specific behaviors.
+ static CreateAppDataArgs buildCreateAppDataArgs(String uuid, String packageName,
int userId, int flags, int appId, String seInfo, int targetSdkVersion) {
final CreateAppDataArgs args = new CreateAppDataArgs();
args.uuid = uuid;
@@ -213,23 +209,6 @@
return result;
}
- /**
- * @deprecated callers are encouraged to migrate to using {@link Batch} to
- * more efficiently handle operations in bulk.
- */
- @Deprecated
- public long createAppData(String uuid, String packageName, int userId, int flags, int appId,
- String seInfo, int targetSdkVersion) throws InstallerException {
- final CreateAppDataArgs args = buildCreateAppDataArgs(uuid, packageName, userId, flags,
- appId, seInfo, targetSdkVersion);
- final CreateAppDataResult result = createAppData(args);
- if (result.exceptionCode == 0) {
- return result.ceDataInode;
- } else {
- throw new InstallerException(result.exceptionMessage);
- }
- }
-
public @NonNull CreateAppDataResult createAppData(@NonNull CreateAppDataArgs args)
throws InstallerException {
if (!checkBeforeRemote()) {
@@ -284,13 +263,11 @@
* Callers of this method are not required to hold a monitor lock on an
* {@link Installer} object.
*/
- public synchronized @NonNull CompletableFuture<Long> createAppData(String uuid,
- String packageName, int userId, int flags, int appId, String seInfo,
- int targetSdkVersion) {
- if (mExecuted) throw new IllegalStateException();
-
- final CreateAppDataArgs args = buildCreateAppDataArgs(uuid, packageName, userId, flags,
- appId, seInfo, targetSdkVersion);
+ @NonNull
+ public synchronized CompletableFuture<Long> createAppData(CreateAppDataArgs args) {
+ if (mExecuted) {
+ throw new IllegalStateException();
+ }
final CompletableFuture<Long> future = new CompletableFuture<>();
mArgs.add(args);
mFutures.add(future);
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 37f5a4d..f7d4dba 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -58,6 +58,8 @@
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.utils.Snappable;
import com.android.server.utils.SnapshotCache;
import com.android.server.utils.Watchable;
@@ -372,14 +374,15 @@
@GuardedBy("mService.mLock")
public void onPackageInstalledLPw(@NonNull AndroidPackage pkg, @NonNull int[] userIds) {
- PackageSetting ps = mPmInternal.getPackageSetting(pkg.getPackageName());
+ PackageStateInternal ps = mPmInternal.getPackageStateInternal(pkg.getPackageName());
if (ps == null) {
return;
}
for (int userId : userIds) {
// Ignore not installed apps
- if (mService.mPackages.get(pkg.getPackageName()) == null || !ps.getInstalled(userId)) {
+ if (mService.mPackages.get(pkg.getPackageName()) == null
+ || !ps.getUserStateOrDefault(userId).isInstalled()) {
continue;
}
@@ -387,7 +390,7 @@
propagateInstantAppPermissionsIfNeeded(pkg, userId);
// Track instant apps
- if (ps.getInstantApp(userId)) {
+ if (ps.getUserStateOrDefault(userId).isInstantApp()) {
addInstantAppLPw(userId, ps.getAppId());
}
@@ -791,20 +794,22 @@
final int packageCount = mService.mPackages.size();
for (int i = 0; i < packageCount; i++) {
final AndroidPackage pkg = mService.mPackages.valueAt(i);
- final PackageSetting ps = mPmInternal.getPackageSetting(pkg.getPackageName());
+ final PackageStateInternal ps =
+ mPmInternal.getPackageStateInternal(pkg.getPackageName());
if (ps == null) {
continue;
}
- if (now - ps.getPkgState().getLatestPackageUseTimeInMills()
+ if (now - ps.getTransientState().getLatestPackageUseTimeInMills()
< maxInstalledCacheDuration) {
continue;
}
boolean installedOnlyAsInstantApp = false;
for (int userId : allUsers) {
- if (ps.getInstalled(userId)) {
- if (ps.getInstantApp(userId)) {
+ final PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
+ if (userState.isInstalled()) {
+ if (userState.isInstantApp()) {
installedOnlyAsInstantApp = true;
} else {
installedOnlyAsInstantApp = false;
@@ -831,23 +836,23 @@
} else if (rhsPkg == null) {
return 1;
} else {
- final PackageSetting lhsPs = mPmInternal.getPackageSetting(
- lhsPkg.getPackageName());
+ final PackageStateInternal lhsPs =
+ mPmInternal.getPackageStateInternal(lhsPkg.getPackageName());
if (lhsPs == null) {
return 0;
}
- final PackageSetting rhsPs = mPmInternal.getPackageSetting(
- rhsPkg.getPackageName());
+ final PackageStateInternal rhsPs =
+ mPmInternal.getPackageStateInternal(rhsPkg.getPackageName());
if (rhsPs == null) {
return 0;
}
- if (lhsPs.getPkgState().getLatestPackageUseTimeInMills() >
- rhsPs.getPkgState().getLatestPackageUseTimeInMills()) {
+ if (lhsPs.getTransientState().getLatestPackageUseTimeInMills() >
+ rhsPs.getTransientState().getLatestPackageUseTimeInMills()) {
return 1;
- } else if (lhsPs.getPkgState().getLatestPackageUseTimeInMills() <
- rhsPs.getPkgState().getLatestPackageUseTimeInMills()) {
+ } else if (lhsPs.getTransientState().getLatestPackageUseTimeInMills() <
+ rhsPs.getTransientState().getLatestPackageUseTimeInMills()) {
return -1;
} else if (lhsPs.getFirstInstallTime() > rhsPs.getFirstInstallTime()) {
return 1;
@@ -927,8 +932,9 @@
final int packageCount = mService.mPackages.size();
for (int i = 0; i < packageCount; i++) {
final AndroidPackage pkg = mService.mPackages.valueAt(i);
- final PackageSetting ps = mPmInternal.getPackageSetting(pkg.getPackageName());
- if (ps == null || !ps.getInstantApp(userId)) {
+ final PackageStateInternal ps =
+ mPmInternal.getPackageStateInternal(pkg.getPackageName());
+ if (ps == null || !ps.getUserStateOrDefault(userId).isInstantApp()) {
continue;
}
final InstantAppInfo info = createInstantAppInfoForPackage(
@@ -949,11 +955,11 @@
InstantAppInfo createInstantAppInfoForPackage(
@NonNull AndroidPackage pkg, @UserIdInt int userId,
boolean addApplicationInfo) {
- PackageSetting ps = mPmInternal.getPackageSetting(pkg.getPackageName());
+ PackageStateInternal ps = mPmInternal.getPackageStateInternal(pkg.getPackageName());
if (ps == null) {
return null;
}
- if (!ps.getInstalled(userId)) {
+ if (!ps.getUserStateOrDefault(userId).isInstalled()) {
return null;
}
@@ -968,7 +974,7 @@
// TODO(b/135203078): This may be broken due to inner mutability problems that were broken
// as part of moving to PackageInfoUtils. Flags couldn't be determined.
ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(ps.getPkg(), 0,
- ps.readUserState(userId), userId, ps);
+ ps.getUserStateOrDefault(userId), userId, ps);
if (addApplicationInfo) {
return new InstantAppInfo(appInfo, requestedPermissions, grantedPermissions);
} else {
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 9122221f..61a6ed4 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -39,6 +39,7 @@
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.pkg.PackageStateInternal;
import java.io.File;
import java.io.FileDescriptor;
@@ -404,7 +405,7 @@
continue;
}
- PackageSetting pkgSetting = pmInt.getPackageSetting(pkg.getPackageName());
+ PackageStateInternal pkgSetting = pmInt.getPackageStateInternal(pkg.getPackageName());
final String[] instructionSets = getAppDexInstructionSets(
AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting),
AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting));
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index cac1978..e845aec 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -78,6 +78,7 @@
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.pkg.PackageStateInternal;
import dalvik.system.DexFile;
@@ -185,8 +186,12 @@
}
// We do not dexopt unused packages.
+ // It's possible for this to be called before app hibernation service is ready due to
+ // an OTA dexopt. In this case, we ignore the hibernation check here. This is fine since
+ // a hibernating app should have no artifacts to copy in the first place.
AppHibernationManagerInternal ahm = mInjector.getAppHibernationManagerInternal();
- if (ahm.isHibernatingGlobally(pkg.getPackageName())
+ if (ahm != null
+ && ahm.isHibernatingGlobally(pkg.getPackageName())
&& ahm.isOatArtifactDeletionEnabled()) {
return false;
}
@@ -202,7 +207,7 @@
* synchronized on {@link #mInstallLock}.
*/
@DexOptResult
- int performDexOpt(AndroidPackage pkg, @NonNull PackageSetting pkgSetting,
+ int performDexOpt(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting,
String[] instructionSets, CompilerStats.PackageStats packageStats,
PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
if (PLATFORM_PACKAGE_NAME.equals(pkg.getPackageName())) {
@@ -242,12 +247,12 @@
*/
@GuardedBy("mInstallLock")
@DexOptResult
- private int performDexOptLI(AndroidPackage pkg, @NonNull PackageSetting pkgSetting,
+ private int performDexOptLI(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting,
String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
// ClassLoader only refers non-native (jar) shared libraries and must ignore
// native (so) shared libraries. See also LoadedApk#createSharedLibraryLoader().
- final List<SharedLibraryInfo> sharedLibraries = pkgSetting.getPkgState()
+ final List<SharedLibraryInfo> sharedLibraries = pkgSetting.getTransientState()
.getNonNativeUsesLibraryInfos();
final String[] instructionSets = targetInstructionSets != null ?
targetInstructionSets : getAppDexInstructionSets(
@@ -390,10 +395,11 @@
*/
@GuardedBy("mInstallLock")
@DexOptResult
- private int dexOptPath(AndroidPackage pkg, @NonNull PackageSetting pkgSetting, String path,
- String isa, String compilerFilter, int profileAnalysisResult, String classLoaderContext,
- int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade,
- String profileName, String dexMetadataPath, int compilationReason) {
+ private int dexOptPath(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting,
+ String path, String isa, String compilerFilter, int profileAnalysisResult,
+ String classLoaderContext, int dexoptFlags, int uid,
+ CompilerStats.PackageStats packageStats, boolean downgrade, String profileName,
+ String dexMetadataPath, int compilationReason) {
int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, classLoaderContext,
profileAnalysisResult, downgrade);
if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
@@ -401,7 +407,7 @@
}
String oatDir = getPackageOatDirIfSupported(pkg,
- pkgSetting.getPkgState().isUpdatedSystemApp());
+ pkgSetting.getTransientState().isUpdatedSystemApp());
Log.i(TAG, "Running dexopt (dexoptNeeded=" + dexoptNeeded + ") on: " + path
+ " pkg=" + pkg.getPackageName() + " isa=" + isa
@@ -645,8 +651,8 @@
/**
* Dumps the dexopt state of the given package {@code pkg} to the given {@code PrintWriter}.
*/
- void dumpDexoptState(IndentingPrintWriter pw, AndroidPackage pkg, PackageSetting pkgSetting,
- PackageDexUsage.PackageUseInfo useInfo) {
+ void dumpDexoptState(IndentingPrintWriter pw, AndroidPackage pkg,
+ PackageStateInternal pkgSetting, PackageDexUsage.PackageUseInfo useInfo) {
final String[] instructionSets = getAppDexInstructionSets(
AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting),
AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting));
@@ -783,7 +789,7 @@
info.getHiddenApiEnforcementPolicy(), info.splitDependencies,
info.requestsIsolatedSplitLoading(), compilerFilter, options);
}
- private int getDexFlags(AndroidPackage pkg, @NonNull PackageSetting pkgSetting,
+ private int getDexFlags(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting,
String compilerFilter, DexoptOptions options) {
return getDexFlags(pkg.isDebuggable(),
AndroidPackageUtils.getHiddenApiEnforcementPolicy(pkg, pkgSetting),
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index 3c3fb56..e3a772c 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -24,6 +24,7 @@
import static com.android.server.pm.PackageManagerService.CHECK_PENDING_VERIFICATION;
import static com.android.server.pm.PackageManagerService.DEBUG_BACKUP;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
+import static com.android.server.pm.PackageManagerService.DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD;
import static com.android.server.pm.PackageManagerService.DEFAULT_VERIFICATION_RESPONSE;
import static com.android.server.pm.PackageManagerService.DEFERRED_NO_KILL_INSTALL_OBSERVER;
import static com.android.server.pm.PackageManagerService.DEFERRED_NO_KILL_POST_DELETE;
@@ -37,6 +38,7 @@
import static com.android.server.pm.PackageManagerService.INTEGRITY_VERIFICATION_COMPLETE;
import static com.android.server.pm.PackageManagerService.PACKAGE_VERIFIED;
import static com.android.server.pm.PackageManagerService.POST_INSTALL;
+import static com.android.server.pm.PackageManagerService.PRUNE_UNUSED_STATIC_SHARED_LIBRARIES;
import static com.android.server.pm.PackageManagerService.SEND_PENDING_BROADCAST;
import static com.android.server.pm.PackageManagerService.SNAPSHOT_UNCORK;
import static com.android.server.pm.PackageManagerService.TAG;
@@ -62,6 +64,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.stats.storage.StorageEnums;
@@ -75,6 +78,7 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
import dalvik.system.VMRuntime;
@@ -82,6 +86,8 @@
import java.util.Arrays;
import java.util.Collections;
+import java.io.IOException;
+
/**
* Part of PackageManagerService that handles events.
*/
@@ -472,6 +478,18 @@
}
break;
}
+ case PRUNE_UNUSED_STATIC_SHARED_LIBRARIES: {
+ try {
+ mPm.pruneUnusedStaticSharedLibraries(Long.MAX_VALUE,
+ Settings.Global.getLong(mPm.mContext.getContentResolver(),
+ Settings.Global.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD,
+ DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD));
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to prune unused static shared libraries :"
+ + e.getMessage());
+ }
+ break;
+ }
}
}
@@ -481,9 +499,10 @@
boolean succeeded = res.mReturnCode == PackageManager.INSTALL_SUCCEEDED;
final boolean update = res.mRemovedInfo != null && res.mRemovedInfo.mRemovedPackage != null;
final String packageName = res.mName;
- final PackageSetting pkgSetting = succeeded ? mPm.getPackageSetting(packageName) : null;
+ final PackageStateInternal pkgSetting =
+ succeeded ? mPm.getPackageStateInternal(packageName) : null;
final boolean removedBeforeUpdate = (pkgSetting == null)
- || (pkgSetting.isSystem() && !pkgSetting.getPathString().equals(
+ || (pkgSetting.isSystem() && !pkgSetting.getPath().getPath().equals(
res.mPkg.getPath()));
if (succeeded && removedBeforeUpdate) {
Slog.e(TAG, packageName + " was removed before handlePackagePostInstall "
@@ -530,7 +549,8 @@
int[] instantUserIds = EMPTY_INT_ARRAY;
final boolean allNewUsers = res.mOrigUsers == null || res.mOrigUsers.length == 0;
for (int newUser : res.mNewUsers) {
- final boolean isInstantApp = pkgSetting.getInstantApp(newUser);
+ final boolean isInstantApp = pkgSetting.getUserStateOrDefault(newUser)
+ .isInstantApp();
if (allNewUsers) {
if (isInstantApp) {
firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);
@@ -585,7 +605,7 @@
synchronized (mPm.mLock) {
newBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList(
- mPm.getPackageSettingInternal(res.mName, Process.SYSTEM_UID),
+ mPm.getPackageStateInternal(res.mName, Process.SYSTEM_UID),
updateUserIds, mPm.mSettings.getPackagesLocked());
}
mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
@@ -752,6 +772,9 @@
} else {
mPm.notifyInstallObserver(res, installObserver);
}
+
+ // Prune unused static shared libraries which have been cached a period of time
+ mPm.schedulePruneUnusedStaticSharedLibraries(true /* delay */);
}
/**
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 97f8dc2..7fd7505 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -154,6 +154,7 @@
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
import libcore.io.IoUtils;
import libcore.util.EmptyArray;
@@ -354,8 +355,13 @@
private final AtomicBoolean mCommitted = new AtomicBoolean(false);
+ /**
+ * True if staging files are being used by external entities like {@link PackageSessionVerifier}
+ * or {@link PackageManagerService} which means it is not safe for {@link #abandon()} to clean
+ * up the files.
+ */
@GuardedBy("mLock")
- private boolean mRelinquished = false;
+ private boolean mStageDirInUse = false;
/** Permissions have been accepted by the user (see {@link #setPermissionsResult}) */
@GuardedBy("mLock")
@@ -386,6 +392,9 @@
@GuardedBy("mLock")
private int mParentSessionId;
+ @GuardedBy("mLock")
+ private boolean mHasDeviceAdminReceiver;
+
static class FileEntry {
private final int mIndex;
private final InstallationFile mFile;
@@ -466,12 +475,6 @@
@GuardedBy("mLock")
@Nullable
private Runnable mPendingAbandonCallback;
- /**
- * {@code true} if pre-reboot verification is ongoing which means it is not safe for
- * {@link #abandon()} to clean up staging directories.
- */
- @GuardedBy("mLock")
- private boolean mInPreRebootVerification;
StagedSession(boolean isReady, boolean isApplied, boolean isFailed, int errorCode,
String errorMessage) {
@@ -707,7 +710,7 @@
dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
maybeCleanUpChildSessions();
};
- if (mInPreRebootVerification) {
+ if (mStageDirInUse) {
// Pre-reboot verification is ongoing, not safe to clean up the session yet.
mPendingAbandonCallback = r;
mCallback.onStagedSessionChanged(PackageInstallerSession.this);
@@ -718,29 +721,13 @@
}
/**
- * Called when pre-reboot verification is about to start. This shouldn't be called
- * on a destroyed session.
- */
- private void notifyStartPreRebootVerification() {
- synchronized (mLock) {
- Preconditions.checkState(!mDestroyed);
- if (mInPreRebootVerification) {
- throw new IllegalStateException("Pre-reboot verification has started");
- }
- mInPreRebootVerification = true;
- }
- }
-
- /**
* Called when pre-reboot verification has ended.
* Now it is safe to clean up the session if {@link #abandon()} has been called previously.
*/
private void notifyEndPreRebootVerification() {
synchronized (mLock) {
- if (!mInPreRebootVerification) {
- throw new IllegalStateException("Pre-reboot verification not started");
- }
- mInPreRebootVerification = false;
+ Preconditions.checkState(mStageDirInUse);
+ mStageDirInUse = false;
}
dispatchPendingAbandonCallback();
}
@@ -756,7 +743,6 @@
assertCallerIsOwnerOrRootOrSystem();
Preconditions.checkArgument(isCommitted());
Preconditions.checkArgument(!isInTerminalState());
- notifyStartPreRebootVerification();
verify();
}
@@ -944,11 +930,13 @@
@UserActionRequirement
private int computeUserActionRequirement() {
final String packageName;
+ final boolean hasDeviceAdminReceiver;
synchronized (mLock) {
if (mPermissionsManuallyAccepted) {
return USER_ACTION_NOT_NEEDED;
}
packageName = mPackageName;
+ hasDeviceAdminReceiver = mHasDeviceAdminReceiver;
}
final boolean forcePermissionPrompt =
@@ -971,6 +959,9 @@
final boolean isUpdateWithoutUserActionPermissionGranted = (mPm.checkUidPermission(
android.Manifest.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION, mInstallerUid)
== PackageManager.PERMISSION_GRANTED);
+ final boolean isInstallDpcPackagesPermissionGranted = (mPm.checkUidPermission(
+ android.Manifest.permission.INSTALL_DPC_PACKAGES, mInstallerUid)
+ == PackageManager.PERMISSION_GRANTED);
final int targetPackageUid = mPm.getPackageUid(packageName, 0, userId);
final boolean isUpdate = targetPackageUid != -1 || isApexSession();
final InstallSourceInfo existingInstallSourceInfo = isUpdate
@@ -984,7 +975,8 @@
final boolean isSelfUpdate = targetPackageUid == mInstallerUid;
final boolean isPermissionGranted = isInstallPermissionGranted
|| (isUpdatePermissionGranted && isUpdate)
- || (isSelfUpdatePermissionGranted && isSelfUpdate);
+ || (isSelfUpdatePermissionGranted && isSelfUpdate)
+ || (isInstallDpcPackagesPermissionGranted && hasDeviceAdminReceiver);
final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID);
final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID);
@@ -1936,13 +1928,13 @@
*/
private static boolean isIncrementalInstallationAllowed(String packageName) {
final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
- final PackageSetting existingPkgSetting = pmi.getPackageSetting(packageName);
+ final PackageStateInternal existingPkgSetting = pmi.getPackageStateInternal(packageName);
if (existingPkgSetting == null || existingPkgSetting.getPkg() == null) {
return true;
}
return !existingPkgSetting.getPkg().isSystem()
- && !existingPkgSetting.getPkgState().isUpdatedSystemApp();
+ && !existingPkgSetting.getTransientState().isUpdatedSystemApp();
}
/**
@@ -2351,21 +2343,6 @@
return;
}
}
-
- if (!params.isStaged) {
- // For non-staged APEX installs also check if there is a staged session that
- // contains the same APEX. If that's the case, we should fail this session.
- synchronized (mLock) {
- int sessionId = mStagingManager.getSessionIdByPackageName(mPackageName);
- if (sessionId != -1) {
- onSessionValidationFailure(
- PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
- "Staged session " + sessionId + " already contains "
- + mPackageName);
- return;
- }
- }
- }
}
if (params.isStaged) {
@@ -2418,9 +2395,9 @@
return;
}
synchronized (mLock) {
- if (mRelinquished) {
+ if (mStageDirInUse) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Session relinquished");
+ "Session files in use");
}
if (mDestroyed) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
@@ -2482,9 +2459,9 @@
private void parseApkAndExtractNativeLibraries() throws PackageManagerException {
synchronized (mLock) {
- if (mRelinquished) {
+ if (mStageDirInUse) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Session relinquished");
+ "Session files in use");
}
if (mDestroyed) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
@@ -2524,34 +2501,27 @@
private void verifyNonStaged()
throws PackageManagerException {
- final VerificationParams verifyingSession = prepareForVerification();
- if (isMultiPackage()) {
- final List<PackageInstallerSession> childSessions = getChildSessions();
- List<VerificationParams> verifyingChildSessions =
- new ArrayList<>(childSessions.size());
- boolean success = true;
- PackageManagerException failure = null;
- for (int i = 0; i < childSessions.size(); ++i) {
- final PackageInstallerSession session = childSessions.get(i);
- try {
- final VerificationParams verifyingChildSession =
- session.prepareForVerification();
- verifyingChildSessions.add(verifyingChildSession);
- } catch (PackageManagerException e) {
- failure = e;
- success = false;
- }
+ synchronized (mLock) {
+ if (mDestroyed) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Session destroyed");
}
- if (!success) {
- sendOnPackageInstalled(mContext, getRemoteStatusReceiver(), sessionId,
- isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId, null,
- failure.error, failure.getLocalizedMessage(), null);
- return;
- }
- verifyingSession.verifyStage(verifyingChildSessions);
- } else {
- verifyingSession.verifyStage();
+ // Set this flag to prevent abandon() from deleting staging files while verification is
+ // in progress. For staged sessions, we will reset this flag when verification is done
+ // so abandon() can take effect. For non-staged sessions, the staging files will be
+ // deleted when install is completed (no matter success or not). No need to reset
+ // the flag.
+ mStageDirInUse = true;
}
+ mSessionProvider.getSessionVerifier().verifyNonStaged(this, (error, msg) -> {
+ mHandler.post(() -> {
+ if (error == INSTALL_SUCCEEDED) {
+ onVerificationComplete();
+ } else {
+ onSessionVerificationFailure(error, msg);
+ }
+ });
+ });
}
private void install() {
@@ -2600,32 +2570,6 @@
}
}
- /**
- * Stages this session for verification and returns a
- * {@link VerificationParams} representing this new staged state or null
- * in case permissions need to be requested before verification can proceed.
- */
- @NonNull
- private VerificationParams prepareForVerification() throws PackageManagerException {
- assertNotLocked("makeSessionActive");
-
- synchronized (mLock) {
- if (mRelinquished) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Session relinquished");
- }
- if (mDestroyed) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Session destroyed");
- }
- if (!mSealed) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Session not sealed");
- }
- return makeVerificationParamsLocked();
- }
- }
-
private void sendPendingUserActionIntent() {
// User needs to confirm installation;
// give installer an intent they can use to involve
@@ -2641,50 +2585,6 @@
closeInternal(false);
}
- @GuardedBy("mLock")
- @Nullable
- /**
- * Returns a {@link com.android.server.pm.VerificationParams}
- */
- private VerificationParams makeVerificationParamsLocked() {
- final IPackageInstallObserver2 localObserver;
- if (!hasParentSessionId()) {
- // Avoid attaching this observer to child session since they won't use it.
- localObserver = new IPackageInstallObserver2.Stub() {
- @Override
- public void onUserActionRequired(Intent intent) {
- throw new IllegalStateException();
- }
-
- @Override
- public void onPackageInstalled(String basePackageName, int returnCode, String msg,
- Bundle extras) {
- mHandler.post(() -> {
- if (returnCode == INSTALL_SUCCEEDED) {
- onVerificationComplete();
- } else {
- onSessionVerificationFailure(returnCode, msg);
- }
- });
- }
- };
- } else {
- localObserver = null;
- }
-
- final UserHandle user;
- if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
- user = UserHandle.ALL;
- } else {
- user = new UserHandle(userId);
- }
-
- mRelinquished = true;
-
- return new VerificationParams(user, stageDir, localObserver, params,
- mInstallSource, mInstallerUid, mSigningDetails, sessionId, mPackageLite, mPm);
- }
-
@WorkerThread
private void onVerificationComplete() {
// APK verification is done. Continue the installation depending on whether it is a
@@ -2828,7 +2728,7 @@
private long getApksSize(String packageName) {
final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
- final PackageSetting ps = pmi.getPackageSetting(packageName);
+ final PackageStateInternal ps = pmi.getPackageStateInternal(packageName);
if (ps == null) {
return 0;
}
@@ -2963,6 +2863,7 @@
}
mSigningDetails = apk.getSigningDetails();
+ mHasDeviceAdminReceiver = apk.isHasDeviceAdminReceiver();
}
/**
@@ -3053,6 +2954,7 @@
if (mSigningDetails == SigningDetails.UNKNOWN) {
mSigningDetails = apk.getSigningDetails();
}
+ mHasDeviceAdminReceiver = apk.isHasDeviceAdminReceiver();
assertApkConsistentLocked(String.valueOf(addedFile), apk);
@@ -3610,6 +3512,18 @@
}
}
+ SigningDetails getSigningDetails() {
+ synchronized (mLock) {
+ return mSigningDetails;
+ }
+ }
+
+ PackageLite getPackageLite() {
+ synchronized (mLock) {
+ return mPackageLite;
+ }
+ }
+
private static String getRelativePath(File file, File base) throws IOException {
final String pathStr = file.getAbsolutePath();
final String baseStr = base.getAbsolutePath();
@@ -3816,8 +3730,8 @@
synchronized (mLock) {
assertNotChild("abandonNonStaged");
assertCallerIsOwnerOrRootOrSystem();
- if (mRelinquished) {
- if (LOGD) Slog.d(TAG, "Ignoring abandon after commit relinquished control");
+ if (mStageDirInUse) {
+ if (LOGD) Slog.d(TAG, "Ignoring abandon for staging files are in use");
return;
}
destroyInternal();
@@ -4445,7 +4359,7 @@
pw.printPair("mCommitted", mCommitted);
pw.printPair("mSealed", mSealed);
pw.printPair("mPermissionsManuallyAccepted", mPermissionsManuallyAccepted);
- pw.printPair("mRelinquished", mRelinquished);
+ pw.printPair("mStageDirInUse", mStageDirInUse);
pw.printPair("mDestroyed", mDestroyed);
pw.printPair("mFds", mFds.size());
pw.printPair("mBridges", mBridges.size());
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3d916ae4..8b26419 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -146,9 +146,13 @@
import android.content.pm.parsing.component.ParsedIntentInfo;
import android.content.pm.parsing.component.ParsedMainComponent;
import android.content.pm.parsing.component.ParsedProvider;
-import android.content.pm.pkg.PackageUserState;
-import android.content.pm.pkg.PackageUserStateInternal;
+
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageStateUtils;
+import com.android.server.pm.pkg.PackageUserState;
+import com.android.server.pm.pkg.PackageUserStateInternal;
import android.content.pm.pkg.PackageUserStateUtils;
+import com.android.server.pm.pkg.SuspendParams;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Bitmap;
@@ -303,6 +307,7 @@
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
@@ -806,10 +811,16 @@
@GuardedBy("mProtectedBroadcasts")
final ArraySet<String> mProtectedBroadcasts = new ArraySet<>();
- /** List of packages waiting for verification. */
+ /**
+ * List of packages waiting for verification.
+ * Handler thread only!
+ */
final SparseArray<PackageVerificationState> mPendingVerification = new SparseArray<>();
- /** List of packages waiting for rollback to be enabled. */
+ /**
+ * List of packages waiting for rollback to be enabled.
+ * Handler thread only!
+ */
final SparseArray<VerificationParams> mPendingEnableRollback = new SparseArray<>();
final PackageInstallerService mInstallerService;
@@ -830,10 +841,16 @@
// Cache of users who need badging.
private final SparseBooleanArray mUserNeedsBadging = new SparseBooleanArray();
- /** Token for keys in mPendingVerification. */
+ /**
+ * Token for keys in mPendingVerification.
+ * Handler thread only!
+ */
int mPendingVerificationToken = 0;
- /** Token for keys in mPendingEnableRollback. */
+ /**
+ * Token for keys in mPendingEnableRollback.
+ * Handler thread only!
+ */
int mPendingEnableRollbackToken = 0;
@Watched(manual = true)
@@ -918,6 +935,7 @@
static final int CHECK_PENDING_INTEGRITY_VERIFICATION = 26;
static final int DOMAIN_VERIFICATION = 27;
static final int SNAPSHOT_UNCORK = 28;
+ static final int PRUNE_UNUSED_STATIC_SHARED_LIBRARIES = 29;
static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000;
private static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500;
@@ -927,11 +945,16 @@
private static final long BROADCAST_DELAY_DURING_STARTUP = 10 * 1000L; // 10 seconds (in millis)
private static final long BROADCAST_DELAY = 1 * 1000L; // 1 second (in millis)
+ private static final long PRUNE_UNUSED_STATIC_SHARED_LIBRARIES_DELAY =
+ TimeUnit.MINUTES.toMillis(3); // 3 minutes
+
// When the service constructor finished plus a delay (used for broadcast delay computation)
private long mServiceStartWithDelay;
- private static final long DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD =
- 2 * 60 * 60 * 1000L; /* two hours */
+ private static final long FREE_STORAGE_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD =
+ TimeUnit.HOURS.toMillis(2); /* two hours */
+ static final long DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD =
+ TimeUnit.DAYS.toMillis(7); /* 7 days */
final UserManagerService mUserManager;
@@ -1250,6 +1273,12 @@
mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS);
}
+ void schedulePruneUnusedStaticSharedLibraries(boolean delay) {
+ mHandler.removeMessages(PRUNE_UNUSED_STATIC_SHARED_LIBRARIES);
+ mHandler.sendEmptyMessageDelayed(PRUNE_UNUSED_STATIC_SHARED_LIBRARIES,
+ delay ? PRUNE_UNUSED_STATIC_SHARED_LIBRARIES_DELAY : 0);
+ }
+
@Override
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.TypeMask int optional,
@@ -2076,8 +2105,9 @@
continue;
}
for (int userId : userIds) {
- final PackageSetting ps = getPackageSetting(pkg.getPackageName());
- if (ps == null || !ps.getInstantApp(userId) || !ps.getInstalled(userId)) {
+ final PackageStateInternal ps = getPackageStateInternal(pkg.getPackageName());
+ if (ps == null || !ps.getUserStateOrDefault(userId).isInstantApp()
+ || !ps.getUserStateOrDefault(userId).isInstalled()) {
continue;
}
mInstantAppRegistry.addInstantAppLPw(userId, ps.getAppId());
@@ -2502,7 +2532,8 @@
return mComputer.canViewInstantApps(callingUid, userId);
}
- private PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) {
+ private PackageInfo generatePackageInfo(@NonNull PackageStateInternal ps, int flags,
+ int userId) {
return mComputer.generatePackageInfo(ps, flags, userId);
}
@@ -2564,12 +2595,12 @@
synchronized (mLock) {
AndroidPackage p = mPackages.get(packageName);
if (p != null) {
- final PackageSetting ps = getPackageSetting(p.getPackageName());
+ final PackageStateInternal ps = getPackageStateInternal(p.getPackageName());
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
return false;
}
if (ps != null) {
- final PackageUserState state = ps.readUserState(userId);
+ final PackageUserState state = ps.getUserStateOrDefault(userId);
if (state != null) {
return PackageUserStateUtils.isAvailable(state, 0);
}
@@ -2612,24 +2643,24 @@
* @see #canViewInstantApps(int, int)
*/
@GuardedBy("mLock")
- private boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid,
+ private boolean shouldFilterApplicationLocked(@Nullable PackageStateInternal ps, int callingUid,
@Nullable ComponentName component, @ComponentType int componentType, int userId) {
return mComputer.shouldFilterApplicationLocked(ps, callingUid,
component, componentType, userId);
}
/**
- * @see #shouldFilterApplicationLocked(PackageSetting, int, ComponentName, int, int)
+ * @see #shouldFilterApplicationLocked(PackageStateInternal, int, ComponentName, int, int)
*/
@GuardedBy("mLock")
boolean shouldFilterApplicationLocked(
- @Nullable PackageSetting ps, int callingUid, int userId) {
+ @Nullable PackageStateInternal ps, int callingUid, int userId) {
return mComputer.shouldFilterApplicationLocked(
ps, callingUid, userId);
}
/**
- * @see #shouldFilterApplicationLocked(PackageSetting, int, ComponentName, int, int)
+ * @see #shouldFilterApplicationLocked(PackageStateInternal, int, ComponentName, int, int)
*/
@GuardedBy("mLock")
private boolean shouldFilterApplicationLocked(@NonNull SharedUserSetting sus, int callingUid,
@@ -2638,10 +2669,9 @@
}
@GuardedBy("mLock")
- private boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId,
- int flags) {
- return mComputer.filterSharedLibPackageLPr(ps, uid, userId,
- flags);
+ private boolean filterSharedLibPackageLPr(@Nullable PackageStateInternal ps, int uid,
+ int userId, int flags) {
+ return mComputer.filterSharedLibPackageLPr(ps, uid, userId, flags);
}
@Override
@@ -2726,8 +2756,8 @@
synchronized (mLock) {
final AndroidPackage p = mPackages.get(packageName);
if (p != null && AndroidPackageUtils.isMatchForSystemOnly(p, flags)) {
- final PackageSetting ps = getPackageSetting(p.getPackageName());
- if (ps != null && ps.getInstalled(userId)
+ final PackageStateInternal ps = getPackageStateInternal(p.getPackageName());
+ if (ps != null && ps.getUserStateOrDefault(userId).isInstalled()
&& !shouldFilterApplicationLocked(ps, callingUid, userId)) {
return mPermissionManager.getGidsForUid(UserHandle.getUid(userId,
ps.getAppId()));
@@ -2735,7 +2765,7 @@
}
if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps != null && ps.isMatch(flags)
+ if (ps != null && PackageStateUtils.isMatch(ps, flags)
&& !shouldFilterApplicationLocked(ps, callingUid, userId)) {
return mPermissionManager.getGidsForUid(
UserHandle.getUid(userId, ps.getAppId()));
@@ -2876,7 +2906,7 @@
if (internalVolume && pruneUnusedStaticSharedLibraries(bytes,
android.provider.Settings.Global.getLong(mContext.getContentResolver(),
Global.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD,
- DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD))) {
+ FREE_STORAGE_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD))) {
return;
}
@@ -2931,7 +2961,7 @@
throw new IOException("Failed to free " + bytes + " on storage device at " + file);
}
- private boolean pruneUnusedStaticSharedLibraries(long neededSpace, long maxCachePeriod)
+ boolean pruneUnusedStaticSharedLibraries(long neededSpace, long maxCachePeriod)
throws IOException {
final StorageManager storage = mInjector.getSystemService(StorageManager.class);
final File volume = storage.findPathForUuid(StorageManager.UUID_PRIVATE_INTERNAL);
@@ -3048,7 +3078,7 @@
return -1;
}
- final PackageSetting ps = getPackageSetting(pkg.getPackageName());
+ final PackageStateInternal ps = getPackageStateInternal(pkg.getPackageName());
if (shouldFilterApplicationLocked(ps, Binder.getCallingUid(),
UserHandle.getCallingUserId())) {
return -1;
@@ -3326,7 +3356,7 @@
dependentPackageName = ps.getPkg().getManifestPackageName();
}
versionedPackages.add(new VersionedPackage(dependentPackageName,
- ps.getLongVersionCode()));
+ ps.getVersionCode()));
} else if (ps.getPkg() != null) {
if (ArrayUtils.contains(ps.getPkg().getUsesLibraries(), libName)
|| ArrayUtils.contains(ps.getPkg().getUsesOptionalLibraries(), libName)) {
@@ -3337,7 +3367,7 @@
versionedPackages = new ArrayList<>();
}
versionedPackages.add(new VersionedPackage(ps.getPackageName(),
- ps.getLongVersionCode()));
+ ps.getVersionCode()));
}
}
}
@@ -3566,7 +3596,8 @@
public String getPermissionControllerPackageName() {
synchronized (mLock) {
if (mRequiredPermissionControllerPackage != null) {
- final PackageSetting ps = getPackageSetting(mRequiredPermissionControllerPackage);
+ final PackageStateInternal ps =
+ getPackageStateInternal(mRequiredPermissionControllerPackage);
if (ps != null) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
@@ -3640,8 +3671,10 @@
synchronized (mLock) {
final AndroidPackage p1 = mPackages.get(pkg1);
final AndroidPackage p2 = mPackages.get(pkg2);
- final PackageSetting ps1 = p1 == null ? null : getPackageSetting(p1.getPackageName());
- final PackageSetting ps2 = p2 == null ? null : getPackageSetting(p2.getPackageName());
+ final PackageStateInternal ps1 =
+ p1 == null ? null : getPackageStateInternal(p1.getPackageName());
+ final PackageStateInternal ps2 =
+ p2 == null ? null : getPackageStateInternal(p2.getPackageName());
if (p1 == null || ps1 == null || p2 == null || ps2 == null) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
@@ -3750,7 +3783,7 @@
if (p == null) {
return false;
}
- final PackageSetting ps = getPackageSetting(p.getPackageName());
+ final PackageStateInternal ps = getPackageStateInternal(p.getPackageName());
if (ps == null) {
return false;
}
@@ -3837,9 +3870,9 @@
} else {
// caller is a normal application; filter instant applications
for (AndroidPackage pkg : mPackages.values()) {
- final PackageSetting ps = getPackageSetting(pkg.getPackageName());
+ final PackageStateInternal ps = getPackageStateInternal(pkg.getPackageName());
if (ps != null
- && ps.getInstantApp(callingUserId)
+ && ps.getUserStateOrDefault(callingUserId).isInstantApp()
&& !mInstantAppRegistry.isInstantAccessGranted(callingUserId,
UserHandle.getAppId(callingUid), ps.getAppId())) {
continue;
@@ -4308,7 +4341,7 @@
return mComputer.getInstalledPackages(flags, userId);
}
- private void addPackageHoldingPermissions(ArrayList<PackageInfo> list, PackageSetting ps,
+ private void addPackageHoldingPermissions(ArrayList<PackageInfo> list, PackageStateInternal ps,
String[] permissions, boolean[] tmp, int flags, int userId) {
int numMatch = 0;
for (int i=0; i<permissions.length; i++) {
@@ -4366,7 +4399,7 @@
}
} else {
for (AndroidPackage pkg : mPackages.values()) {
- PackageSetting ps = getPackageSetting(pkg.getPackageName());
+ PackageStateInternal ps = getPackageStateInternal(pkg.getPackageName());
if (ps != null) {
addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags,
userId);
@@ -4437,7 +4470,7 @@
} else {
list = new ArrayList<>(mPackages.size());
for (AndroidPackage p : mPackages.values()) {
- final PackageSetting ps = getPackageSetting(p.getPackageName());
+ final PackageStateInternal ps = getPackageStateInternal(p.getPackageName());
if (ps != null) {
if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
continue;
@@ -4446,7 +4479,7 @@
continue;
}
ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(p, flags,
- ps.readUserState(userId), userId, ps);
+ ps.getUserStateOrDefault(userId), userId, ps);
if (ai != null) {
ai.packageName = resolveExternalPackageNameLPr(p);
list.add(ai);
@@ -4751,7 +4784,7 @@
|| targetPackage.equals(p.getTargetPackage())) {
String packageName = p.getPackageName();
AndroidPackage pkg = mPackages.get(packageName);
- PackageSetting pkgSetting = getPackageSetting(packageName);
+ PackageStateInternal pkgSetting = getPackageStateInternal(packageName);
if (pkg != null) {
InstrumentationInfo ii = PackageInfoUtils.generateInstrumentationInfo(p,
pkg, flags, userId, pkgSetting);
@@ -5210,7 +5243,7 @@
continue;
}
final PackageSetting staticLibPkgSetting =
- getPackageSetting(sharedLibraryInfo.getPackageName());
+ getPackageSettingForMutation(sharedLibraryInfo.getPackageName());
if (staticLibPkgSetting == null) {
Slog.wtf(TAG, "Shared lib without setting: " + sharedLibraryInfo);
continue;
@@ -5347,7 +5380,7 @@
Objects.requireNonNull(propertyName);
Objects.requireNonNull(packageName);
synchronized (mLock) {
- final PackageSetting ps = getPackageSetting(packageName);
+ final PackageStateInternal ps = getPackageStateInternal(packageName);
if (shouldFilterApplicationLocked(ps, Binder.getCallingUid(),
UserHandle.getCallingUserId())) {
return null;
@@ -5364,7 +5397,7 @@
final int callingUserId = UserHandle.getCallingUserId();
final List<Property> result =
mPackageProperty.queryProperty(propertyName, componentType, packageName -> {
- final PackageSetting ps = getPackageSetting(packageName);
+ final PackageStateInternal ps = getPackageStateInternal(packageName);
return shouldFilterApplicationLocked(ps, callingUid, callingUserId);
});
if (result == null) {
@@ -5516,7 +5549,7 @@
return;
}
SparseArray<int[]> broadcastAllowList = mAppsFilter.getVisibilityAllowList(
- getPackageSettingInternal(packageName, Process.SYSTEM_UID),
+ getPackageStateInternal(packageName, Process.SYSTEM_UID),
userIds, mSettings.getPackagesLocked());
mHandler.post(() -> mBroadcastHelper.sendPackageAddedForNewUsers(
packageName, appId, userIds, instantUserIds, dataLoaderType, broadcastAllowList));
@@ -5707,7 +5740,7 @@
final String pkgName = pkgList[i];
final int uid = uidList[i];
SparseArray<int[]> allowList = mAppsFilter.getVisibilityAllowList(
- getPackageSettingInternal(pkgName, Process.SYSTEM_UID),
+ getPackageStateInternal(pkgName, Process.SYSTEM_UID),
userIds, mSettings.getPackagesLocked());
if (allowList == null) {
allowList = new SparseArray<>(0);
@@ -6015,7 +6048,7 @@
final Bundle allExtras = new Bundle();
if (pus.isSuspended()) {
for (int i = 0; i < pus.getSuspendParams().size(); i++) {
- final PackageUserState.SuspendParams params = pus.getSuspendParams().valueAt(i);
+ final SuspendParams params = pus.getSuspendParams().valueAt(i);
if (params != null && params.appExtras != null) {
allExtras.putAll(params.appExtras);
}
@@ -6326,25 +6359,28 @@
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
"Only package verification agents can extend verification timeouts");
- final PackageVerificationState state = mPendingVerification.get(id);
- final PackageVerificationResponse response = new PackageVerificationResponse(
- verificationCodeAtTimeout, Binder.getCallingUid());
+ mHandler.post(() -> {
+ final PackageVerificationState state = mPendingVerification.get(id);
+ final PackageVerificationResponse response = new PackageVerificationResponse(
+ verificationCodeAtTimeout, Binder.getCallingUid());
- if (millisecondsToDelay > PackageManager.MAXIMUM_VERIFICATION_TIMEOUT) {
- millisecondsToDelay = PackageManager.MAXIMUM_VERIFICATION_TIMEOUT;
- }
- if (millisecondsToDelay < 0) {
- millisecondsToDelay = 0;
- }
+ long delay = millisecondsToDelay;
+ if (delay > PackageManager.MAXIMUM_VERIFICATION_TIMEOUT) {
+ delay = PackageManager.MAXIMUM_VERIFICATION_TIMEOUT;
+ }
+ if (delay < 0) {
+ delay = 0;
+ }
- if ((state != null) && !state.timeoutExtended()) {
- state.extendTimeout();
+ if ((state != null) && !state.timeoutExtended()) {
+ state.extendTimeout();
- final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED);
- msg.arg1 = id;
- msg.obj = response;
- mHandler.sendMessageDelayed(msg, millisecondsToDelay);
- }
+ final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED);
+ msg.arg1 = id;
+ msg.obj = response;
+ mHandler.sendMessageDelayed(msg, delay);
+ }
+ });
}
private void setEnableRollbackCode(int token, int enableRollbackCode) {
@@ -6406,7 +6442,7 @@
if (pkg == null || ArrayUtils.isEmpty(pkg.getActivities())) {
return ParceledListSlice.emptyList();
}
- final PackageSetting ps = getPackageSetting(pkg.getPackageName());
+ final PackageStateInternal ps = getPackageStateInternal(pkg.getPackageName());
if (ps == null) {
return ParceledListSlice.emptyList();
}
@@ -6994,11 +7030,12 @@
// Queue up an async operation since the package deletion may take a little while.
mHandler.post(() -> {
- final PackageSetting ps = pkg == null ? null : getPackageSetting(pkg.getPackageName());
+ final PackageStateInternal ps =
+ pkg == null ? null : getPackageStateInternal(pkg.getPackageName());
boolean doClearData = true;
if (ps != null) {
final boolean targetIsInstantApp =
- ps.getInstantApp(UserHandle.getUserId(callingUid));
+ ps.getUserStateOrDefault(UserHandle.getUserId(callingUid)).isInstantApp();
doClearData = !targetIsInstantApp
|| hasAccessInstantApps == PackageManager.PERMISSION_GRANTED;
}
@@ -7583,7 +7620,7 @@
synchronized (mLock) {
AndroidPackage pkg = mPackages.get(componentPkgName);
- PackageSetting pkgSetting = getPackageSetting(componentPkgName);
+ PackageSetting pkgSetting = getPackageSettingForMutation(componentPkgName);
if (pkg == null || pkgSetting == null
|| (!pkg.isSystem() && !pkgSetting.getPkgState().isUpdatedSystemApp())) {
throw new SecurityException(
@@ -8010,7 +8047,7 @@
}
final SparseArray<int[]> broadcastAllowList;
synchronized (mLock) {
- PackageSetting setting = getPackageSettingInternal(packageName, Process.SYSTEM_UID);
+ PackageStateInternal setting = getPackageStateInternal(packageName, Process.SYSTEM_UID);
if (setting == null) {
return null;
}
@@ -8432,6 +8469,9 @@
});
mBackgroundDexOptService.systemReady();
+
+ // Prune unused static shared libraries which have been cached a period of time
+ schedulePruneUnusedStaticSharedLibraries(false /* delay */);
}
/**
@@ -8765,7 +8805,7 @@
synchronized (mLock) {
final AndroidPackage pkg = mPackages.get(packageName);
if (pkg == null
- || shouldFilterApplicationLocked(getPackageSetting(pkg.getPackageName()),
+ || shouldFilterApplicationLocked(getPackageStateInternal(pkg.getPackageName()),
Binder.getCallingUid(), UserHandle.getCallingUserId())) {
Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
throw new IllegalArgumentException("Unknown package: " + packageName);
@@ -8785,7 +8825,7 @@
final int callingUserId = UserHandle.getUserId(callingUid);
final AndroidPackage pkg = mPackages.get(packageName);
if (pkg == null
- || shouldFilterApplicationLocked(getPackageSetting(pkg.getPackageName()),
+ || shouldFilterApplicationLocked(getPackageStateInternal(pkg.getPackageName()),
callingUid, callingUserId)) {
Slog.w(TAG, "KeySet requested for unknown package: " + packageName
+ ", uid:" + callingUid);
@@ -8812,7 +8852,7 @@
synchronized (mLock) {
final AndroidPackage pkg = mPackages.get(packageName);
if (pkg == null
- || shouldFilterApplicationLocked(getPackageSetting(pkg.getPackageName()),
+ || shouldFilterApplicationLocked(getPackageStateInternal(pkg.getPackageName()),
callingUid, UserHandle.getUserId(callingUid))) {
Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
throw new IllegalArgumentException("Unknown package: " + packageName);
@@ -8838,7 +8878,7 @@
synchronized (mLock) {
final AndroidPackage pkg = mPackages.get(packageName);
if (pkg == null
- || shouldFilterApplicationLocked(getPackageSetting(pkg.getPackageName()),
+ || shouldFilterApplicationLocked(getPackageStateInternal(pkg.getPackageName()),
callingUid, UserHandle.getUserId(callingUid))) {
Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
throw new IllegalArgumentException("Unknown package: " + packageName);
@@ -8898,7 +8938,8 @@
private int[] getVisibilityAllowList(@NonNull String packageName, int userId) {
synchronized (mLock) {
- final PackageSetting ps = getPackageSettingInternal(packageName, Process.SYSTEM_UID);
+ final PackageStateInternal ps =
+ getPackageStateInternal(packageName, Process.SYSTEM_UID);
if (ps == null) {
return null;
}
@@ -9092,8 +9133,8 @@
@Nullable
@Override
- public PackageSetting getPackageSetting(String packageName) {
- return PackageManagerService.this.getPackageSetting(packageName);
+ public PackageStateInternal getPackageStateInternal(String packageName) {
+ return PackageManagerService.this.getPackageStateInternal(packageName);
}
@Nullable
@@ -9191,8 +9232,7 @@
final PackageUserStateInternal pus = ps.readUserState(userId);
if (pus.isSuspended()) {
for (int i = 0; i < pus.getSuspendParams().size(); i++) {
- final PackageUserState.SuspendParams params =
- pus.getSuspendParams().valueAt(i);
+ final SuspendParams params = pus.getSuspendParams().valueAt(i);
if (params != null && params.launcherExtras != null) {
allExtras.putAll(params.launcherExtras);
}
@@ -9278,8 +9318,8 @@
if (ps != null) {
final PackageUserStateInternal pus = ps.readUserState(userId);
if (pus.isSuspended()) {
- final PackageUserState.SuspendParams suspendParams =
- pus.getSuspendParams().get(suspendingPackage);
+ final SuspendParams suspendParams = pus.getSuspendParams()
+ .get(suspendingPackage);
return (suspendParams != null) ? suspendParams.dialogInfo : null;
}
}
@@ -9528,8 +9568,8 @@
synchronized (mLock) {
for (AndroidPackage p : mPackages.values()) {
if (p.getOverlayTarget() != null) {
- PackageInfo pkg = generatePackageInfo(getPackageSetting(p.getPackageName()),
- 0, userId);
+ PackageInfo pkg = generatePackageInfo(
+ getPackageStateInternal(p.getPackageName()), 0, userId);
if (pkg != null) {
overlayPackages.add(pkg);
}
@@ -9910,7 +9950,8 @@
if (pkg == null) {
return false;
}
- final PackageSetting packageSetting = getPackageSetting(pkg.getPackageName());
+ final PackageStateInternal packageSetting =
+ getPackageStateInternal(pkg.getPackageName());
if (packageSetting == null) {
return false;
}
@@ -10035,51 +10076,51 @@
@Override
public void withPackageSettingsSnapshot(
- @NonNull Consumer<Function<String, PackageSetting>> block) {
+ @NonNull Consumer<Function<String, PackageStateInternal>> block) {
final Computer snapshot = snapshotComputer();
// This method needs to either lock or not lock consistently throughout the method,
// so if the live computer is returned, force a wrapping sync block.
if (snapshot == mLiveComputer) {
synchronized (mLock) {
- block.accept(snapshot::getPackageSetting);
+ block.accept(snapshot::getPackageStateInternal);
}
} else {
- block.accept(snapshot::getPackageSetting);
+ block.accept(snapshot::getPackageStateInternal);
}
}
@Override
public <Output> Output withPackageSettingsSnapshotReturning(
- @NonNull FunctionalUtils.ThrowingFunction<Function<String, PackageSetting>, Output>
- block) {
+ @NonNull FunctionalUtils.ThrowingFunction<Function<String, PackageStateInternal>,
+ Output> block) {
final Computer snapshot = snapshotComputer();
// This method needs to either lock or not lock consistently throughout the method,
// so if the live computer is returned, force a wrapping sync block.
if (snapshot == mLiveComputer) {
synchronized (mLock) {
- return block.apply(snapshot::getPackageSetting);
+ return block.apply(snapshot::getPackageStateInternal);
}
} else {
- return block.apply(snapshot::getPackageSetting);
+ return block.apply(snapshot::getPackageStateInternal);
}
}
@Override
public <ExceptionType extends Exception> void withPackageSettingsSnapshotThrowing(
- @NonNull FunctionalUtils.ThrowingCheckedConsumer<Function<String, PackageSetting>,
- ExceptionType> block) throws ExceptionType {
+ @NonNull FunctionalUtils.ThrowingCheckedConsumer<Function<String,
+ PackageStateInternal>, ExceptionType> block) throws ExceptionType {
final Computer snapshot = snapshotComputer();
// This method needs to either lock or not lock consistently throughout the method,
// so if the live computer is returned, force a wrapping sync block.
if (snapshot == mLiveComputer) {
synchronized (mLock) {
- block.accept(snapshot::getPackageSetting);
+ block.accept(snapshot::getPackageStateInternal);
}
} else {
- block.accept(snapshot::getPackageSetting);
+ block.accept(snapshot::getPackageStateInternal);
}
}
@@ -10087,7 +10128,8 @@
public <ExceptionOne extends Exception, ExceptionTwo extends Exception> void
withPackageSettingsSnapshotThrowing2(
@NonNull FunctionalUtils.ThrowingChecked2Consumer<
- Function<String, PackageSetting>, ExceptionOne, ExceptionTwo> block)
+ Function<String, PackageStateInternal>, ExceptionOne,
+ ExceptionTwo> block)
throws ExceptionOne, ExceptionTwo {
final Computer snapshot = snapshotComputer();
@@ -10095,10 +10137,10 @@
// so if the live computer is returned, force a wrapping sync block.
if (snapshot == mLiveComputer) {
synchronized (mLock) {
- block.accept(snapshot::getPackageSetting);
+ block.accept(snapshot::getPackageStateInternal);
}
} else {
- block.accept(snapshot::getPackageSetting);
+ block.accept(snapshot::getPackageStateInternal);
}
}
@@ -10106,7 +10148,8 @@
public <Output, ExceptionType extends Exception> Output
withPackageSettingsSnapshotReturningThrowing(
@NonNull FunctionalUtils.ThrowingCheckedFunction<
- Function<String, PackageSetting>, Output, ExceptionType> block)
+ Function<String, PackageStateInternal>, Output,
+ ExceptionType> block)
throws ExceptionType {
final Computer snapshot = snapshotComputer();
@@ -10114,10 +10157,10 @@
// so if the live computer is returned, force a wrapping sync block.
if (snapshot == mLiveComputer) {
synchronized (mLock) {
- return block.apply(snapshot::getPackageSetting);
+ return block.apply(snapshot::getPackageStateInternal);
}
} else {
- return block.apply(snapshot::getPackageSetting);
+ return block.apply(snapshot::getPackageStateInternal);
}
}
@@ -10210,19 +10253,27 @@
}
}
+ // TODO: Remove
+ @Deprecated
@Nullable
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- PackageSetting getPackageSetting(String packageName) {
- return mComputer.getPackageSetting(packageName);
+ PackageSetting getPackageSettingForMutation(String packageName) {
+ return (PackageSetting) mComputer.getPackageStateInternal(packageName);
}
- PackageSetting getPackageSettingInternal(String packageName, int callingUid) {
- return mComputer.getPackageSettingInternal(packageName, callingUid);
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ @Nullable
+ PackageStateInternal getPackageStateInternal(String packageName) {
+ return mComputer.getPackageStateInternal(packageName);
+ }
+
+ @Nullable
+ PackageStateInternal getPackageStateInternal(String packageName, int callingUid) {
+ return mComputer.getPackageStateInternal(packageName, callingUid);
}
@Nullable
private PackageState getPackageState(String packageName) {
- return mComputer.getPackageState(packageName);
+ return mComputer.getPackageStateCopied(packageName);
}
void forEachPackage(Consumer<AndroidPackage> actionLocked) {
@@ -10815,8 +10866,8 @@
enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
false /*checkShell*/, "may package query");
synchronized (mLock) {
- final PackageSetting sourceSetting = getPackageSetting(sourcePackageName);
- final PackageSetting targetSetting = getPackageSetting(targetPackageName);
+ final PackageStateInternal sourceSetting = getPackageStateInternal(sourcePackageName);
+ final PackageStateInternal targetSetting = getPackageStateInternal(targetPackageName);
if (sourceSetting == null || targetSetting == null) {
throw new ParcelableException(new PackageManager.NameNotFoundException("Package(s) "
+ (sourceSetting == null ? sourcePackageName + " " : "")
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index e124e04..9c41ccb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -84,6 +84,7 @@
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import dalvik.system.VMRuntime;
@@ -1037,9 +1038,9 @@
* @return if the package is approved at any non-zero level for the domain in the intent
*/
public static boolean hasAnyDomainApproval(
- @NonNull DomainVerificationManagerInternal manager, @NonNull PackageSetting pkgSetting,
- @NonNull Intent intent, @PackageManager.ResolveInfoFlags int resolveInfoFlags,
- @UserIdInt int userId) {
+ @NonNull DomainVerificationManagerInternal manager,
+ @NonNull PackageStateInternal pkgSetting, @NonNull Intent intent,
+ @PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId) {
return manager.approvalLevelForDomain(pkgSetting, intent, resolveInfoFlags, userId)
> DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE;
}
diff --git a/services/core/java/com/android/server/pm/PackageSessionVerifier.java b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
index 3f5410d..866712c 100644
--- a/services/core/java/com/android/server/pm/PackageSessionVerifier.java
+++ b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
@@ -21,6 +21,8 @@
import android.apex.ApexSessionInfo;
import android.apex.ApexSessionParams;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.IPackageInstallObserver2;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageManager;
@@ -31,13 +33,16 @@
import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.IntArray;
import android.util.Slog;
import android.util.apk.ApkSignatureVerifier;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageHelper;
import com.android.server.LocalServices;
import com.android.server.pm.parsing.PackageParser2;
@@ -76,6 +81,79 @@
mHandler = new Handler(looper);
}
+ @VisibleForTesting
+ PackageSessionVerifier() {
+ mContext = null;
+ mPm = null;
+ mApexManager = null;
+ mPackageParserSupplier = null;
+ mHandler = null;
+ }
+
+ /**
+ * Runs verifications that are common to both staged and non-staged sessions.
+ */
+ public void verifyNonStaged(PackageInstallerSession session, Callback callback) {
+ mHandler.post(() -> {
+ try {
+ storeSession(session.mStagedSession);
+ if (session.isMultiPackage()) {
+ for (PackageInstallerSession child : session.getChildSessions()) {
+ checkRebootlessApex(child);
+ }
+ } else {
+ checkRebootlessApex(session);
+ }
+ verifyAPK(session, callback);
+ } catch (PackageManagerException e) {
+ callback.onResult(e.error, e.getMessage());
+ }
+ });
+ }
+
+ /**
+ * Runs verifications particular to APK. This includes APEX sessions since an APEX can also
+ * be treated as APK.
+ */
+ private void verifyAPK(PackageInstallerSession session, Callback callback)
+ throws PackageManagerException {
+ final IPackageInstallObserver2 observer = new IPackageInstallObserver2.Stub() {
+ @Override
+ public void onUserActionRequired(Intent intent) {
+ throw new IllegalStateException();
+ }
+ @Override
+ public void onPackageInstalled(String basePackageName, int returnCode, String msg,
+ Bundle extras) {
+ callback.onResult(returnCode, msg);
+ }
+ };
+ final VerificationParams verifyingSession = makeVerificationParams(session, observer);
+ if (session.isMultiPackage()) {
+ final List<PackageInstallerSession> childSessions = session.getChildSessions();
+ List<VerificationParams> verifyingChildSessions = new ArrayList<>(childSessions.size());
+ for (PackageInstallerSession child : childSessions) {
+ verifyingChildSessions.add(makeVerificationParams(child, null));
+ }
+ verifyingSession.verifyStage(verifyingChildSessions);
+ } else {
+ verifyingSession.verifyStage();
+ }
+ }
+
+ private VerificationParams makeVerificationParams(
+ PackageInstallerSession session, IPackageInstallObserver2 observer) {
+ final UserHandle user;
+ if ((session.params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
+ user = UserHandle.ALL;
+ } else {
+ user = new UserHandle(session.userId);
+ }
+ return new VerificationParams(user, session.stageDir, observer, session.params,
+ session.getInstallSource(), session.getInstallerUid(), session.getSigningDetails(),
+ session.sessionId, session.getPackageLite(), mPm);
+ }
+
/**
* Starts pre-reboot verification for the staged-session. This operation is broken into the
* following phases:
@@ -94,7 +172,6 @@
Slog.d(TAG, "Starting preRebootVerification for session " + session.sessionId());
mHandler.post(() -> {
try {
- storeSession(session);
checkActiveSessions();
checkRollbacks(session);
if (session.isMultiPackage()) {
@@ -114,8 +191,11 @@
/**
* Stores staged-sessions for checking package overlapping and rollback conflicts.
*/
- private void storeSession(StagingManager.StagedSession session) {
- mStagedSessions.add(session);
+ @VisibleForTesting
+ void storeSession(StagingManager.StagedSession session) {
+ if (session != null) {
+ mStagedSessions.add(session);
+ }
}
private void onVerificationSuccess(StagingManager.StagedSession session, Callback callback) {
@@ -382,17 +462,49 @@
}
/**
+ * Fails this rebootless APEX session if the same package name found in any staged sessions.
+ */
+ @VisibleForTesting
+ void checkRebootlessApex(PackageInstallerSession session)
+ throws PackageManagerException {
+ if (session.isStaged() || !session.isApexSession()) {
+ return;
+ }
+ String packageName = session.getPackageName();
+ if (packageName == null) {
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
+ "Invalid session " + session.sessionId + " with package name null");
+ }
+ for (StagingManager.StagedSession stagedSession : mStagedSessions) {
+ if (stagedSession.isDestroyed() || stagedSession.isInTerminalState()) {
+ continue;
+ }
+ if (stagedSession.sessionContains(s -> packageName.equals(s.getPackageName()))) {
+ // Staged-sessions take priority over rebootless APEX
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
+ "Staged session " + stagedSession.sessionId() + " already contains "
+ + packageName);
+ }
+ }
+ }
+
+ /**
* Checks if multiple staged-sessions are supported. It is supported only when the system
* supports checkpoint.
*/
private void checkActiveSessions() throws PackageManagerException {
- final boolean supportsCheckpoint;
try {
- supportsCheckpoint = PackageHelper.getStorageManager().supportsCheckpoint();
+ checkActiveSessions(PackageHelper.getStorageManager().supportsCheckpoint());
} catch (RemoteException e) {
throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
"Can't query fs-checkpoint status : " + e);
}
+ }
+
+ @VisibleForTesting
+ void checkActiveSessions(boolean supportsCheckpoint) throws PackageManagerException {
int activeSessions = 0;
for (StagingManager.StagedSession stagedSession : mStagedSessions) {
if (stagedSession.isDestroyed() || stagedSession.isInTerminalState()) {
@@ -412,7 +524,8 @@
* downgrade of SDK extension which in turn will result in dependency violation of other
* non-rollback sessions.
*/
- private void checkRollbacks(StagingManager.StagedSession session)
+ @VisibleForTesting
+ void checkRollbacks(StagingManager.StagedSession session)
throws PackageManagerException {
for (StagingManager.StagedSession stagedSession : mStagedSessions) {
if (stagedSession.isDestroyed() || stagedSession.isInTerminalState()) {
@@ -449,7 +562,8 @@
* @param child The child session whose package name will be checked.
* This will equal to {@code parent} for a single-package session.
*/
- private void checkOverlaps(StagingManager.StagedSession parent,
+ @VisibleForTesting
+ void checkOverlaps(StagingManager.StagedSession parent,
StagingManager.StagedSession child) throws PackageManagerException {
final String packageName = child.getPackageName();
if (packageName == null) {
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index b8e354e..64fb630 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -32,8 +32,12 @@
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
import android.content.pm.overlay.OverlayPaths;
-import android.content.pm.pkg.PackageUserState;
-import android.content.pm.pkg.PackageUserStateInternal;
+
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageUserState;
+import com.android.server.pm.pkg.PackageUserStateImpl;
+import com.android.server.pm.pkg.PackageUserStateInternal;
+import com.android.server.pm.pkg.SuspendParams;
import android.os.PersistableBundle;
import android.service.pm.PackageProto;
import android.util.ArrayMap;
@@ -50,7 +54,6 @@
import com.android.server.pm.pkg.AndroidPackageApi;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateUnserialized;
-import com.android.server.pm.pkg.PackageUserStateInternalImpl;
import com.android.server.utils.SnapshotCache;
import libcore.util.EmptyArray;
@@ -73,7 +76,7 @@
*/
@DataClass(genGetters = true, genConstructor = false, genSetters = false, genBuilder = false)
@DataClass.Suppress({"getSnapshot", })
-public class PackageSetting extends SettingBase implements PackageState {
+public class PackageSetting extends SettingBase implements PackageStateInternal {
/**
* Temporary holding space for the shared user ID. While parsing package settings, the
@@ -156,11 +159,6 @@
@Nullable
private String mSecondaryCpuAbi;
- /**
- * The install time CPU override, if any. This value is written at install time
- * and doesn't change during the life of an install. If non-null,
- * {@code primaryCpuAbiString} will contain the same value.
- */
@Nullable
private String mCpuAbiOverride;
@@ -178,10 +176,8 @@
private PackageKeySetData keySetData = new PackageKeySetData();
// TODO: Access is not locked.
- // Whether this package is currently stopped, thus can not be
- // started until explicitly launched by the user.
@NonNull
- private final SparseArray<PackageUserStateInternalImpl> mUserState = new SparseArray<>();
+ private final SparseArray<PackageUserStateImpl> mUserStates = new SparseArray<>();
@NonNull
private InstallSource installSource;
@@ -338,13 +334,6 @@
return mimeGroups != null ? mimeGroups.get(mimeGroup) : null;
}
- public boolean isMatch(int flags) {
- if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
- return isSystem();
- }
- return true;
- }
-
public boolean isSharedUser() {
return sharedUser != null;
}
@@ -644,9 +633,9 @@
? Arrays.copyOf(other.usesStaticLibrariesVersions,
other.usesStaticLibrariesVersions.length) : null;
- mUserState.clear();
- for (int i = 0; i < other.mUserState.size(); i++) {
- mUserState.put(other.mUserState.keyAt(i), other.mUserState.valueAt(i));
+ mUserStates.clear();
+ for (int i = 0; i < other.mUserStates.size(); i++) {
+ mUserStates.put(other.mUserStates.keyAt(i), other.mUserStates.valueAt(i));
}
if (mOldCodePaths != null) {
@@ -664,11 +653,11 @@
}
@VisibleForTesting
- PackageUserStateInternalImpl modifyUserState(int userId) {
- PackageUserStateInternalImpl state = mUserState.get(userId);
+ PackageUserStateImpl modifyUserState(int userId) {
+ PackageUserStateImpl state = mUserStates.get(userId);
if (state == null) {
- state = new PackageUserStateInternalImpl();
- mUserState.put(userId, state);
+ state = new PackageUserStateImpl();
+ mUserStates.put(userId, state);
onChanged();
}
return state;
@@ -676,18 +665,13 @@
@NonNull
public PackageUserStateInternal readUserState(int userId) {
- PackageUserStateInternal state = mUserState.get(userId);
+ PackageUserStateInternal state = mUserStates.get(userId);
if (state == null) {
return PackageUserStateInternal.DEFAULT;
}
return state;
}
- @Nullable
- public PackageUserState readUserStateNullable(int userId) {
- return mUserState.get(userId);
- }
-
void setEnabled(int state, int userId, String callingPackage) {
modifyUserState(userId)
.setEnabledState(state)
@@ -755,12 +739,6 @@
return readUserState(userId).getSharedLibraryOverlayPaths();
}
- /** Only use for testing. Do NOT use in production code. */
- @VisibleForTesting
- SparseArray<PackageUserStateInternalImpl> getUserState() {
- return mUserState;
- }
-
boolean isAnyInstalled(int[] users) {
for (int user: users) {
if (readUserState(user).isInstalled()) {
@@ -845,14 +823,13 @@
boolean addOrUpdateSuspension(String suspendingPackage, SuspendDialogInfo dialogInfo,
PersistableBundle appExtras, PersistableBundle launcherExtras, int userId) {
- final PackageUserStateInternalImpl existingUserState = modifyUserState(userId);
- final PackageUserState.SuspendParams newSuspendParams =
- PackageUserState.SuspendParams.getInstanceOrNull(dialogInfo, appExtras,
- launcherExtras);
+ final PackageUserStateImpl existingUserState = modifyUserState(userId);
+ final SuspendParams newSuspendParams = SuspendParams.getInstanceOrNull(dialogInfo,
+ appExtras, launcherExtras);
if (existingUserState.getSuspendParams() == null) {
existingUserState.setSuspendParams(new ArrayMap<>());
}
- final PackageUserState.SuspendParams oldSuspendParams =
+ final SuspendParams oldSuspendParams =
existingUserState.getSuspendParams().put(suspendingPackage, newSuspendParams);
existingUserState.setSuspended(true);
onChanged();
@@ -861,7 +838,7 @@
boolean removeSuspension(String suspendingPackage, int userId) {
boolean wasModified = false;
- final PackageUserStateInternalImpl existingUserState = modifyUserState(userId);
+ final PackageUserStateImpl existingUserState = modifyUserState(userId);
if (existingUserState.getSuspendParams() != null) {
if (existingUserState.getSuspendParams().remove(suspendingPackage) != null) {
wasModified = true;
@@ -876,7 +853,7 @@
}
void removeSuspension(Predicate<String> suspendingPackagePredicate, int userId) {
- final PackageUserStateInternalImpl existingUserState = modifyUserState(userId);
+ final PackageUserStateImpl existingUserState = modifyUserState(userId);
if (existingUserState.getSuspendParams() != null) {
for (int i = existingUserState.getSuspendParams().size() - 1; i >= 0; i--) {
final String suspendingPackage = existingUserState.getSuspendParams().keyAt(i);
@@ -912,7 +889,7 @@
void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
boolean notLaunched, boolean hidden, int distractionFlags, boolean suspended,
- ArrayMap<String, PackageUserState.SuspendParams> suspendParams, boolean instantApp,
+ ArrayMap<String, SuspendParams> suspendParams, boolean instantApp,
boolean virtualPreload, String lastDisableAppCaller,
ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
int installReason, int uninstallReason,
@@ -947,7 +924,8 @@
otherState.getSuspendParams(),
otherState.isInstantApp(),
otherState.isVirtualPreload(), otherState.getLastDisableAppCaller(),
- otherState.getEnabledComponentsNoCopy(), otherState.getDisabledComponentsNoCopy(),
+ new ArraySet<>(otherState.getEnabledComponentsNoCopy()),
+ new ArraySet<>(otherState.getDisabledComponentsNoCopy()),
otherState.getInstallReason(), otherState.getUninstallReason(),
otherState.getHarmfulAppWarning(), otherState.getSplashScreenTheme());
}
@@ -982,9 +960,9 @@
onChanged();
}
- PackageUserStateInternalImpl modifyUserStateComponents(int userId, boolean disabled,
+ PackageUserStateImpl modifyUserStateComponents(int userId, boolean disabled,
boolean enabled) {
- PackageUserStateInternalImpl state = modifyUserState(userId);
+ PackageUserStateImpl state = modifyUserState(userId);
boolean changed = false;
if (disabled && state.getDisabledComponentsNoCopy() == null) {
state.setDisabledComponents(new ArraySet<String>(1));
@@ -1013,7 +991,7 @@
}
boolean enableComponentLPw(String componentClassName, int userId) {
- PackageUserStateInternalImpl state = modifyUserStateComponents(userId, false, true);
+ PackageUserStateImpl state = modifyUserStateComponents(userId, false, true);
boolean changed = state.getDisabledComponentsNoCopy() != null
? state.getDisabledComponentsNoCopy().remove(componentClassName) : false;
changed |= state.getEnabledComponentsNoCopy().add(componentClassName);
@@ -1024,7 +1002,7 @@
}
boolean disableComponentLPw(String componentClassName, int userId) {
- PackageUserStateInternalImpl state = modifyUserStateComponents(userId, true, false);
+ PackageUserStateImpl state = modifyUserStateComponents(userId, true, false);
boolean changed = state.getEnabledComponentsNoCopy() != null
? state.getEnabledComponentsNoCopy().remove(componentClassName) : false;
changed |= state.getDisabledComponentsNoCopy().add(componentClassName);
@@ -1035,7 +1013,7 @@
}
boolean restoreComponentLPw(String componentClassName, int userId) {
- PackageUserStateInternalImpl state = modifyUserStateComponents(userId, true, true);
+ PackageUserStateImpl state = modifyUserStateComponents(userId, true, true);
boolean changed = state.getDisabledComponentsNoCopy() != null
? state.getDisabledComponentsNoCopy().remove(componentClassName) : false;
changed |= state.getEnabledComponentsNoCopy() != null
@@ -1060,15 +1038,15 @@
}
void removeUser(int userId) {
- mUserState.delete(userId);
+ mUserStates.delete(userId);
onChanged();
}
public int[] getNotInstalledUserIds() {
int count = 0;
- int userStateCount = mUserState.size();
+ int userStateCount = mUserStates.size();
for (int i = 0; i < userStateCount; i++) {
- if (!mUserState.valueAt(i).isInstalled()) {
+ if (!mUserStates.valueAt(i).isInstalled()) {
count++;
}
}
@@ -1079,8 +1057,8 @@
int[] excludedUserIds = new int[count];
int idx = 0;
for (int i = 0; i < userStateCount; i++) {
- if (!mUserState.valueAt(i).isInstalled()) {
- excludedUserIds[idx++] = mUserState.keyAt(i);
+ if (!mUserStates.valueAt(i).isInstalled()) {
+ excludedUserIds[idx++] = mUserStates.keyAt(i);
}
}
return excludedUserIds;
@@ -1110,11 +1088,11 @@
}
protected void writeUsersInfoToProto(ProtoOutputStream proto, long fieldId) {
- int count = mUserState.size();
+ int count = mUserStates.size();
for (int i = 0; i < count; i++) {
final long userToken = proto.start(fieldId);
- final int userId = mUserState.keyAt(i);
- final PackageUserStateInternal state = mUserState.valueAt(i);
+ final int userId = mUserStates.keyAt(i);
+ final PackageUserStateInternal state = mUserStates.valueAt(i);
proto.write(PackageProto.UserInfoProto.ID, userId);
final int installType;
if (state.isInstantApp()) {
@@ -1170,7 +1148,7 @@
}
/**
- * @see PackageUserState#overrideLabelAndIcon(ComponentName, String, Integer)
+ * @see PackageUserStateImpl#overrideLabelAndIcon(ComponentName, String, Integer)
*
* @param userId the specific user to change the label/icon for
*/
@@ -1183,7 +1161,7 @@
}
/**
- * @see PackageUserState#resetOverrideComponentLabelIcon()
+ * @see PackageUserStateImpl#resetOverrideComponentLabelIcon()
*
* @param userId the specific user to reset
*/
@@ -1228,7 +1206,7 @@
@NonNull
@Override
- public long getLongVersionCode() {
+ public long getVersionCode() {
return versionCode;
}
@@ -1291,12 +1269,6 @@
return pkgState.isHiddenUntilInstalled();
}
- @Nullable
- @Override
- public String getSeInfoOverride() {
- return pkgState.getOverrideSeInfo();
- }
-
@NonNull
@Override
public long[] getLastPackageUsageTime() {
@@ -1308,22 +1280,6 @@
return pkgState.isUpdatedSystemApp();
}
- @Deprecated
- @Override
- public int[] getUserIds() {
- int size = mUserState.size();
- int[] array = new int[size];
- for (int index = 0; index < size; index++) {
- array[index] = mUserState.keyAt(index);
- }
- return array;
- }
-
- @Override
- public PackageUserState getUserState(int userId) {
- return readUserStateNullable(userId);
- }
-
public PackageSetting setDomainSetId(@NonNull UUID domainSetId) {
mDomainSetId = domainSetId;
onChanged();
@@ -1349,6 +1305,27 @@
return this;
}
+ @NonNull
+ @Override
+ public PackageStateUnserialized getTransientState() {
+ return pkgState;
+ }
+
+ @NonNull
+ public SparseArray<? extends PackageUserStateInternal> getUserStates() {
+ return mUserStates;
+ }
+
+ @Override
+ public int getPkgFlags() {
+ return pkgFlags;
+ }
+
+ @Override
+ public int getPkgPrivateFlags() {
+ return pkgPrivateFlags;
+ }
+
// Code below generated by codegen v1.0.23.
@@ -1447,11 +1424,6 @@
return mSecondaryCpuAbi;
}
- /**
- * The install time CPU override, if any. This value is written at install time
- * and doesn't change during the life of an install. If non-null,
- * {@code primaryCpuAbiString} will contain the same value.
- */
@DataClass.Generated.Member
public @Nullable String getCpuAbiOverride() {
return mCpuAbiOverride;
@@ -1473,11 +1445,6 @@
}
@DataClass.Generated.Member
- public long getVersionCode() {
- return versionCode;
- }
-
- @DataClass.Generated.Member
public @NonNull PackageSignatures getSignatures() {
return signatures;
}
@@ -1537,10 +1504,10 @@
}
@DataClass.Generated(
- time = 1635295317317L,
+ time = 1635392392447L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java",
- inputSignatures = "protected int sharedUserId\n @android.annotation.Nullable java.util.Map<java.lang.String,android.util.ArraySet<java.lang.String>> mimeGroups\n @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\n @android.annotation.Nullable java.lang.String[] usesStaticLibraries\n @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.Nullable com.android.server.pm.SharedUserSetting sharedUser\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float loadingProgress\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long firstInstallTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateInternalImpl> mUserState\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate boolean updateAvailable\nprivate boolean forceQueryableOverride\nprivate @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic java.util.List<java.lang.String> getMimeGroup(java.lang.String)\nprivate android.util.ArraySet<java.lang.String> getMimeGroupInternal(java.lang.String)\npublic boolean isMatch(int)\npublic boolean isSharedUser()\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,java.util.List<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic int getSharedUserIdInt()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,android.util.ArraySet<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateInternalImpl modifyUserState(int)\npublic @android.annotation.NonNull android.content.pm.pkg.PackageUserStateInternal readUserState(int)\npublic @android.annotation.Nullable android.content.pm.pkg.PackageUserState readUserStateNullable(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n java.lang.String getLastDisabledAppCaller(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getOverlayPathsForLibrary(int)\n @com.android.internal.annotations.VisibleForTesting android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateInternalImpl> getUserState()\n boolean isAnyInstalled(int[])\n int[] queryInstalledUsers(int[],boolean)\n long getCeDataInode(int)\n void setCeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\n boolean getSuspended(int)\n boolean isSuspendedBy(java.lang.String,int)\n boolean addOrUpdateSuspension(java.lang.String,android.content.pm.SuspendDialogInfo,android.os.PersistableBundle,android.os.PersistableBundle,int)\n boolean removeSuspension(java.lang.String,int)\n void removeSuspension(java.util.function.Predicate<java.lang.String>,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,boolean,android.util.ArrayMap<java.lang.String,android.content.pm.pkg.PackageUserState.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String)\n void setUserState(int,android.content.pm.pkg.PackageUserStateInternal)\n android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateInternalImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n void setHarmfulAppWarning(int,java.lang.String)\n java.lang.String getHarmfulAppWarning(int)\n com.android.server.pm.PackageSetting setPath(java.io.File)\n java.lang.String getPathString()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic void setSplashScreenTheme(int,java.lang.String)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isPackageLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getLongVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.Nullable @java.lang.Override java.lang.Integer getSharedUserId()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfoOverride()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Deprecated @java.lang.Override int[] getUserIds()\npublic @java.lang.Override android.content.pm.pkg.PackageUserState getUserState(int)\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setSharedUser(com.android.server.pm.SharedUserSetting)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageState]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
+ inputSignatures = "protected int sharedUserId\n @android.annotation.Nullable java.util.Map<java.lang.String,android.util.ArraySet<java.lang.String>> mimeGroups\n @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\n @android.annotation.Nullable java.lang.String[] usesStaticLibraries\n @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.Nullable com.android.server.pm.SharedUserSetting sharedUser\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long firstInstallTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate boolean updateAvailable\nprivate boolean forceQueryableOverride\nprivate @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic java.util.List<java.lang.String> getMimeGroup(java.lang.String)\nprivate android.util.ArraySet<java.lang.String> getMimeGroupInternal(java.lang.String)\npublic boolean isSharedUser()\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,java.util.List<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic int getSharedUserIdInt()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,android.util.ArraySet<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n java.lang.String getLastDisabledAppCaller(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getOverlayPathsForLibrary(int)\n boolean isAnyInstalled(int[])\n int[] queryInstalledUsers(int[],boolean)\n long getCeDataInode(int)\n void setCeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\n boolean getSuspended(int)\n boolean isSuspendedBy(java.lang.String,int)\n boolean addOrUpdateSuspension(java.lang.String,android.content.pm.SuspendDialogInfo,android.os.PersistableBundle,android.os.PersistableBundle,int)\n boolean removeSuspension(java.lang.String,int)\n void removeSuspension(java.util.function.Predicate<java.lang.String>,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,boolean,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n void setHarmfulAppWarning(int,java.lang.String)\n java.lang.String getHarmfulAppWarning(int)\n com.android.server.pm.PackageSetting setPath(java.io.File)\n java.lang.String getPathString()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic void setSplashScreenTheme(int,java.lang.String)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isPackageLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.Nullable @java.lang.Override java.lang.Integer getSharedUserId()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setSharedUser(com.android.server.pm.SharedUserSetting)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic @java.lang.Override int getPkgFlags()\npublic @java.lang.Override int getPkgPrivateFlags()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index bb3ffb4..1c0fe2a 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -45,6 +45,7 @@
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.utils.WatchedLongSparseArray;
import java.io.File;
@@ -118,7 +119,7 @@
public void removePackageLI(AndroidPackage pkg, boolean chatty) {
// Remove the parent package setting
- PackageSetting ps = mPm.getPackageSetting(pkg.getPackageName());
+ PackageStateInternal ps = mPm.getPackageStateInternal(pkg.getPackageName());
if (ps != null) {
removePackageLI(ps.getPackageName(), chatty);
} else if (DEBUG_REMOVE && chatty) {
@@ -295,7 +296,7 @@
synchronized (mPm.mLock) {
mPm.mDomainVerificationManager.clearPackage(deletedPs.getPackageName());
mPm.mSettings.getKeySetManagerService().removeAppKeySetDataLPw(packageName);
- mPm.mAppsFilter.removePackage(mPm.getPackageSetting(packageName),
+ mPm.mAppsFilter.removePackage(mPm.getPackageStateInternal(packageName),
false /* isReplace */);
removedAppId = mPm.mSettings.removePackageLPw(packageName);
if (outInfo != null) {
diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
index f5010e4..fcecc20 100644
--- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java
+++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
@@ -51,6 +51,7 @@
import com.android.internal.app.ResolverActivity;
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
import java.util.ArrayList;
import java.util.Collections;
@@ -428,7 +429,7 @@
&& isTargetHiddenFromInstantApp));
final boolean blockNormalResolution = !isTargetInstantApp && !isCallerInstantApp
&& mPm.shouldFilterApplicationLocked(
- mPm.getPackageSettingInternal(pi.applicationInfo.packageName,
+ mPm.getPackageStateInternal(pi.applicationInfo.packageName,
Process.SYSTEM_UID), callingUid, userId);
if (!blockResolution && !blockNormalResolution) {
final ResolveInfo ri = new ResolveInfo();
@@ -475,8 +476,8 @@
if (instantAppPkgName == null) {
SettingBase callingSetting =
mPm.mSettings.getSettingLPr(UserHandle.getAppId(callingUid));
- PackageSetting resolvedSetting =
- mPm.getPackageSettingInternal(info.providerInfo.packageName, 0);
+ PackageStateInternal resolvedSetting =
+ mPm.getPackageStateInternal(info.providerInfo.packageName, 0);
if (!mPm.mAppsFilter.shouldFilterApplication(
callingUid, callingSetting, resolvedSetting, userId)) {
continue;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 6df1006..3d6467e 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -55,12 +55,15 @@
import android.content.pm.parsing.component.ParsedMainComponent;
import android.content.pm.parsing.component.ParsedPermission;
import android.content.pm.parsing.component.ParsedProcess;
-import android.content.pm.pkg.PackageUserState;
-import android.content.pm.pkg.PackageUserStateInternal;
+
+import com.android.server.pm.pkg.PackageUserState;
+import com.android.server.pm.pkg.PackageUserStateInternal;
import android.content.pm.pkg.PackageUserStateUtils;
+import com.android.server.pm.pkg.SuspendParams;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
+import android.os.CreateAppDataArgs;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
@@ -289,17 +292,17 @@
private static final String TAG_DEFAULT_DIALER = "default-dialer";
private static final String TAG_VERSION = "version";
/**
- * @deprecated Moved to {@link PackageUserState.SuspendParams}
+ * @deprecated Moved to {@link SuspendParams}
*/
@Deprecated
private static final String TAG_SUSPENDED_DIALOG_INFO = "suspended-dialog-info";
/**
- * @deprecated Moved to {@link PackageUserState.SuspendParams}
+ * @deprecated Moved to {@link SuspendParams}
*/
@Deprecated
private static final String TAG_SUSPENDED_APP_EXTRAS = "suspended-app-extras";
/**
- * @deprecated Moved to {@link PackageUserState.SuspendParams}
+ * @deprecated Moved to {@link SuspendParams}
*/
@Deprecated
private static final String TAG_SUSPENDED_LAUNCHER_EXTRAS = "suspended-launcher-extras";
@@ -822,7 +825,7 @@
PackageSetting ret = addPackageLPw(name, p.getRealName(), p.getPath(),
p.getLegacyNativeLibraryPath(), p.getPrimaryCpuAbi(),
p.getSecondaryCpuAbi(), p.getCpuAbiOverride(),
- p.getAppId(), p.getLongVersionCode(), p.pkgFlags, p.pkgPrivateFlags,
+ p.getAppId(), p.getVersionCode(), p.pkgFlags, p.pkgPrivateFlags,
p.usesStaticLibraries, p.usesStaticLibrariesVersions, p.mimeGroups,
mDomainVerificationManager.generateNewId());
if (ret != null) {
@@ -1730,7 +1733,7 @@
SuspendDialogInfo oldSuspendDialogInfo = null;
int packageDepth = parser.getDepth();
- ArrayMap<String, PackageUserState.SuspendParams> suspendParamsMap = null;
+ ArrayMap<String, SuspendParams> suspendParamsMap = null;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > packageDepth)) {
@@ -1766,7 +1769,7 @@
suspendParamsMap = new ArrayMap<>();
}
suspendParamsMap.put(suspendingPackage,
- PackageUserState.SuspendParams.restoreFromXml(parser));
+ SuspendParams.restoreFromXml(parser));
break;
default:
Slog.wtf(TAG, "Unknown tag " + parser.getName() + " under tag "
@@ -1779,8 +1782,8 @@
.build();
}
if (suspended && suspendParamsMap == null) {
- final PackageUserState.SuspendParams suspendParams =
- PackageUserState.SuspendParams.getInstanceOrNull(
+ final SuspendParams suspendParams =
+ SuspendParams.getInstanceOrNull(
oldSuspendDialogInfo,
suspendedAppExtras,
suspendedLauncherExtras);
@@ -2065,7 +2068,7 @@
final String suspendingPackage = ustate.getSuspendParams().keyAt(i);
serializer.startTag(null, TAG_SUSPEND_PARAMS);
serializer.attribute(null, ATTR_SUSPENDING_PACKAGE, suspendingPackage);
- final PackageUserState.SuspendParams params =
+ final SuspendParams params =
ustate.getSuspendParams().valueAt(i);
if (params != null) {
params.saveToXml(serializer);
@@ -2685,7 +2688,7 @@
serializer.attributeLongHex(null, "ft", pkg.getLastModifiedTime());
serializer.attributeLongHex(null, "it", pkg.getFirstInstallTime());
serializer.attributeLongHex(null, "ut", pkg.getLastUpdateTime());
- serializer.attributeLong(null, "version", pkg.getLongVersionCode());
+ serializer.attributeLong(null, "version", pkg.getVersionCode());
if (pkg.getLegacyNativeLibraryPath() != null) {
serializer.attribute(null, "nativeLibraryPath", pkg.getLegacyNativeLibraryPath());
}
@@ -2738,7 +2741,7 @@
serializer.attributeLongHex(null, "ft", pkg.getLastModifiedTime());
serializer.attributeLongHex(null, "it", pkg.getFirstInstallTime());
serializer.attributeLongHex(null, "ut", pkg.getLastUpdateTime());
- serializer.attributeLong(null, "version", pkg.getLongVersionCode());
+ serializer.attributeLong(null, "version", pkg.getVersionCode());
if (pkg.getSharedUser() == null) {
serializer.attributeInt(null, "userId", pkg.getAppId());
} else {
@@ -4016,9 +4019,11 @@
// Accumulate all required args and call the installer after mPackages lock
// has been released
final String seInfo = AndroidPackageUtils.getSeInfo(ps.getPkg(), ps);
- batch.createAppData(ps.getVolumeUuid(), ps.getPackageName(), userHandle,
+ final CreateAppDataArgs args = Installer.buildCreateAppDataArgs(
+ ps.getVolumeUuid(), ps.getPackageName(), userHandle,
StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE,
ps.getAppId(), seInfo, ps.getPkg().getTargetSdkVersion());
+ batch.createAppData(args);
} else {
// Make sure the app is excluded from storage mapping for this user
writeKernelMappingLPr(ps);
@@ -4217,7 +4222,6 @@
}
if (pkgSetting.getStopped(userId) != stopped) {
pkgSetting.setStopped(stopped, userId);
- // pkgSetting.pkg.mSetStopped = stopped;
if (pkgSetting.getNotLaunched(userId)) {
if (pkgSetting.getInstallSource().installerPackageName != null) {
pm.notifyFirstLaunch(pkgSetting.getPackageName(),
@@ -4399,7 +4403,7 @@
pw.print(",");
pw.print(ps.getAppId());
pw.print(",");
- pw.print(ps.getLongVersionCode());
+ pw.print(ps.getVersionCode());
pw.print(",");
pw.print(ps.getFirstInstallTime());
pw.print(",");
@@ -4479,7 +4483,7 @@
pw.print(prefix); pw.print(" secondaryCpuAbi="); pw.println(ps.getSecondaryCpuAbi());
pw.print(prefix); pw.print(" cpuAbiOverride="); pw.println(ps.getCpuAbiOverride());
}
- pw.print(prefix); pw.print(" versionCode="); pw.print(ps.getLongVersionCode());
+ pw.print(prefix); pw.print(" versionCode="); pw.print(ps.getVersionCode());
if (pkg != null) {
pw.print(" minSdk="); pw.print(pkg.getMinSdkVersion());
pw.print(" targetSdk="); pw.println(pkg.getTargetSdkVersion());
@@ -4770,7 +4774,7 @@
pw.print(prefix);
pw.print(" suspendingPackage=");
pw.print(pus.getSuspendParams().keyAt(i));
- final PackageUserState.SuspendParams params = pus.getSuspendParams().valueAt(i);
+ final SuspendParams params = pus.getSuspendParams().valueAt(i);
if (params != null) {
pw.print(" dialogInfo=");
pw.print(params.dialogInfo);
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index c83cdb4..9cb8863 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -61,6 +61,8 @@
import com.android.server.SystemServiceManager;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageStateUtils;
import com.android.server.rollback.RollbackManagerInternal;
import com.android.server.rollback.WatchdogRollbackLogger;
@@ -344,13 +346,13 @@
int appId = -1;
long ceDataInode = -1;
- final PackageSetting ps = mPmi.getPackageSetting(packageName);
+ final PackageStateInternal ps = mPmi.getPackageStateInternal(packageName);
if (ps != null) {
appId = ps.getAppId();
- ceDataInode = ps.getCeDataInode(UserHandle.USER_SYSTEM);
+ ceDataInode = ps.getUserStateOrDefault(UserHandle.USER_SYSTEM).getCeDataInode();
// NOTE: We ignore the user specified in the InstallParam because we know this is
// an update, and hence need to restore data for all installed users.
- final int[] installedUsers = ps.queryInstalledUsers(allUsers, true);
+ final int[] installedUsers = PackageStateUtils.queryInstalledUsers(ps, allUsers, true);
final String seInfo = AndroidPackageUtils.getSeInfo(pkg, ps);
rm.snapshotAndRestoreUserData(packageName, UserHandle.toUserHandles(installedUsers),
@@ -525,26 +527,6 @@
}
}
- /**
- * Returns id of a committed and non-finalized stated session that contains same
- * {@code packageName}, or {@code -1} if no sessions have this {@code packageName} staged.
- */
- int getSessionIdByPackageName(@NonNull String packageName) {
- synchronized (mStagedSessions) {
- for (int i = 0; i < mStagedSessions.size(); i++) {
- StagedSession stagedSession = mStagedSessions.valueAt(i);
- if (!stagedSession.isCommitted() || stagedSession.isDestroyed()
- || stagedSession.isInTerminalState()) {
- continue;
- }
- if (stagedSession.getPackageName().equals(packageName)) {
- return stagedSession.sessionId();
- }
- }
- }
- return -1;
- }
-
@VisibleForTesting
void createSession(@NonNull StagedSession sessionInfo) {
synchronized (mStagedSessions) {
@@ -950,6 +932,9 @@
info.diskImagePath = ai.modulePath;
info.versionCode = ai.versionCode;
info.versionName = ai.versionName;
+ info.hasBootClassPathJars = ai.hasBootClassPathJars;
+ info.hasDex2OatBootClassPathJars = ai.hasDex2OatBootClassPathJars;
+ info.hasSystemServerClassPathJars = ai.hasSystemServerClassPathJars;
return info;
}
}
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 626146c2..7f42374 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -141,7 +141,8 @@
UserManager.DISALLOW_CONFIG_PRIVATE_DNS,
UserManager.DISALLOW_MICROPHONE_TOGGLE,
UserManager.DISALLOW_CAMERA_TOGGLE,
- UserManager.DISALLOW_CHANGE_WIFI_STATE
+ UserManager.DISALLOW_CHANGE_WIFI_STATE,
+ UserManager.DISALLOW_WIFI_TETHERING
});
public static final Set<String> DEPRECATED_USER_RESTRICTIONS = Sets.newArraySet(
@@ -186,7 +187,8 @@
UserManager.DISALLOW_CONFIG_PRIVATE_DNS,
UserManager.DISALLOW_MICROPHONE_TOGGLE,
UserManager.DISALLOW_CAMERA_TOGGLE,
- UserManager.DISALLOW_CHANGE_WIFI_STATE
+ UserManager.DISALLOW_CHANGE_WIFI_STATE,
+ UserManager.DISALLOW_WIFI_TETHERING
);
/**
@@ -222,7 +224,8 @@
UserManager.DISALLOW_AIRPLANE_MODE,
UserManager.DISALLOW_CONFIG_DATE_TIME,
UserManager.DISALLOW_CONFIG_PRIVATE_DNS,
- UserManager.DISALLOW_CHANGE_WIFI_STATE
+ UserManager.DISALLOW_CHANGE_WIFI_STATE,
+ UserManager.DISALLOW_WIFI_TETHERING
);
/**
diff --git a/services/core/java/com/android/server/pm/VerificationParams.java b/services/core/java/com/android/server/pm/VerificationParams.java
index c5569cd..7dec756 100644
--- a/services/core/java/com/android/server/pm/VerificationParams.java
+++ b/services/core/java/com/android/server/pm/VerificationParams.java
@@ -397,6 +397,18 @@
PackageManager.EXTRA_VERIFICATION_LONG_VERSION_CODE,
pkgLite.getLongVersionCode());
+ final String baseCodePath = mPackageLite.getBaseApkPath();
+ final String[] splitCodePaths = mPackageLite.getSplitApkPaths();
+ final String rootHashString =
+ PackageManagerServiceUtils.buildVerificationRootHashString(baseCodePath,
+ splitCodePaths);
+
+ if (rootHashString != null) {
+ verification.putExtra(PackageManager.EXTRA_VERIFICATION_ROOT_HASH, rootHashString);
+ }
+
+ verification.putExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, mDataLoaderType);
+
populateInstallerExtras(verification);
final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 4c88b47..0ab1d36 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -46,7 +46,6 @@
import android.content.pm.parsing.component.ParsedProcess;
import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedService;
-import android.content.pm.pkg.PackageUserState;
import android.content.pm.pkg.PackageUserStateUtils;
import android.os.UserHandle;
import android.util.ArrayMap;
@@ -55,11 +54,12 @@
import android.util.Slog;
import com.android.internal.util.ArrayUtils;
-import com.android.server.pm.PackageSetting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageStateUnserialized;
+import com.android.server.pm.pkg.PackageUserState;
import libcore.util.EmptyArray;
@@ -70,9 +70,9 @@
/**
- * Methods that use a {@link PackageSetting} use it to override information provided from the raw
- * package, or to provide information that would otherwise be missing. Null can be passed if none of
- * the state values should be applied.
+ * Methods that use a {@link PackageStateInternal} use it to override information provided from the
+ * raw package, or to provide information that would otherwise be missing. Null can be passed if
+ * none of the state values should be applied.
*
* @hide
**/
@@ -86,7 +86,7 @@
public static PackageInfo generate(AndroidPackage pkg, int[] gids,
@PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
Set<String> grantedPermissions, PackageUserState state, int userId,
- @Nullable PackageSetting pkgSetting) {
+ @Nullable PackageStateInternal pkgSetting) {
return generateWithComponents(pkg, gids, flags, firstInstallTime, lastUpdateTime,
grantedPermissions, state, userId, null, pkgSetting);
}
@@ -96,7 +96,7 @@
*/
@Nullable
public static PackageInfo generate(AndroidPackage pkg, ApexInfo apexInfo, int flags,
- @Nullable PackageSetting pkgSetting) {
+ @Nullable PackageStateInternal pkgSetting) {
return generateWithComponents(pkg, EmptyArray.INT, flags, 0, 0, Collections.emptySet(),
PackageUserState.DEFAULT, UserHandle.getCallingUserId(), apexInfo, pkgSetting);
}
@@ -107,7 +107,7 @@
private static PackageInfo generateWithComponents(AndroidPackage pkg, int[] gids,
@PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
Set<String> grantedPermissions, PackageUserState state, int userId,
- @Nullable ApexInfo apexInfo, @Nullable PackageSetting pkgSetting) {
+ @Nullable ApexInfo apexInfo, @Nullable PackageStateInternal pkgSetting) {
ApplicationInfo applicationInfo = generateApplicationInfo(pkg, flags, state, userId,
pkgSetting);
if (applicationInfo == null) {
@@ -210,7 +210,7 @@
@Nullable
public static ApplicationInfo generateApplicationInfo(AndroidPackage pkg,
@PackageManager.ApplicationInfoFlags int flags, @NonNull PackageUserState state,
- int userId, @Nullable PackageSetting pkgSetting) {
+ int userId, @Nullable PackageStateInternal pkgSetting) {
if (pkg == null) {
return null;
}
@@ -227,7 +227,7 @@
if (pkgSetting != null) {
// TODO(b/135203078): Remove PackageParser1/toAppInfoWithoutState and clean all this up
- PackageStateUnserialized pkgState = pkgSetting.getPkgState();
+ PackageStateUnserialized pkgState = pkgSetting.getTransientState();
info.hiddenUntilInstalled = pkgState.isHiddenUntilInstalled();
List<String> usesLibraryFiles = pkgState.getUsesLibraryFiles();
List<SharedLibraryInfo> usesLibraryInfos = pkgState.getUsesLibraryInfos();
@@ -256,7 +256,7 @@
@Nullable
public static ActivityInfo generateActivityInfo(AndroidPackage pkg, ParsedActivity a,
@PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId,
- @Nullable PackageSetting pkgSetting) {
+ @Nullable PackageStateInternal pkgSetting) {
return generateActivityInfo(pkg, a, flags, state, null, userId, pkgSetting);
}
@@ -267,7 +267,7 @@
private static ActivityInfo generateActivityInfo(AndroidPackage pkg, ParsedActivity a,
@PackageManager.ComponentInfoFlags int flags, PackageUserState state,
@Nullable ApplicationInfo applicationInfo, int userId,
- @Nullable PackageSetting pkgSetting) {
+ @Nullable PackageStateInternal pkgSetting) {
if (a == null) return null;
if (!checkUseInstalledOrHidden(pkg, pkgSetting, state, flags)) {
return null;
@@ -292,7 +292,7 @@
@Nullable
public static ServiceInfo generateServiceInfo(AndroidPackage pkg, ParsedService s,
@PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId,
- @Nullable PackageSetting pkgSetting) {
+ @Nullable PackageStateInternal pkgSetting) {
return generateServiceInfo(pkg, s, flags, state, null, userId, pkgSetting);
}
@@ -303,7 +303,7 @@
private static ServiceInfo generateServiceInfo(AndroidPackage pkg, ParsedService s,
@PackageManager.ComponentInfoFlags int flags, PackageUserState state,
@Nullable ApplicationInfo applicationInfo, int userId,
- @Nullable PackageSetting pkgSetting) {
+ @Nullable PackageStateInternal pkgSetting) {
if (s == null) return null;
if (!checkUseInstalledOrHidden(pkg, pkgSetting, state, flags)) {
return null;
@@ -328,7 +328,7 @@
public static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p,
@PackageManager.ComponentInfoFlags int flags, PackageUserState state,
@NonNull ApplicationInfo applicationInfo, int userId,
- @Nullable PackageSetting pkgSetting) {
+ @Nullable PackageStateInternal pkgSetting) {
if (p == null) return null;
if (!checkUseInstalledOrHidden(pkg, pkgSetting, state, flags)) {
return null;
@@ -354,7 +354,7 @@
@Nullable
public static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i,
AndroidPackage pkg, @PackageManager.ComponentInfoFlags int flags, int userId,
- @Nullable PackageSetting pkgSetting) {
+ @Nullable PackageStateInternal pkgSetting) {
if (i == null) return null;
InstrumentationInfo info =
@@ -377,8 +377,8 @@
return info;
}
- // TODO(b/135203078): Determine if permission methods need to pass in a non-null PackageSetting
- // os that checkUseInstalledOrHidden filter can apply
+ // TODO(b/135203078): Determine if permission methods need to pass in a non-null
+ // PackageStateInternal os that checkUseInstalledOrHidden filter can apply
@Nullable
public static PermissionInfo generatePermissionInfo(ParsedPermission p,
@PackageManager.ComponentInfoFlags int flags) {
@@ -422,13 +422,13 @@
* explicitly wanted all uninstalled and hidden packages as well.
*/
public static boolean checkUseInstalledOrHidden(AndroidPackage pkg,
- PackageSetting pkgSetting, PackageUserState state,
+ PackageStateInternal pkgSetting, PackageUserState state,
@PackageManager.PackageInfoFlags int flags) {
// Returns false if the package is hidden system app until installed.
if ((flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) == 0
&& !state.isInstalled()
&& pkgSetting != null
- && pkgSetting.getPkgState().isHiddenUntilInstalled()) {
+ && pkgSetting.getTransientState().isHiddenUntilInstalled()) {
return false;
}
@@ -441,7 +441,7 @@
}
private static void assignSharedFieldsForComponentInfo(@NonNull ComponentInfo componentInfo,
- @NonNull ParsedMainComponent mainComponent, @Nullable PackageSetting pkgSetting,
+ @NonNull ParsedMainComponent mainComponent, @Nullable PackageStateInternal pkgSetting,
int userId) {
assignStateFieldsForPackageItemInfo(componentInfo, mainComponent, pkgSetting, userId);
componentInfo.descriptionRes = mainComponent.getDescriptionRes();
@@ -453,7 +453,7 @@
private static void assignStateFieldsForPackageItemInfo(
@NonNull PackageItemInfo packageItemInfo, @NonNull ParsedComponent component,
- @Nullable PackageSetting pkgSetting, int userId) {
+ @Nullable PackageStateInternal pkgSetting, int userId) {
Pair<CharSequence, Integer> labelAndIcon =
ParsedComponentStateUtils.getNonLocalizedLabelAndIcon(component, pkgSetting,
userId);
@@ -467,7 +467,7 @@
}
/** @see ApplicationInfo#flags */
- public static int appInfoFlags(AndroidPackage pkg, @Nullable PackageSetting pkgSetting) {
+ public static int appInfoFlags(AndroidPackage pkg, @Nullable PackageStateInternal pkgSetting) {
// @formatter:off
int pkgWithoutStateFlags = PackageInfoWithoutStateUtils.appInfoFlags(pkg)
| flag(pkg.isSystem(), ApplicationInfo.FLAG_SYSTEM)
@@ -478,18 +478,20 @@
}
/** @see ApplicationInfo#flags */
- public static int appInfoFlags(int pkgWithoutStateFlags, @NonNull PackageSetting pkgSetting) {
+ public static int appInfoFlags(int pkgWithoutStateFlags,
+ @NonNull PackageStateInternal pkgSetting) {
// @formatter:off
int flags = pkgWithoutStateFlags;
if (pkgSetting != null) {
- flags |= flag(pkgSetting.getPkgState().isUpdatedSystemApp(), ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
+ flags |= flag(pkgSetting.getTransientState().isUpdatedSystemApp(), ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
}
return flags;
// @formatter:on
}
/** @see ApplicationInfo#privateFlags */
- public static int appInfoPrivateFlags(AndroidPackage pkg, @Nullable PackageSetting pkgSetting) {
+ public static int appInfoPrivateFlags(AndroidPackage pkg,
+ @Nullable PackageStateInternal pkgSetting) {
// @formatter:off
int pkgWithoutStateFlags = PackageInfoWithoutStateUtils.appInfoPrivateFlags(pkg)
| flag(pkg.isSystemExt(), ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT)
@@ -504,7 +506,8 @@
}
/** @see ApplicationInfo#privateFlags */
- public static int appInfoPrivateFlags(int pkgWithoutStateFlags, @Nullable PackageSetting pkgSetting) {
+ public static int appInfoPrivateFlags(int pkgWithoutStateFlags,
+ @Nullable PackageStateInternal pkgSetting) {
// @formatter:off
// TODO: Add state specific flags
return pkgWithoutStateFlags;
@@ -513,7 +516,7 @@
/** @see ApplicationInfo#privateFlagsExt */
public static int appInfoPrivateFlagsExt(AndroidPackage pkg,
- @Nullable PackageSetting pkgSetting) {
+ @Nullable PackageStateInternal pkgSetting) {
// @formatter:off
int pkgWithoutStateFlags = PackageInfoWithoutStateUtils.appInfoPrivateFlagsExt(pkg);
return appInfoPrivateFlagsExt(pkgWithoutStateFlags, pkgSetting);
@@ -522,7 +525,7 @@
/** @see ApplicationInfo#privateFlagsExt */
public static int appInfoPrivateFlagsExt(int pkgWithoutStateFlags,
- @Nullable PackageSetting pkgSetting) {
+ @Nullable PackageStateInternal pkgSetting) {
// @formatter:off
// TODO: Add state specific flags
return pkgWithoutStateFlags;
@@ -626,7 +629,7 @@
@Nullable
public ApplicationInfo generate(AndroidPackage pkg,
@PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId,
- @Nullable PackageSetting pkgSetting) {
+ @Nullable PackageStateInternal pkgSetting) {
ApplicationInfo appInfo = mCache.get(pkg.getPackageName());
if (appInfo != null) {
return appInfo;
diff --git a/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java b/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java
index 54466ac..3a49216 100644
--- a/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/ParsedComponentStateUtils.java
@@ -22,6 +22,7 @@
import android.util.Pair;
import com.android.server.pm.PackageSetting;
+import com.android.server.pm.pkg.PackageStateInternal;
/**
* For exposing internal fields to the rest of the server, enforcing that any overridden state from
@@ -37,12 +38,12 @@
@NonNull
public static Pair<CharSequence, Integer> getNonLocalizedLabelAndIcon(ParsedComponent component,
- @Nullable PackageSetting pkgSetting, int userId) {
+ @Nullable PackageStateInternal pkgSetting, int userId) {
CharSequence label = component.getNonLocalizedLabel();
int icon = component.getIcon();
Pair<String, Integer> overrideLabelIcon = pkgSetting == null ? null :
- pkgSetting.readUserState(userId)
+ pkgSetting.getUserStateOrDefault(userId)
.getOverrideLabelIconForComponent(component.getComponentName());
if (overrideLabelIcon != null) {
if (overrideLabelIcon.first != null) {
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
index 55911b6..61fd5ee 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
@@ -38,7 +38,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemConfig;
import com.android.server.pm.PackageManagerException;
-import com.android.server.pm.PackageSetting;
+import com.android.server.pm.pkg.PackageStateInternal;
import java.io.IOException;
import java.util.ArrayList;
@@ -222,11 +222,11 @@
}
public static int getHiddenApiEnforcementPolicy(AndroidPackage pkg,
- @NonNull PackageSetting pkgSetting) {
+ @NonNull PackageStateInternal pkgSetting) {
boolean isAllowedToUseHiddenApis;
if (pkg.isSignedWithPlatformKey()) {
isAllowedToUseHiddenApis = true;
- } else if (pkg.isSystem() || pkgSetting.getPkgState().isUpdatedSystemApp()) {
+ } else if (pkg.isSystem() || pkgSetting.getTransientState().isUpdatedSystemApp()) {
isAllowedToUseHiddenApis = pkg.isUsesNonSdkApi()
|| SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(
pkg.getPackageName());
@@ -263,7 +263,8 @@
return true;
}
- public static String getPrimaryCpuAbi(AndroidPackage pkg, @Nullable PackageSetting pkgSetting) {
+ public static String getPrimaryCpuAbi(AndroidPackage pkg,
+ @Nullable PackageStateInternal pkgSetting) {
if (pkgSetting == null || TextUtils.isEmpty(pkgSetting.getPrimaryCpuAbi())) {
return getRawPrimaryCpuAbi(pkg);
}
@@ -272,7 +273,7 @@
}
public static String getSecondaryCpuAbi(AndroidPackage pkg,
- @Nullable PackageSetting pkgSetting) {
+ @Nullable PackageStateInternal pkgSetting) {
if (pkgSetting == null || TextUtils.isEmpty(pkgSetting.getSecondaryCpuAbi())) {
return getRawSecondaryCpuAbi(pkg);
}
@@ -282,7 +283,7 @@
/**
* Returns the primary ABI as parsed from the package. Used only during parsing and derivation.
- * Otherwise prefer {@link #getPrimaryCpuAbi(AndroidPackage, PackageSetting)}.
+ * Otherwise prefer {@link #getPrimaryCpuAbi(AndroidPackage, PackageStateInternal)}.
*/
public static String getRawPrimaryCpuAbi(AndroidPackage pkg) {
return ((AndroidPackageHidden) pkg).getPrimaryCpuAbi();
@@ -290,15 +291,16 @@
/**
* Returns the secondary ABI as parsed from the package. Used only during parsing and
- * derivation. Otherwise prefer {@link #getSecondaryCpuAbi(AndroidPackage, PackageSetting)}.
+ * derivation. Otherwise prefer
+ * {@link #getSecondaryCpuAbi(AndroidPackage, PackageStateInternal)}.
*/
public static String getRawSecondaryCpuAbi(AndroidPackage pkg) {
return ((AndroidPackageHidden) pkg).getSecondaryCpuAbi();
}
- public static String getSeInfo(AndroidPackage pkg, @Nullable PackageSetting pkgSetting) {
+ public static String getSeInfo(AndroidPackage pkg, @Nullable PackageStateInternal pkgSetting) {
if (pkgSetting != null) {
- String overrideSeInfo = pkgSetting.getPkgState().getOverrideSeInfo();
+ String overrideSeInfo = pkgSetting.getTransientState().getOverrideSeInfo();
if (!TextUtils.isEmpty(overrideSeInfo)) {
return overrideSeInfo;
}
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PkgAppInfo.java b/services/core/java/com/android/server/pm/parsing/pkg/PkgAppInfo.java
index c7dc267..a13f297 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PkgAppInfo.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PkgAppInfo.java
@@ -21,6 +21,7 @@
import android.content.pm.parsing.PkgWithoutStateAppInfo;
import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.pkg.PackageState;
/**
* Equivalent of {@link PkgWithoutStateAppInfo} but contains fields that are set inside {@link
@@ -50,8 +51,12 @@
String getSecondaryNativeLibraryDir();
/**
- * @see ApplicationInfo#uid
+ * This is an appId, the {@link ApplicationInfo#uid} if the user ID is
+ * {@link android.os.UserHandle#SYSTEM}.
+ *
+ * @deprecated Use {@link PackageState#getAppId()} instead.
*/
+ @Deprecated
int getUid();
/**
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PkgPackageInfo.java b/services/core/java/com/android/server/pm/parsing/pkg/PkgPackageInfo.java
index b3b3910..e2efbe1 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PkgPackageInfo.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PkgPackageInfo.java
@@ -31,16 +31,11 @@
public interface PkgPackageInfo extends PkgWithoutStatePackageInfo {
/**
- * For marking packages required for a minimal boot state, through the "coreApp" manifest
- * attribute.
- *
* @see PackageInfo#coreApp
*/
boolean isCoreApp();
/**
- * Whether or not the package is a stub and must be replaced by the full version.
- *
* @see PackageInfo#isStub
*/
boolean isStub();
diff --git a/services/core/java/com/android/server/pm/permission/OWNERS b/services/core/java/com/android/server/pm/permission/OWNERS
index 8c1a90c..fc0ee23 100644
--- a/services/core/java/com/android/server/pm/permission/OWNERS
+++ b/services/core/java/com/android/server/pm/permission/OWNERS
@@ -1,7 +1,3 @@
include platform/frameworks/base:/core/java/android/permission/OWNERS
-per-file DefaultPermissionGrantPolicy.java = hackbod@android.com
-per-file DefaultPermissionGrantPolicy.java = jsharkey@android.com
-per-file DefaultPermissionGrantPolicy.java = toddke@google.com
-per-file DefaultPermissionGrantPolicy.java = yamasani@google.com
-per-file DefaultPermissionGrantPolicy.java = patb@google.com
+per-file DefaultPermissionGrantPolicy.java = file:platform/frameworks/base:/core/java/android/permission/DEFAULT_PERMISSION_GRANT_POLICY_OWNERS
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index e62988dd..3855e65 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -159,6 +159,7 @@
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.permission.PermissionManagerServiceInternal.HotwordDetectionServiceProvider;
import com.android.server.pm.permission.PermissionManagerServiceInternal.OnRuntimePermissionStateChangedListener;
+import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.SoftRestrictedPermissionPolicy;
@@ -1472,7 +1473,7 @@
"grantRuntimePermission");
final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
- final PackageSetting ps = mPackageManagerInt.getPackageSetting(packageName);
+ final PackageStateInternal ps = mPackageManagerInt.getPackageStateInternal(packageName);
if (pkg == null || ps == null) {
Log.e(TAG, "Unknown package: " + packageName);
return;
@@ -1575,7 +1576,7 @@
return;
}
} else {
- if (ps.getInstantApp(userId) && !bp.isInstant()) {
+ if (ps.getUserStateOrDefault(userId).isInstantApp() && !bp.isInstant()) {
throw new SecurityException("Cannot grant non-ephemeral permission" + permName
+ " for package " + packageName);
}
@@ -2554,7 +2555,7 @@
@NonNull
private Set<String> getGrantedPermissionsInternal(@NonNull String packageName,
@UserIdInt int userId) {
- final PackageSetting ps = mPackageManagerInt.getPackageSetting(packageName);
+ final PackageStateInternal ps = mPackageManagerInt.getPackageStateInternal(packageName);
if (ps == null) {
return Collections.emptySet();
}
@@ -2565,7 +2566,7 @@
Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
return Collections.emptySet();
}
- if (!ps.getInstantApp(userId)) {
+ if (!ps.getUserStateOrDefault(userId).isInstantApp()) {
return uidState.getGrantedPermissions();
} else {
// Install permission state is shared among all users, but instant app state is
@@ -2631,7 +2632,8 @@
// being upgraded to target a newer SDK, in which case dangerous permissions
// are transformed from install time to runtime ones.
- final PackageSetting ps = mPackageManagerInt.getPackageSetting(pkg.getPackageName());
+ final PackageStateInternal ps =
+ mPackageManagerInt.getPackageStateInternal(pkg.getPackageName());
if (ps == null) {
return;
}
@@ -2700,7 +2702,7 @@
if (uidState.isMissing()) {
Collection<String> uidRequestedPermissions;
int targetSdkVersion;
- if (!ps.isSharedUser()) {
+ if (ps.getSharedUser() == null) {
uidRequestedPermissions = pkg.getRequestedPermissions();
targetSdkVersion = pkg.getTargetSdkVersion();
} else {
@@ -2751,7 +2753,7 @@
if (replace) {
userState.setInstallPermissionsFixed(ps.getPackageName(), false);
- if (!ps.isSharedUser()) {
+ if (ps.getSharedUser() == null) {
origState = new UidPermissionState(uidState);
uidState.reset();
} else {
@@ -3050,7 +3052,7 @@
if ((changedInstallPermission || replace)
&& !userState.areInstallPermissionsFixed(ps.getPackageName())
- && !ps.isSystem() || ps.getPkgState().isUpdatedSystemApp()) {
+ && !ps.isSystem() || ps.getTransientState().isUpdatedSystemApp()) {
// This is the first that we have heard about this package, so the
// permissions we have now selected are fixed until explicitly
// changed.
@@ -3466,7 +3468,7 @@
}
private boolean checkPrivilegedPermissionAllowlist(@NonNull AndroidPackage pkg,
- @NonNull PackageSetting packageSetting, @NonNull Permission permission) {
+ @NonNull PackageStateInternal packageSetting, @NonNull Permission permission) {
if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE) {
return true;
}
@@ -3489,7 +3491,7 @@
return false;
}
// Updated system apps do not need to be allowlisted
- if (packageSetting.getPkgState().isUpdatedSystemApp()) {
+ if (packageSetting.getTransientState().isUpdatedSystemApp()) {
// Let shouldGrantPermissionByProtectionFlags() decide whether the privileged permission
// can be granted, because an updated system app may be in a shared UID, and in case a
// new privileged permission is requested by the updated system app but not the factory
@@ -3587,7 +3589,7 @@
}
private boolean shouldGrantPermissionByProtectionFlags(@NonNull AndroidPackage pkg,
- @NonNull PackageSetting pkgSetting, @NonNull Permission bp,
+ @NonNull PackageStateInternal pkgSetting, @NonNull Permission bp,
@NonNull ArraySet<String> shouldGrantPrivilegedPermissionIfWasGranted) {
boolean allowed = false;
final boolean isPrivilegedPermission = bp.isPrivileged();
@@ -3596,7 +3598,7 @@
final String permissionName = bp.getName();
// For updated system applications, a privileged/oem permission
// is granted only if it had been defined by the original application.
- if (pkgSetting.getPkgState().isUpdatedSystemApp()) {
+ if (pkgSetting.getTransientState().isUpdatedSystemApp()) {
final PackageSetting disabledPs = mPackageManagerInt
.getDisabledSystemPackage(pkg.getPackageName());
final AndroidPackage disabledPkg = disabledPs == null ? null : disabledPs.getPkg();
@@ -3735,7 +3737,7 @@
@NonNull
private SigningDetails getSourcePackageSigningDetails(
@NonNull Permission bp) {
- final PackageSetting ps = getSourcePackageSetting(bp);
+ final PackageStateInternal ps = getSourcePackageSetting(bp);
if (ps == null) {
return SigningDetails.UNKNOWN;
}
@@ -3743,9 +3745,9 @@
}
@Nullable
- private PackageSetting getSourcePackageSetting(@NonNull Permission bp) {
+ private PackageStateInternal getSourcePackageSetting(@NonNull Permission bp) {
final String sourcePackageName = bp.getPackageName();
- return mPackageManagerInt.getPackageSetting(sourcePackageName);
+ return mPackageManagerInt.getPackageStateInternal(sourcePackageName);
}
private static boolean canGrantOemPermission(AndroidPackage pkg, String permission) {
@@ -4328,9 +4330,8 @@
}
final AndroidPackage sourcePkg =
mPackageManagerInt.getPackage(bp.getPackageName());
- final PackageSetting sourcePs =
- (PackageSetting) mPackageManagerInt.getPackageSetting(
- bp.getPackageName());
+ final PackageStateInternal sourcePs =
+ mPackageManagerInt.getPackageStateInternal(bp.getPackageName());
synchronized (mLock) {
if (sourcePkg != null && sourcePs != null) {
continue;
@@ -4447,9 +4448,8 @@
for (final Permission bp : needsUpdate) {
final AndroidPackage sourcePkg =
mPackageManagerInt.getPackage(bp.getPackageName());
- final PackageSetting sourcePs =
- (PackageSetting) mPackageManagerInt.getPackageSetting(
- bp.getPackageName());
+ final PackageStateInternal sourcePs =
+ mPackageManagerInt.getPackageStateInternal(bp.getPackageName());
synchronized (mLock) {
if (sourcePkg != null && sourcePs != null) {
continue;
@@ -4664,7 +4664,7 @@
@GuardedBy("mLock")
@Nullable
- private UidPermissionState getUidStateLocked(@NonNull PackageSetting ps,
+ private UidPermissionState getUidStateLocked(@NonNull PackageStateInternal ps,
@UserIdInt int userId) {
return getUidStateLocked(ps.getAppId(), userId);
}
@@ -4899,7 +4899,8 @@
private boolean canAdoptPermissionsInternal(@NonNull String oldPackageName,
@NonNull AndroidPackage newPkg) {
- final PackageSetting oldPs = mPackageManagerInt.getPackageSetting(oldPackageName);
+ final PackageStateInternal oldPs =
+ mPackageManagerInt.getPackageStateInternal(oldPackageName);
if (oldPs == null) {
return false;
}
@@ -4928,7 +4929,8 @@
// - Remove the app from the original shared user group. Other apps in the shared
// user group will perceive as if the original app is uninstalled.
if (previousAppId != Process.INVALID_UID) {
- final PackageSetting ps = mPackageManagerInt.getPackageSetting(pkg.getPackageName());
+ final PackageStateInternal ps =
+ mPackageManagerInt.getPackageStateInternal(pkg.getPackageName());
final List<AndroidPackage> origSharedUserPackages =
mPackageManagerInt.getPackagesForAppId(previousAppId);
diff --git a/services/core/java/com/android/server/pm/pkg/PackageState.java b/services/core/java/com/android/server/pm/pkg/PackageState.java
index 2ee8eae..82edce6 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageState.java
@@ -18,14 +18,14 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
+import android.annotation.Size;
import android.annotation.UserIdInt;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.SigningInfo;
-import android.content.pm.pkg.PackageUserState;
+import android.util.SparseArray;
import com.android.internal.R;
import com.android.server.pm.PackageSetting;
@@ -79,7 +79,8 @@
AndroidPackageApi getAndroidPackage();
/**
- * The non-user-specific UID
+ * The non-user-specific UID, or the UID if the user ID is
+ * {@link android.os.UserHandle#USER_SYSTEM}.
*/
int getAppId();
@@ -91,36 +92,51 @@
*/
int getCategoryOverride();
+ /**
+ * The install time CPU override, if any. This value is written at install time
+ * and doesn't change during the life of an install. If non-null,
+ * {@link #getPrimaryCpuAbi()} will also contain the same value.
+ */
@Nullable
String getCpuAbiOverride();
/**
- * In epoch milliseconds.
+ * In epoch milliseconds. The timestamp of the first install of the particular app on the
+ * device, surviving past app updates. This does not survive full uninstalls + reinstalls.
*/
long getFirstInstallTime();
/**
- * In epoch milliseconds.
+ * In epoch milliseconds. The last modified time of the file directory which houses the app
+ * APKs. Only updated on package update; does not track realtime modifications.
*/
long getLastModifiedTime();
+ /**
+ * An aggregation across the framework of the last time an app was used for a particular reason.
+ * Keys are indexes into the array represented by {@link PackageManager.NotifyReason}, values
+ * are in epoch milliseconds.
+ */
+ @Size(PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT)
@NonNull
long[] getLastPackageUsageTime();
/**
- * In epoch milliseconds.
+ * In epoch milliseconds. The timestamp of the last time the package on device went through
+ * an update package installation.
*/
long getLastUpdateTime();
/**
+ * Cached here in case the physical code directory on device is unmounted.
* @see AndroidPackageApi#getLongVersionCode()
*/
- long getLongVersionCode();
+ long getVersionCode();
/**
* Maps mime group name to the set of Mime types in a group. Mime groups declared by app are
* populated with empty sets at construction. Mime groups can not be created/removed at runtime,
- * thus keys in this map should not change
+ * thus keys in this map should not change.
*/
@NonNull
Map<String, Set<String>> getMimeGroups();
@@ -132,17 +148,21 @@
String getPackageName();
/**
+ * TODO: Rename this to getCodePath
* @see AndroidPackageApi#getPath()
*/
@NonNull
File getPath();
+ /**
+ * @see ApplicationInfo#primaryCpuAbi
+ */
@Nullable
String getPrimaryCpuAbi();
- @Nullable
- String getSeInfoOverride();
-
+ /**
+ * @see ApplicationInfo#secondaryCpuAbi
+ */
@Nullable
String getSecondaryCpuAbi();
@@ -158,16 +178,18 @@
@NonNull
SigningInfo getSigningInfo();
- /**
- * Valid users for this package, for use with {@link #getUserState(int)}.
- */
- int[] getUserIds();
+ @NonNull
+ SparseArray<? extends PackageUserState> getUserStates();
/**
- * Retrieves per-user state for this package. Acceptable user IDs are in {@link #getUserIds()}.
+ * @return the result of {@link #getUserStates()}.get(userId) or
+ * {@link PackageUserState#DEFAULT} if the state doesn't exist.
*/
- @Nullable
- PackageUserState getUserState(@UserIdInt int userId);
+ @NonNull
+ default PackageUserState getUserStateOrDefault(@UserIdInt int userId) {
+ PackageUserState userState = getUserStates().get(userId);
+ return userState == null ? PackageUserState.DEFAULT : userState;
+ }
/**
* The actual files resolved for each shared library.
@@ -220,6 +242,9 @@
*/
boolean isHiddenUntilInstalled();
+ /**
+ * @see com.android.server.pm.permission.UserPermissionState
+ */
boolean isInstallPermissionsFixed();
/**
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
index 0ba6d6e..46d32b9 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
@@ -24,7 +24,6 @@
import android.content.pm.SharedLibraryInfo;
import android.content.pm.SigningInfo;
import android.content.pm.overlay.OverlayPaths;
-import android.content.pm.pkg.PackageUserState;
import android.util.SparseArray;
import com.android.internal.util.DataClass;
@@ -51,7 +50,7 @@
@DataClass.Suppress({"mUserStates"})
public class PackageStateImpl implements PackageState {
- public static PackageState copy(@NonNull PackageSetting pkgSetting) {
+ public static PackageState copy(@NonNull PackageStateInternal pkgSetting) {
return new PackageStateImpl(pkgSetting, pkgSetting.getPkg());
}
@@ -138,15 +137,11 @@
private final List<SharedLibraryInfo> mUsesLibraryInfos;
@NonNull
private final List<String> mUsesLibraryFiles;
- @Nullable
- private final String mSeInfoOverride;
@NonNull
private final long[] mLastPackageUsageTime;
@NonNull
private final SigningInfo mSigningInfo;
@NonNull
- private final int[] mUserIds;
- @NonNull
private final SparseArray<PackageUserState> mUserStates;
private PackageStateImpl(@NonNull PackageState pkgState, @Nullable AndroidPackage pkg) {
@@ -170,7 +165,7 @@
mFirstInstallTime = pkgState.getFirstInstallTime();
mLastModifiedTime = pkgState.getLastModifiedTime();
mLastUpdateTime = pkgState.getLastUpdateTime();
- mLongVersionCode = pkgState.getLongVersionCode();
+ mLongVersionCode = pkgState.getVersionCode();
mMimeGroups = pkgState.getMimeGroups();
mPath = pkgState.getPath();
mPrimaryCpuAbi = pkgState.getPrimaryCpuAbi();
@@ -184,24 +179,19 @@
setBoolean(Booleans.HIDDEN_UNTIL_INSTALLED, pkgState.isHiddenUntilInstalled());
setBoolean(Booleans.INSTALL_PERMISSIONS_FIXED, pkgState.isInstallPermissionsFixed());
setBoolean(Booleans.UPDATE_AVAILABLE, pkgState.isUpdateAvailable());
- mSeInfoOverride = pkgState.getSeInfoOverride();
mLastPackageUsageTime = pkgState.getLastPackageUsageTime();
setBoolean(Booleans.UPDATED_SYSTEM_APP, pkgState.isUpdatedSystemApp());
mSigningInfo = pkgState.getSigningInfo();
- mUserIds = pkgState.getUserIds();
- mUserStates = new SparseArray<>(mUserIds.length);
- for (int userId : mUserIds) {
- mUserStates.put(userId, UserStateImpl.copy(pkgState.getUserState(userId)));
+ SparseArray<? extends PackageUserState> userStates = pkgState.getUserStates();
+ int userStatesSize = userStates.size();
+ mUserStates = new SparseArray<>(userStatesSize);
+ for (int index = 0; index < userStatesSize; index++) {
+ mUserStates.put(mUserStates.keyAt(index),
+ UserStateImpl.copy(mUserStates.valueAt(index)));
}
}
- @Nullable
- @Override
- public PackageUserState getUserState(int userId) {
- return mUserStates.get(userId);
- }
-
@Override
public boolean isExternalStorage() {
return getBoolean(Booleans.EXTERNAL_STORAGE);
@@ -510,10 +500,10 @@
}
@DataClass.Generated(
- time = 1630604891308L,
+ time = 1633375703010L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java",
- inputSignatures = "private int mBooleans\nprivate final long mCeDataInode\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mDisabledComponents\nprivate final @android.content.pm.PackageManager.DistractionRestriction int mDistractionFlags\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mEnabledComponents\nprivate final int mEnabledState\nprivate final @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate final @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate final @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate final @android.annotation.NonNull android.content.pm.overlay.OverlayPaths mOverlayPaths\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate final @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate final @android.annotation.Nullable java.lang.String mSplashScreenTheme\npublic static android.content.pm.pkg.PackageUserState copy(android.content.pm.pkg.PackageUserState)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isSuspended()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\nclass UserStateImpl extends java.lang.Object implements [android.content.pm.pkg.PackageUserState, android.content.pm.pkg.PackageUserStateHidden]\nprivate static final int HIDDEN\nprivate static final int INSTALLED\nprivate static final int INSTANT_APP\nprivate static final int NOT_LAUNCHED\nprivate static final int STOPPED\nprivate static final int SUSPENDED\nprivate static final int VIRTUAL_PRELOAD\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
+ inputSignatures = "private int mBooleans\nprivate final long mCeDataInode\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mDisabledComponents\nprivate final @android.content.pm.PackageManager.DistractionRestriction int mDistractionFlags\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mEnabledComponents\nprivate final int mEnabledState\nprivate final @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate final @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate final @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate final @android.annotation.NonNull android.content.pm.overlay.OverlayPaths mOverlayPaths\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate final @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate final @android.annotation.Nullable java.lang.String mSplashScreenTheme\npublic static android.content.pm.pkg.PackageUserState copy(android.content.pm.pkg.PackageUserState)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isSuspended()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\nclass UserStateImpl extends java.lang.Object implements [android.content.pm.pkg.PackageUserState]\nprivate static final int HIDDEN\nprivate static final int INSTALLED\nprivate static final int INSTANT_APP\nprivate static final int NOT_LAUNCHED\nprivate static final int STOPPED\nprivate static final int SUSPENDED\nprivate static final int VIRTUAL_PRELOAD\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
@Deprecated
private void __metadata() {}
@@ -589,7 +579,7 @@
}
@DataClass.Generated.Member
- public long getLongVersionCode() {
+ public long getVersionCode() {
return mLongVersionCode;
}
@@ -639,11 +629,6 @@
}
@DataClass.Generated.Member
- public @Nullable String getSeInfoOverride() {
- return mSeInfoOverride;
- }
-
- @DataClass.Generated.Member
public @NonNull long[] getLastPackageUsageTime() {
return mLastPackageUsageTime;
}
@@ -654,11 +639,6 @@
}
@DataClass.Generated.Member
- public @NonNull int[] getUserIds() {
- return mUserIds;
- }
-
- @DataClass.Generated.Member
public @NonNull SparseArray<PackageUserState> getUserStates() {
return mUserStates;
}
@@ -670,10 +650,10 @@
}
@DataClass.Generated(
- time = 1630604891337L,
+ time = 1633375703038L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java",
- inputSignatures = "private int mBooleans\nprivate final @android.annotation.Nullable com.android.server.pm.pkg.AndroidPackageApi mAndroidPackage\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mVolumeUuid\nprivate final int mAppId\nprivate final int mCategoryOverride\nprivate final @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate final long mFirstInstallTime\nprivate final long mLastModifiedTime\nprivate final long mLastUpdateTime\nprivate final long mLongVersionCode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mMimeGroups\nprivate final @android.annotation.NonNull java.io.File mPath\nprivate final @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.Integer mSharedUserId\nprivate final @android.annotation.NonNull java.lang.String[] mUsesStaticLibraries\nprivate final @android.annotation.NonNull long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mUsesLibraryInfos\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesLibraryFiles\nprivate final @android.annotation.Nullable java.lang.String mSeInfoOverride\nprivate final @android.annotation.NonNull long[] mLastPackageUsageTime\nprivate final @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate final @android.annotation.NonNull int[] mUserIds\nprivate final @android.annotation.NonNull android.util.SparseArray<android.content.pm.pkg.PackageUserState> mUserStates\npublic static com.android.server.pm.pkg.PackageState copy(com.android.server.pm.PackageSetting)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @android.annotation.Nullable @java.lang.Override android.content.pm.pkg.PackageUserState getUserState(int)\npublic @java.lang.Override boolean isExternalStorage()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isOdm()\npublic @java.lang.Override boolean isOem()\npublic @java.lang.Override boolean isPrivileged()\npublic @java.lang.Override boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic @java.lang.Override boolean isSystem()\npublic @java.lang.Override boolean isSystemExt()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isVendor()\nclass PackageStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageState]\nprivate static final int SYSTEM\nprivate static final int EXTERNAL_STORAGE\nprivate static final int PRIVILEGED\nprivate static final int OEM\nprivate static final int VENDOR\nprivate static final int PRODUCT\nprivate static final int SYSTEM_EXT\nprivate static final int REQUIRED_FOR_SYSTEM_USER\nprivate static final int ODM\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nprivate static final int HIDDEN_UNTIL_INSTALLED\nprivate static final int INSTALL_PERMISSIONS_FIXED\nprivate static final int UPDATE_AVAILABLE\nprivate static final int UPDATED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
+ inputSignatures = "private int mBooleans\nprivate final @android.annotation.Nullable com.android.server.pm.pkg.AndroidPackageApi mAndroidPackage\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mVolumeUuid\nprivate final int mAppId\nprivate final int mCategoryOverride\nprivate final @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate final long mFirstInstallTime\nprivate final long mLastModifiedTime\nprivate final long mLastUpdateTime\nprivate final long mLongVersionCode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mMimeGroups\nprivate final @android.annotation.NonNull java.io.File mPath\nprivate final @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.Integer mSharedUserId\nprivate final @android.annotation.NonNull java.lang.String[] mUsesStaticLibraries\nprivate final @android.annotation.NonNull long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mUsesLibraryInfos\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesLibraryFiles\nprivate final @android.annotation.NonNull long[] mLastPackageUsageTime\nprivate final @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate final @android.annotation.NonNull android.util.SparseArray<android.content.pm.pkg.PackageUserState> mUserStates\npublic static com.android.server.pm.pkg.PackageState copy(com.android.server.pm.PackageSetting)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isExternalStorage()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isOdm()\npublic @java.lang.Override boolean isOem()\npublic @java.lang.Override boolean isPrivileged()\npublic @java.lang.Override boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic @java.lang.Override boolean isSystem()\npublic @java.lang.Override boolean isSystemExt()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isVendor()\nclass PackageStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageState]\nprivate static final int SYSTEM\nprivate static final int EXTERNAL_STORAGE\nprivate static final int PRIVILEGED\nprivate static final int OEM\nprivate static final int VENDOR\nprivate static final int PRODUCT\nprivate static final int SYSTEM_EXT\nprivate static final int REQUIRED_FOR_SYSTEM_USER\nprivate static final int ODM\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nprivate static final int HIDDEN_UNTIL_INSTALLED\nprivate static final int INSTALL_PERMISSIONS_FIXED\nprivate static final int UPDATE_AVAILABLE\nprivate static final int UPDATED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
new file mode 100644
index 0000000..1155132
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.SigningDetails;
+import android.util.SparseArray;
+
+import com.android.server.pm.InstallSource;
+import com.android.server.pm.SharedUserSetting;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import java.util.UUID;
+
+/**
+ * Exposes internal types for internal usage of {@link PackageState}.
+ */
+public interface PackageStateInternal extends PackageState {
+
+ @NonNull
+ AndroidPackage getPkg();
+
+ // TODO: Remove in favor of exposing APIs directly?
+ @NonNull
+ PackageStateUnserialized getTransientState();
+
+ @NonNull
+ UUID getDomainSetId();
+
+ @NonNull
+ SigningDetails getSigningDetails();
+
+ @NonNull
+ InstallSource getInstallSource();
+
+ @Nullable
+ SharedUserSetting getSharedUser();
+
+ // TODO: Remove this in favor of boolean APIs
+ int getPkgFlags();
+ int getPkgPrivateFlags();
+
+ @NonNull
+ SparseArray<? extends PackageUserStateInternal> getUserStates();
+
+ /**
+ * @return the result of {@link #getUserStates()}.get(userId) or
+ * {@link PackageUserState#DEFAULT} if the state doesn't exist.
+ */
+ @NonNull
+ default PackageUserStateInternal getUserStateOrDefault(@UserIdInt int userId) {
+ PackageUserStateInternal userState = getUserStates().get(userId);
+ return userState == null ? PackageUserStateInternal.DEFAULT : userState;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java b/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
new file mode 100644
index 0000000..c1d760e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
@@ -0,0 +1,48 @@
+/*
+ * 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.pm.pkg;
+
+import android.content.pm.PackageManager;
+
+public class PackageStateUtils {
+
+ public static boolean isMatch(PackageState packageState, int flags) {
+ if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
+ return packageState.isSystem();
+ }
+ return true;
+ }
+
+ public static int[] queryInstalledUsers(PackageStateInternal pkgState, int[] users,
+ boolean installed) {
+ int num = 0;
+ for (int user : users) {
+ if (pkgState.getUserStateOrDefault(user).isInstalled() == installed) {
+ num++;
+ }
+ }
+ int[] res = new int[num];
+ num = 0;
+ for (int user : users) {
+ if (pkgState.getUserStateOrDefault(user).isInstalled() == installed) {
+ res[num] = user;
+ num++;
+ }
+ }
+ return res;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserState.java b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
new file mode 100644
index 0000000..147edf7
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
@@ -0,0 +1,153 @@
+/*
+ * 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.pm.pkg;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageManager;
+import android.content.pm.overlay.OverlayPaths;
+import android.content.pm.pkg.FrameworkPackageUserState;
+import android.os.UserHandle;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The API surface for {@link PackageUserStateInternal}, for use by in-process mainline consumers.
+ *
+ * The parent of this class is {@link PackageState}, which handles non-user state, exposing this
+ * interface for per-user state.
+ */
+// TODO(b/173807334): Expose API
+//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public interface PackageUserState extends FrameworkPackageUserState {
+
+ PackageUserState DEFAULT = PackageUserStateInternal.DEFAULT;
+
+ /**
+ * Combination of {@link #getOverlayPaths()} and {@link #getSharedLibraryOverlayPaths()}
+ */
+ @Nullable
+ OverlayPaths getAllOverlayPaths();
+
+ /**
+ * Credential encrypted /data partition inode.
+ */
+ long getCeDataInode();
+
+ /**
+ * Fully qualified class names of components explicitly disabled.
+ */
+ @NonNull
+ Set<String> getDisabledComponents();
+
+ @PackageManager.DistractionRestriction
+ int getDistractionFlags();
+
+ /**
+ * Fully qualified class names of components explicitly enabled.
+ */
+ @NonNull
+ Set<String> getEnabledComponents();
+
+ /**
+ * Retrieve the effective enabled state of the package itself.
+ */
+ @PackageManager.EnabledState
+ int getEnabledState();
+
+ /**
+ * @see PackageManager#setHarmfulAppWarning(String, CharSequence)
+ */
+ @Nullable
+ String getHarmfulAppWarning();
+
+ @PackageManager.InstallReason
+ int getInstallReason();
+
+ /**
+ * Tracks the last calling package to set a specific enabled state for the package.
+ */
+ @Nullable
+ String getLastDisableAppCaller();
+
+ @Nullable
+ OverlayPaths getOverlayPaths();
+
+ @NonNull
+ Map<String, OverlayPaths> getSharedLibraryOverlayPaths();
+
+ @PackageManager.UninstallReason
+ int getUninstallReason();
+
+ /**
+ * @return whether the given fully qualified class name is explicitly enabled
+ */
+ boolean isComponentEnabled(@NonNull String componentName);
+
+ /**
+ * @return {@link #isComponentEnabled(String)} but for explicitly disabled
+ */
+ boolean isComponentDisabled(@NonNull String componentName);
+
+ /**
+ * @see PackageManager#setApplicationHiddenSettingAsUser(String, boolean, UserHandle)
+ */
+ boolean isHidden();
+
+ /**
+ * @return whether the package is marked as installed for all users
+ */
+ boolean isInstalled();
+
+ /**
+ * @return whether the package is marked as an ephemeral app, which restricts permissions,
+ * features, visibility
+ */
+ boolean isInstantApp();
+
+ /**
+ * @return whether the package has not been launched since being explicitly stopped
+ */
+ boolean isNotLaunched();
+
+ /**
+ * @return whether the package has been stopped, which can occur if it's force-stopped, data
+ * cleared, or just been installed
+ */
+ boolean isStopped();
+
+ /**
+ * @return whether the package has been suspended, maybe by the device admin, disallowing its
+ * launch
+ */
+ boolean isSuspended();
+
+ /**
+ * @return whether the package was installed as a virtual preload, which may be done as part
+ * of device infrastructure auto installation outside of the initial device image
+ */
+ boolean isVirtualPreload();
+
+ /**
+ * The "package:type/entry" form of the theme resource ID previously set as the splash screen.
+ * @see android.window.SplashScreen#setSplashScreenTheme(int)
+ * @see android.content.res.Resources#getResourceName(int)
+ */
+ @Nullable
+ String getSplashScreenTheme();
+}
diff --git a/core/java/android/content/pm/pkg/PackageUserStateDefault.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
similarity index 79%
copy from core/java/android/content/pm/pkg/PackageUserStateDefault.java
copy to services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
index 6bee8c8..481c3e0 100644
--- a/core/java/android/content/pm/pkg/PackageUserStateDefault.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
@@ -14,18 +14,22 @@
* limitations under the License.
*/
-package android.content.pm.pkg;
+package com.android.server.pm.pkg;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.overlay.OverlayPaths;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Pair;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
-class PackageUserStateDefault implements PackageUserState {
+class PackageUserStateDefault implements PackageUserStateInternal {
@Override
public int getEnabledState() {
@@ -139,8 +143,34 @@
return false;
}
+ @Nullable
@Override
public OverlayPaths getAllOverlayPaths() {
return null;
}
+
+ @Nullable
+ @Override
+ public ArrayMap<String, SuspendParams> getSuspendParams() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public ArraySet<String> getDisabledComponentsNoCopy() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public ArraySet<String> getEnabledComponentsNoCopy() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public Pair<String, Integer> getOverrideLabelIconForComponent(
+ @NonNull ComponentName componentName) {
+ return null;
+ }
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
new file mode 100644
index 0000000..32a9cf1
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
@@ -0,0 +1,595 @@
+/*
+ * 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.pm.pkg;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.overlay.OverlayPaths;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Pair;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.DataClass;
+
+import java.util.Objects;
+
+@DataClass(genConstructor = false, genBuilder = false, genEqualsHashCode = true)
+@DataClass.Suppress({"mOverlayPathsLock", "mOverlayPaths", "mSharedLibraryOverlayPathsLock",
+ "mSharedLibraryOverlayPaths", "setOverlayPaths", "mCachedOverlayPathsLock",
+ "mCachedOverlayPaths", "setCachedOverlayPaths"})
+public class PackageUserStateImpl implements PackageUserStateInternal {
+
+ @Nullable
+ protected ArraySet<String> mDisabledComponents;
+ @Nullable
+ protected ArraySet<String> mEnabledComponents;
+
+ private long mCeDataInode;
+ private boolean mInstalled = true;
+ private boolean mStopped;
+ private boolean mNotLaunched;
+ private boolean mHidden; // Is the app restricted by owner / admin
+ private int mDistractionFlags;
+ private boolean mSuspended;
+ private boolean mInstantApp;
+ private boolean mVirtualPreload;
+ private int mEnabledState = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+ @PackageManager.InstallReason
+ private int mInstallReason = PackageManager.INSTALL_REASON_UNKNOWN;
+ @PackageManager.UninstallReason
+ private int mUninstallReason = PackageManager.UNINSTALL_REASON_UNKNOWN;
+ @Nullable
+ private String mHarmfulAppWarning;
+ @Nullable
+ private String mLastDisableAppCaller;
+
+ @Nullable
+ protected OverlayPaths mOverlayPaths;
+
+ // Lib name to overlay paths
+ @Nullable
+ protected ArrayMap<String, OverlayPaths> mSharedLibraryOverlayPaths;
+
+ @Nullable
+ private String mSplashScreenTheme;
+
+ /** Suspending package to suspend params */
+ @Nullable
+ private ArrayMap<String, SuspendParams> mSuspendParams;
+
+ @Nullable
+ private OverlayPaths mCachedOverlayPaths;
+
+ @Nullable
+ private ArrayMap<ComponentName, Pair<String, Integer>> mComponentLabelIconOverrideMap;
+
+ public PackageUserStateImpl() {
+ super();
+ }
+
+ public PackageUserStateImpl(PackageUserStateImpl other) {
+ mDisabledComponents = ArrayUtils.cloneOrNull(other.mDisabledComponents);
+ mEnabledComponents = ArrayUtils.cloneOrNull(other.mEnabledComponents);
+ mOverlayPaths = other.mOverlayPaths;
+ if (other.mSharedLibraryOverlayPaths != null) {
+ mSharedLibraryOverlayPaths = new ArrayMap<>(other.mSharedLibraryOverlayPaths);
+ }
+ mDisabledComponents = other.mDisabledComponents;
+ mEnabledComponents = other.mEnabledComponents;
+ mCeDataInode = other.mCeDataInode;
+ mInstalled = other.mInstalled;
+ mStopped = other.mStopped;
+ mNotLaunched = other.mNotLaunched;
+ mHidden = other.mHidden;
+ mDistractionFlags = other.mDistractionFlags;
+ mSuspended = other.mSuspended;
+ mInstantApp = other.mInstantApp;
+ mVirtualPreload = other.mVirtualPreload;
+ mEnabledState = other.mEnabledState;
+ mInstallReason = other.mInstallReason;
+ mUninstallReason = other.mUninstallReason;
+ mHarmfulAppWarning = other.mHarmfulAppWarning;
+ mLastDisableAppCaller = other.mLastDisableAppCaller;
+ mOverlayPaths = other.mOverlayPaths;
+ mSharedLibraryOverlayPaths = other.mSharedLibraryOverlayPaths;
+ mSplashScreenTheme = other.mSplashScreenTheme;
+ mSuspendParams = other.mSuspendParams == null ? null : new ArrayMap<>(other.mSuspendParams);
+ mComponentLabelIconOverrideMap = other.mComponentLabelIconOverrideMap == null ? null
+ : new ArrayMap<>(other.mComponentLabelIconOverrideMap);
+ }
+
+ /**
+ * Sets the path of overlays currently enabled for this package and user combination.
+ * @return true if the path contents differ than what they were previously
+ */
+ @Nullable
+ public boolean setOverlayPaths(@Nullable OverlayPaths paths) {
+ if (Objects.equals(paths, mOverlayPaths)) {
+ return false;
+ }
+ if ((mOverlayPaths == null && paths.isEmpty())
+ || (paths == null && mOverlayPaths.isEmpty())) {
+ return false;
+ }
+ mOverlayPaths = paths;
+ mCachedOverlayPaths = null;
+ return true;
+ }
+
+ /**
+ * Sets the path of overlays currently enabled for a library that this package uses.
+ *
+ * @return true if the path contents for the library differ than what they were previously
+ */
+ public boolean setSharedLibraryOverlayPaths(@NonNull String library,
+ @Nullable OverlayPaths paths) {
+ if (mSharedLibraryOverlayPaths == null) {
+ mSharedLibraryOverlayPaths = new ArrayMap<>();
+ }
+ final OverlayPaths currentPaths = mSharedLibraryOverlayPaths.get(library);
+ if (Objects.equals(paths, currentPaths)) {
+ return false;
+ }
+ mCachedOverlayPaths = null;
+ if (paths == null || paths.isEmpty()) {
+ return mSharedLibraryOverlayPaths.remove(library) != null;
+ } else {
+ mSharedLibraryOverlayPaths.put(library, paths);
+ return true;
+ }
+ }
+
+ @Nullable
+ @Override
+ public ArraySet<String> getDisabledComponentsNoCopy() {
+ return mDisabledComponents;
+ }
+
+ @Nullable
+ @Override
+ public ArraySet<String> getEnabledComponentsNoCopy() {
+ return mEnabledComponents;
+ }
+
+ @Override
+ public boolean isComponentEnabled(String componentName) {
+ // TODO: Not locked
+ return ArrayUtils.contains(mEnabledComponents, componentName);
+ }
+
+ @Override
+ public boolean isComponentDisabled(String componentName) {
+ // TODO: Not locked
+ return ArrayUtils.contains(mDisabledComponents, componentName);
+ }
+
+ @Override
+ public OverlayPaths getAllOverlayPaths() {
+ if (mOverlayPaths == null && mSharedLibraryOverlayPaths == null) {
+ return null;
+ }
+ final OverlayPaths.Builder newPaths = new OverlayPaths.Builder();
+ newPaths.addAll(mOverlayPaths);
+ if (mSharedLibraryOverlayPaths != null) {
+ for (final OverlayPaths libOverlayPaths : mSharedLibraryOverlayPaths.values()) {
+ newPaths.addAll(libOverlayPaths);
+ }
+ }
+ return newPaths.build();
+ }
+
+ /**
+ * Overrides the non-localized label and icon of a component.
+ *
+ * @return true if the label or icon was changed.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public boolean overrideLabelAndIcon(@NonNull ComponentName component,
+ @Nullable String nonLocalizedLabel, @Nullable Integer icon) {
+ String existingLabel = null;
+ Integer existingIcon = null;
+
+ if (mComponentLabelIconOverrideMap != null) {
+ Pair<String, Integer> pair = mComponentLabelIconOverrideMap.get(component);
+ if (pair != null) {
+ existingLabel = pair.first;
+ existingIcon = pair.second;
+ }
+ }
+
+ boolean changed = !TextUtils.equals(existingLabel, nonLocalizedLabel)
+ || !Objects.equals(existingIcon, icon);
+
+ if (changed) {
+ if (nonLocalizedLabel == null && icon == null) {
+ mComponentLabelIconOverrideMap.remove(component);
+ if (mComponentLabelIconOverrideMap.isEmpty()) {
+ mComponentLabelIconOverrideMap = null;
+ }
+ } else {
+ if (mComponentLabelIconOverrideMap == null) {
+ mComponentLabelIconOverrideMap = new ArrayMap<>(1);
+ }
+
+ mComponentLabelIconOverrideMap.put(component, Pair.create(nonLocalizedLabel, icon));
+ }
+ }
+
+ return changed;
+ }
+
+ /**
+ * Clears all values previously set by {@link #overrideLabelAndIcon(ComponentName,
+ * String, Integer)}.
+ *
+ * This is done when the package is updated as the components and resource IDs may have changed.
+ */
+ public void resetOverrideComponentLabelIcon() {
+ mComponentLabelIconOverrideMap = null;
+ }
+
+ @Nullable
+ public Pair<String, Integer> getOverrideLabelIconForComponent(ComponentName componentName) {
+ if (ArrayUtils.isEmpty(mComponentLabelIconOverrideMap)) {
+ return null;
+ }
+
+ return mComponentLabelIconOverrideMap.get(componentName);
+ }
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ public @Nullable ArraySet<String> getDisabledComponents() {
+ return mDisabledComponents;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable ArraySet<String> getEnabledComponents() {
+ return mEnabledComponents;
+ }
+
+ @DataClass.Generated.Member
+ public long getCeDataInode() {
+ return mCeDataInode;
+ }
+
+ @DataClass.Generated.Member
+ public boolean isInstalled() {
+ return mInstalled;
+ }
+
+ @DataClass.Generated.Member
+ public boolean isStopped() {
+ return mStopped;
+ }
+
+ @DataClass.Generated.Member
+ public boolean isNotLaunched() {
+ return mNotLaunched;
+ }
+
+ @DataClass.Generated.Member
+ public boolean isHidden() {
+ return mHidden;
+ }
+
+ @DataClass.Generated.Member
+ public int getDistractionFlags() {
+ return mDistractionFlags;
+ }
+
+ @DataClass.Generated.Member
+ public boolean isSuspended() {
+ return mSuspended;
+ }
+
+ @DataClass.Generated.Member
+ public boolean isInstantApp() {
+ return mInstantApp;
+ }
+
+ @DataClass.Generated.Member
+ public boolean isVirtualPreload() {
+ return mVirtualPreload;
+ }
+
+ @DataClass.Generated.Member
+ public int getEnabledState() {
+ return mEnabledState;
+ }
+
+ @DataClass.Generated.Member
+ public @PackageManager.InstallReason int getInstallReason() {
+ return mInstallReason;
+ }
+
+ @DataClass.Generated.Member
+ public @PackageManager.UninstallReason int getUninstallReason() {
+ return mUninstallReason;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable String getHarmfulAppWarning() {
+ return mHarmfulAppWarning;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable String getLastDisableAppCaller() {
+ return mLastDisableAppCaller;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable OverlayPaths getOverlayPaths() {
+ return mOverlayPaths;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable ArrayMap<String,OverlayPaths> getSharedLibraryOverlayPaths() {
+ return mSharedLibraryOverlayPaths;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable String getSplashScreenTheme() {
+ return mSplashScreenTheme;
+ }
+
+ /**
+ * Suspending package to suspend params
+ */
+ @DataClass.Generated.Member
+ public @Nullable ArrayMap<String,SuspendParams> getSuspendParams() {
+ return mSuspendParams;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable OverlayPaths getCachedOverlayPaths() {
+ return mCachedOverlayPaths;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable ArrayMap<ComponentName,Pair<String,Integer>> getComponentLabelIconOverrideMap() {
+ return mComponentLabelIconOverrideMap;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull PackageUserStateImpl setDisabledComponents(@NonNull ArraySet<String> value) {
+ mDisabledComponents = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull PackageUserStateImpl setEnabledComponents(@NonNull ArraySet<String> value) {
+ mEnabledComponents = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull PackageUserStateImpl setCeDataInode( long value) {
+ mCeDataInode = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull PackageUserStateImpl setInstalled( boolean value) {
+ mInstalled = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull PackageUserStateImpl setStopped( boolean value) {
+ mStopped = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull PackageUserStateImpl setNotLaunched( boolean value) {
+ mNotLaunched = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull PackageUserStateImpl setHidden( boolean value) {
+ mHidden = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull PackageUserStateImpl setDistractionFlags( int value) {
+ mDistractionFlags = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull PackageUserStateImpl setSuspended( boolean value) {
+ mSuspended = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull PackageUserStateImpl setInstantApp( boolean value) {
+ mInstantApp = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull PackageUserStateImpl setVirtualPreload( boolean value) {
+ mVirtualPreload = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull PackageUserStateImpl setEnabledState( int value) {
+ mEnabledState = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull PackageUserStateImpl setInstallReason(@PackageManager.InstallReason int value) {
+ mInstallReason = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ PackageManager.InstallReason.class, null, mInstallReason);
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull PackageUserStateImpl setUninstallReason(@PackageManager.UninstallReason int value) {
+ mUninstallReason = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ PackageManager.UninstallReason.class, null, mUninstallReason);
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull PackageUserStateImpl setHarmfulAppWarning(@NonNull String value) {
+ mHarmfulAppWarning = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull PackageUserStateImpl setLastDisableAppCaller(@NonNull String value) {
+ mLastDisableAppCaller = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull PackageUserStateImpl setSharedLibraryOverlayPaths(@NonNull ArrayMap<String,OverlayPaths> value) {
+ mSharedLibraryOverlayPaths = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull PackageUserStateImpl setSplashScreenTheme(@NonNull String value) {
+ mSplashScreenTheme = value;
+ return this;
+ }
+
+ /**
+ * Suspending package to suspend params
+ */
+ @DataClass.Generated.Member
+ public @NonNull PackageUserStateImpl setSuspendParams(@NonNull ArrayMap<String,SuspendParams> value) {
+ mSuspendParams = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull PackageUserStateImpl setComponentLabelIconOverrideMap(@NonNull ArrayMap<ComponentName,Pair<String,Integer>> value) {
+ mComponentLabelIconOverrideMap = value;
+ return this;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(PackageUserStateImpl other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ PackageUserStateImpl that = (PackageUserStateImpl) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && Objects.equals(mDisabledComponents, that.mDisabledComponents)
+ && Objects.equals(mEnabledComponents, that.mEnabledComponents)
+ && mCeDataInode == that.mCeDataInode
+ && mInstalled == that.mInstalled
+ && mStopped == that.mStopped
+ && mNotLaunched == that.mNotLaunched
+ && mHidden == that.mHidden
+ && mDistractionFlags == that.mDistractionFlags
+ && mSuspended == that.mSuspended
+ && mInstantApp == that.mInstantApp
+ && mVirtualPreload == that.mVirtualPreload
+ && mEnabledState == that.mEnabledState
+ && mInstallReason == that.mInstallReason
+ && mUninstallReason == that.mUninstallReason
+ && Objects.equals(mHarmfulAppWarning, that.mHarmfulAppWarning)
+ && Objects.equals(mLastDisableAppCaller, that.mLastDisableAppCaller)
+ && Objects.equals(mOverlayPaths, that.mOverlayPaths)
+ && Objects.equals(mSharedLibraryOverlayPaths, that.mSharedLibraryOverlayPaths)
+ && Objects.equals(mSplashScreenTheme, that.mSplashScreenTheme)
+ && Objects.equals(mSuspendParams, that.mSuspendParams)
+ && Objects.equals(mCachedOverlayPaths, that.mCachedOverlayPaths)
+ && Objects.equals(mComponentLabelIconOverrideMap, that.mComponentLabelIconOverrideMap);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + Objects.hashCode(mDisabledComponents);
+ _hash = 31 * _hash + Objects.hashCode(mEnabledComponents);
+ _hash = 31 * _hash + Long.hashCode(mCeDataInode);
+ _hash = 31 * _hash + Boolean.hashCode(mInstalled);
+ _hash = 31 * _hash + Boolean.hashCode(mStopped);
+ _hash = 31 * _hash + Boolean.hashCode(mNotLaunched);
+ _hash = 31 * _hash + Boolean.hashCode(mHidden);
+ _hash = 31 * _hash + mDistractionFlags;
+ _hash = 31 * _hash + Boolean.hashCode(mSuspended);
+ _hash = 31 * _hash + Boolean.hashCode(mInstantApp);
+ _hash = 31 * _hash + Boolean.hashCode(mVirtualPreload);
+ _hash = 31 * _hash + mEnabledState;
+ _hash = 31 * _hash + mInstallReason;
+ _hash = 31 * _hash + mUninstallReason;
+ _hash = 31 * _hash + Objects.hashCode(mHarmfulAppWarning);
+ _hash = 31 * _hash + Objects.hashCode(mLastDisableAppCaller);
+ _hash = 31 * _hash + Objects.hashCode(mOverlayPaths);
+ _hash = 31 * _hash + Objects.hashCode(mSharedLibraryOverlayPaths);
+ _hash = 31 * _hash + Objects.hashCode(mSplashScreenTheme);
+ _hash = 31 * _hash + Objects.hashCode(mSuspendParams);
+ _hash = 31 * _hash + Objects.hashCode(mCachedOverlayPaths);
+ _hash = 31 * _hash + Objects.hashCode(mComponentLabelIconOverrideMap);
+ return _hash;
+ }
+
+ @DataClass.Generated(
+ time = 1633983318771L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java",
+ inputSignatures = "protected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mDisabledComponents\nprotected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate long mCeDataInode\nprivate boolean mInstalled\nprivate boolean mStopped\nprivate boolean mNotLaunched\nprivate boolean mHidden\nprivate int mDistractionFlags\nprivate boolean mSuspended\nprivate boolean mInstantApp\nprivate boolean mVirtualPreload\nprivate int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprotected @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.annotation.Nullable android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mCachedOverlayPaths\nprivate @android.annotation.Nullable android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\nclass PackageUserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserStateInternal]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/content/pm/pkg/PackageUserStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java
similarity index 75%
rename from core/java/android/content/pm/pkg/PackageUserStateInternal.java
rename to services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java
index b3f849b..6f33312 100644
--- a/core/java/android/content/pm/pkg/PackageUserStateInternal.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.content.pm.pkg;
+package com.android.server.pm.pkg;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -23,10 +23,14 @@
import android.util.ArraySet;
import android.util.Pair;
-/** @hide */
+/**
+ * Internal variant of {@link PackageUserState} that includes data not exposed as API. This is
+ * still read-only and should be used inside system server code when possible over the
+ * implementation.
+ */
public interface PackageUserStateInternal extends PackageUserState {
- PackageUserStateInternal DEFAULT = new PackageUserStateInternalDefault();
+ PackageUserStateInternal DEFAULT = new PackageUserStateDefault();
@Nullable
ArrayMap<String, SuspendParams> getSuspendParams();
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateInternalImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateInternalImpl.java
deleted file mode 100644
index d5f8dbf..0000000
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateInternalImpl.java
+++ /dev/null
@@ -1,253 +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.pm.pkg;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.pm.overlay.OverlayPaths;
-import android.content.pm.pkg.PackageUserState;
-import android.content.pm.pkg.PackageUserStateImpl;
-import android.content.pm.pkg.PackageUserStateInternal;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Pair;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.DataClass;
-
-import java.util.Objects;
-
-@DataClass(genConstructor = false, genBuilder = false, genEqualsHashCode = true)
-@DataClass.Suppress({"mCachedOverlayPathsLock", "mCachedOverlayPaths", "setCachedOverlayPaths"})
-public class PackageUserStateInternalImpl extends PackageUserStateImpl implements
- PackageUserStateInternal {
-
- /** Suspending package to suspend params */
- @Nullable
- private ArrayMap<String, PackageUserState.SuspendParams> mSuspendParams;
-
- @Nullable
- private OverlayPaths mCachedOverlayPaths;
-
- @Nullable
- private ArrayMap<ComponentName, Pair<String, Integer>> mComponentLabelIconOverrideMap;
-
- public PackageUserStateInternalImpl() {
- super();
- }
-
- public PackageUserStateInternalImpl(PackageUserStateInternalImpl other) {
- super(other);
- mSuspendParams = other.mSuspendParams == null ? null : new ArrayMap<>(other.mSuspendParams);
- mComponentLabelIconOverrideMap = other.mComponentLabelIconOverrideMap == null ? null
- : new ArrayMap<>(other.mComponentLabelIconOverrideMap);
- }
-
- /**
- * Sets the path of overlays currently enabled for this package and user combination.
- * @return true if the path contents differ than what they were previously
- */
- @Nullable
- public boolean setOverlayPaths(@Nullable OverlayPaths paths) {
- if (Objects.equals(paths, mOverlayPaths)) {
- return false;
- }
- if ((mOverlayPaths == null && paths.isEmpty())
- || (paths == null && mOverlayPaths.isEmpty())) {
- return false;
- }
- mOverlayPaths = paths;
- mCachedOverlayPaths = null;
- return true;
- }
-
- /**
- * Sets the path of overlays currently enabled for a library that this package uses.
- *
- * @return true if the path contents for the library differ than what they were previously
- */
- public boolean setSharedLibraryOverlayPaths(@NonNull String library,
- @Nullable OverlayPaths paths) {
- if (mSharedLibraryOverlayPaths == null) {
- mSharedLibraryOverlayPaths = new ArrayMap<>();
- }
- final OverlayPaths currentPaths = mSharedLibraryOverlayPaths.get(library);
- if (Objects.equals(paths, currentPaths)) {
- return false;
- }
- mCachedOverlayPaths = null;
- if (paths == null || paths.isEmpty()) {
- return mSharedLibraryOverlayPaths.remove(library) != null;
- } else {
- mSharedLibraryOverlayPaths.put(library, paths);
- return true;
- }
- }
-
- @Nullable
- @Override
- public ArraySet<String> getDisabledComponentsNoCopy() {
- return mDisabledComponents;
- }
-
- @Nullable
- @Override
- public ArraySet<String> getEnabledComponentsNoCopy() {
- return mEnabledComponents;
- }
-
- /**
- * Overrides the non-localized label and icon of a component.
- *
- * @return true if the label or icon was changed.
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public boolean overrideLabelAndIcon(@NonNull ComponentName component,
- @Nullable String nonLocalizedLabel, @Nullable Integer icon) {
- String existingLabel = null;
- Integer existingIcon = null;
-
- if (mComponentLabelIconOverrideMap != null) {
- Pair<String, Integer> pair = mComponentLabelIconOverrideMap.get(component);
- if (pair != null) {
- existingLabel = pair.first;
- existingIcon = pair.second;
- }
- }
-
- boolean changed = !TextUtils.equals(existingLabel, nonLocalizedLabel)
- || !Objects.equals(existingIcon, icon);
-
- if (changed) {
- if (nonLocalizedLabel == null && icon == null) {
- mComponentLabelIconOverrideMap.remove(component);
- if (mComponentLabelIconOverrideMap.isEmpty()) {
- mComponentLabelIconOverrideMap = null;
- }
- } else {
- if (mComponentLabelIconOverrideMap == null) {
- mComponentLabelIconOverrideMap = new ArrayMap<>(1);
- }
-
- mComponentLabelIconOverrideMap.put(component, Pair.create(nonLocalizedLabel, icon));
- }
- }
-
- return changed;
- }
-
- /**
- * Clears all values previously set by {@link #overrideLabelAndIcon(ComponentName,
- * String, Integer)}.
- *
- * This is done when the package is updated as the components and resource IDs may have changed.
- */
- public void resetOverrideComponentLabelIcon() {
- mComponentLabelIconOverrideMap = null;
- }
-
- @Nullable
- public Pair<String, Integer> getOverrideLabelIconForComponent(ComponentName componentName) {
- if (ArrayUtils.isEmpty(mComponentLabelIconOverrideMap)) {
- return null;
- }
-
- return mComponentLabelIconOverrideMap.get(componentName);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof PackageUserStateInternalImpl)) return false;
- if (!super.equals(o)) return false;
- PackageUserStateInternalImpl that = (PackageUserStateInternalImpl) o;
- return Objects.equals(mSuspendParams, that.mSuspendParams)
- && Objects.equals(mCachedOverlayPaths, that.mCachedOverlayPaths)
- && Objects.equals(mComponentLabelIconOverrideMap,
- that.mComponentLabelIconOverrideMap);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(super.hashCode(), mSuspendParams, mCachedOverlayPaths,
- mComponentLabelIconOverrideMap);
- }
-
-
-
- // Code below generated by codegen v1.0.23.
- //
- // DO NOT MODIFY!
- // CHECKSTYLE:OFF Generated code
- //
- // To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateInternalImpl.java
- //
- // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
- // Settings > Editor > Code Style > Formatter Control
- //@formatter:off
-
-
- /**
- * Suspending package to suspend params
- */
- @DataClass.Generated.Member
- public @Nullable ArrayMap<String,PackageUserState.SuspendParams> getSuspendParams() {
- return mSuspendParams;
- }
-
- @DataClass.Generated.Member
- public @Nullable OverlayPaths getCachedOverlayPaths() {
- return mCachedOverlayPaths;
- }
-
- @DataClass.Generated.Member
- public @Nullable ArrayMap<ComponentName,Pair<String,Integer>> getComponentLabelIconOverrideMap() {
- return mComponentLabelIconOverrideMap;
- }
-
- /**
- * Suspending package to suspend params
- */
- @DataClass.Generated.Member
- public @NonNull PackageUserStateInternalImpl setSuspendParams(@NonNull ArrayMap<String,PackageUserState.SuspendParams> value) {
- mSuspendParams = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateInternalImpl setComponentLabelIconOverrideMap(@NonNull ArrayMap<ComponentName,Pair<String,Integer>> value) {
- mComponentLabelIconOverrideMap = value;
- return this;
- }
-
- @DataClass.Generated(
- time = 1626458385872L,
- codegenVersion = "1.0.23",
- sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateInternalImpl.java",
- inputSignatures = "private @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.content.pm.PackageUserState.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mCachedOverlayPaths\nprivate @android.annotation.Nullable android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\nclass PackageUserStateInternalImpl extends android.content.pm.pkg.PackageUserStateImpl implements [android.content.pm.pkg.PackageUserStateInternal, android.content.pm.pkg.PackageUserStateHidden]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false)")
- @Deprecated
- private void __metadata() {}
-
-
- //@formatter:on
- // End of generated code
-
-}
diff --git a/services/core/java/com/android/server/pm/pkg/SuspendParams.java b/services/core/java/com/android/server/pm/pkg/SuspendParams.java
new file mode 100644
index 0000000..71512dc
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/SuspendParams.java
@@ -0,0 +1,175 @@
+/*
+ * 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.pm.pkg;
+
+import android.annotation.Nullable;
+import android.content.pm.SuspendDialogInfo;
+import android.os.BaseBundle;
+import android.os.PersistableBundle;
+import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * Container to describe suspension parameters.
+ * @hide
+ */
+public final class SuspendParams {
+
+ private static final String LOG_TAG = "FrameworkPackageUserState";
+ private static final String TAG_DIALOG_INFO = "dialog-info";
+ private static final String TAG_APP_EXTRAS = "app-extras";
+ private static final String TAG_LAUNCHER_EXTRAS = "launcher-extras";
+
+ public SuspendDialogInfo dialogInfo;
+ public PersistableBundle appExtras;
+ public PersistableBundle launcherExtras;
+
+ private SuspendParams() {
+ }
+
+ /**
+ * Returns a {@link SuspendParams} object with the given fields. Returns {@code null} if all
+ * the fields are {@code null}.
+ *
+ * @return A {@link SuspendParams} object or {@code null}.
+ */
+ public static SuspendParams getInstanceOrNull(SuspendDialogInfo dialogInfo,
+ PersistableBundle appExtras, PersistableBundle launcherExtras) {
+ if (dialogInfo == null && appExtras == null && launcherExtras == null) {
+ return null;
+ }
+ final SuspendParams instance = new SuspendParams();
+ instance.dialogInfo = dialogInfo;
+ instance.appExtras = appExtras;
+ instance.launcherExtras = launcherExtras;
+ return instance;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof SuspendParams)) {
+ return false;
+ }
+ final SuspendParams other = (SuspendParams) obj;
+ if (!Objects.equals(dialogInfo, other.dialogInfo)) {
+ return false;
+ }
+ if (!BaseBundle.kindofEquals(appExtras, other.appExtras)) {
+ return false;
+ }
+ if (!BaseBundle.kindofEquals(launcherExtras, other.launcherExtras)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int hashCode = Objects.hashCode(dialogInfo);
+ hashCode = 31 * hashCode + ((appExtras != null) ? appExtras.size() : 0);
+ hashCode = 31 * hashCode + ((launcherExtras != null) ? launcherExtras.size() : 0);
+ return hashCode;
+ }
+
+ /**
+ * Serializes this object into an xml format
+ *
+ * @param out the {@link XmlSerializer} object
+ */
+ public void saveToXml(TypedXmlSerializer out) throws IOException {
+ if (dialogInfo != null) {
+ out.startTag(null, TAG_DIALOG_INFO);
+ dialogInfo.saveToXml(out);
+ out.endTag(null, TAG_DIALOG_INFO);
+ }
+ if (appExtras != null) {
+ out.startTag(null, TAG_APP_EXTRAS);
+ try {
+ appExtras.saveToXml(out);
+ } catch (XmlPullParserException e) {
+ Slog.e(LOG_TAG, "Exception while trying to write appExtras."
+ + " Will be lost on reboot", e);
+ }
+ out.endTag(null, TAG_APP_EXTRAS);
+ }
+ if (launcherExtras != null) {
+ out.startTag(null, TAG_LAUNCHER_EXTRAS);
+ try {
+ launcherExtras.saveToXml(out);
+ } catch (XmlPullParserException e) {
+ Slog.e(LOG_TAG, "Exception while trying to write launcherExtras."
+ + " Will be lost on reboot", e);
+ }
+ out.endTag(null, TAG_LAUNCHER_EXTRAS);
+ }
+ }
+
+ /**
+ * Parses this object from the xml format. Returns {@code null} if no object related
+ * information could be read.
+ *
+ * @param in the reader
+ */
+ public static SuspendParams restoreFromXml(TypedXmlPullParser in) throws IOException {
+ SuspendDialogInfo readDialogInfo = null;
+ PersistableBundle readAppExtras = null;
+ PersistableBundle readLauncherExtras = null;
+
+ final int currentDepth = in.getDepth();
+ int type;
+ try {
+ while ((type = in.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || in.getDepth() > currentDepth)) {
+ if (type == XmlPullParser.END_TAG
+ || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ switch (in.getName()) {
+ case TAG_DIALOG_INFO:
+ readDialogInfo = SuspendDialogInfo.restoreFromXml(in);
+ break;
+ case TAG_APP_EXTRAS:
+ readAppExtras = PersistableBundle.restoreFromXml(in);
+ break;
+ case TAG_LAUNCHER_EXTRAS:
+ readLauncherExtras = PersistableBundle.restoreFromXml(in);
+ break;
+ default:
+ Slog.w(LOG_TAG, "Unknown tag " + in.getName()
+ + " in SuspendParams. Ignoring");
+ break;
+ }
+ }
+ } catch (XmlPullParserException e) {
+ Slog.e(LOG_TAG, "Exception while trying to parse SuspendParams,"
+ + " some fields may default", e);
+ }
+ return getInstanceOrNull(readDialogInfo, readAppExtras, readLauncherExtras);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
index 844111a..b730ab2 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
@@ -30,8 +30,8 @@
import android.util.SparseArray;
import com.android.internal.util.CollectionUtils;
-import com.android.server.pm.PackageSetting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState;
import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
@@ -63,7 +63,7 @@
public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
@Nullable @UserIdInt Integer userId,
- @NonNull Function<String, PackageSetting> pkgSettingFunction,
+ @NonNull Function<String, PackageStateInternal> pkgSettingFunction,
@NonNull DomainVerificationStateMap<DomainVerificationPkgState> stateMap)
throws NameNotFoundException {
ArrayMap<String, Integer> reusedMap = new ArrayMap<>();
@@ -74,7 +74,7 @@
for (int index = 0; index < size; index++) {
DomainVerificationPkgState pkgState = stateMap.valueAt(index);
String pkgName = pkgState.getPackageName();
- PackageSetting pkgSetting = pkgSettingFunction.apply(pkgName);
+ PackageStateInternal pkgSetting = pkgSettingFunction.apply(pkgName);
if (pkgSetting == null || pkgSetting.getPkg() == null) {
continue;
}
@@ -90,7 +90,7 @@
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
- PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
+ PackageStateInternal pkgSetting = pkgSettingFunction.apply(packageName);
if (pkgSetting == null || pkgSetting.getPkg() == null) {
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
index 262734f..1f024ea 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
@@ -40,6 +40,7 @@
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.PackageSetting;
import com.android.server.pm.Settings;
+import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
@@ -134,7 +135,7 @@
/**
* Defines the possible values for
- * {@link #approvalLevelForDomain(PackageSetting, Intent, List, int, int)} which sorts packages
+ * {@link #approvalLevelForDomain(PackageStateInternal, Intent, int, int)} which sorts packages
* by approval priority. A higher numerical value means the package should override all lower
* values. This means that comparison using less/greater than IS valid.
*
@@ -236,11 +237,11 @@
* This will NOT call {@link #writeSettings(TypedXmlSerializer, boolean, int)}. That must be
* handled by the caller.
*/
- void addPackage(@NonNull PackageSetting newPkgSetting);
+ void addPackage(@NonNull PackageStateInternal newPkgSetting);
/**
* Migrates verification state from a previous install to a new one. It is expected that the
- * {@link PackageSetting#getDomainSetId()} already be set to the correct value, usually from
+ * {@link PackageStateInternal#getDomainSetId()} already be set to the correct value, usually from
* {@link #generateNewId()}. This will preserve {@link DomainVerificationState#STATE_SUCCESS}
* domains under the assumption that the new package will pass the same server side config as
* the previous package, as they have matching signatures.
@@ -251,7 +252,8 @@
* This will NOT call {@link #writeSettings(TypedXmlSerializer, boolean, int)}. That must be
* handled by the caller.
*/
- void migrateState(@NonNull PackageSetting oldPkgSetting, @NonNull PackageSetting newPkgSetting);
+ void migrateState(@NonNull PackageStateInternal oldPkgSetting,
+ @NonNull PackageStateInternal newPkgSetting);
/**
* Serializes the entire internal state. This is equivalent to a full backup of the existing
@@ -352,12 +354,12 @@
* @param userId the specific user to print, or null to skip printing user selection
* states, supports {@link android.os.UserHandle#USER_ALL}
* @param pkgSettingFunction the method by which to retrieve package data; if this is called
- * from {@link PackageManagerService}, it is
- * expected to pass in the snapshot of {@link PackageSetting} objects
+ * from {@link PackageManagerService}, it is expected to pass in the
+ * snapshot of {@link PackageStateInternal} objects
*/
void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
@Nullable @UserIdInt Integer userId,
- @NonNull Function<String, PackageSetting> pkgSettingFunction)
+ @NonNull Function<String, PackageStateInternal> pkgSettingFunction)
throws NameNotFoundException;
@NonNull
@@ -376,7 +378,7 @@
@NonNull
Pair<List<ResolveInfo>, Integer> filterToApprovedApp(@NonNull Intent intent,
@NonNull List<ResolveInfo> infos, @UserIdInt int userId,
- @NonNull Function<String, PackageSetting> pkgSettingFunction);
+ @NonNull Function<String, PackageStateInternal> pkgSettingFunction);
/**
* Check at what precedence a package resolving a URI is approved to takeover the domain.
@@ -388,7 +390,7 @@
* {@link #filterToApprovedApp(Intent, List, int, Function)} for that.
*/
@ApprovalLevel
- int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
+ int approvalLevelForDomain(@NonNull PackageStateInternal pkgSetting, @NonNull Intent intent,
@PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId);
/**
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index 3174e91..962816c 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -22,6 +22,7 @@
import android.annotation.CheckResult;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.compat.annotation.ChangeId;
import android.content.Context;
@@ -31,7 +32,6 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.pkg.PackageUserState;
import android.content.pm.pkg.PackageUserStateUtils;
import android.content.pm.verify.domain.DomainOwner;
import android.content.pm.verify.domain.DomainVerificationInfo;
@@ -58,9 +58,10 @@
import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.compat.PlatformCompat;
-import com.android.server.pm.PackageSetting;
import com.android.server.pm.Settings;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState;
import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
@@ -81,6 +82,7 @@
import java.util.function.Consumer;
import java.util.function.Function;
+@SuppressLint("MissingPermission")
public class DomainVerificationService extends SystemService
implements DomainVerificationManagerInternal, DomainVerificationShell.Callback {
@@ -263,7 +265,7 @@
mEnforcer.assertApprovedQuerent(mConnection.getCallingUid(), mProxy);
return mConnection.withPackageSettingsSnapshotReturningThrowing(pkgSettings -> {
synchronized (mLock) {
- PackageSetting pkgSetting = pkgSettings.apply(packageName);
+ PackageStateInternal pkgSetting = pkgSettings.apply(packageName);
AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
if (pkg == null) {
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
@@ -385,7 +387,7 @@
for (int index = 0; index < size; index++) {
DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
String pkgName = pkgState.getPackageName();
- PackageSetting pkgSetting = pkgSettings.apply(pkgName);
+ PackageStateInternal pkgSetting = pkgSettings.apply(pkgName);
if (pkgSetting == null || pkgSetting.getPkg() == null) {
continue;
}
@@ -419,7 +421,7 @@
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
- PackageSetting pkgSetting = pkgSettings.apply(packageName);
+ PackageStateInternal pkgSetting = pkgSettings.apply(packageName);
if (pkgSetting == null || pkgSetting.getPkg() == null) {
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
@@ -596,7 +598,7 @@
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
- PackageSetting pkgSetting = pkgSettings.apply(packageName);
+ PackageStateInternal pkgSetting = pkgSettings.apply(packageName);
AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
if (pkg == null) {
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
@@ -639,7 +641,7 @@
private int revokeOtherUserSelectionsLocked(
@NonNull DomainVerificationInternalUserState userState, @UserIdInt int userId,
@NonNull Set<String> domains,
- @NonNull Function<String, PackageSetting> pkgSettingFunction) {
+ @NonNull Function<String, PackageStateInternal> pkgSettingFunction) {
// Cache the approved packages from the 1st pass because the search is expensive
ArrayMap<String, List<String>> domainToApprovedPackages = new ArrayMap<>();
@@ -697,7 +699,7 @@
return mConnection.withPackageSettingsSnapshotReturningThrowing(pkgSettings -> {
synchronized (mLock) {
- PackageSetting pkgSetting = pkgSettings.apply(packageName);
+ PackageStateInternal pkgSetting = pkgSettings.apply(packageName);
AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
if (pkg == null) {
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
@@ -772,15 +774,15 @@
}
/**
- * @param includeNegative See {@link #approvalLevelForDomain(PackageSetting, String, boolean,
- * int, Object)}.
+ * @param includeNegative See
+ * {@link #approvalLevelForDomain(PackageStateInternal, String, boolean, int, Object)}.
* @return Mapping of approval level to packages; packages are sorted by firstInstallTime. Null
* if no owners were found.
*/
@NonNull
private SparseArray<List<String>> getOwnersForDomainInternal(@NonNull String domain,
boolean includeNegative, @UserIdInt int userId,
- @NonNull Function<String, PackageSetting> pkgSettingFunction) {
+ @NonNull Function<String, PackageStateInternal> pkgSettingFunction) {
SparseArray<List<String>> levelToPackages = new SparseArray<>();
// First, collect the raw approval level values
synchronized (mLock) {
@@ -788,7 +790,7 @@
for (int index = 0; index < size; index++) {
DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
String packageName = pkgState.getPackageName();
- PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
+ PackageStateInternal pkgSetting = pkgSettingFunction.apply(packageName);
if (pkgSetting == null) {
continue;
}
@@ -815,8 +817,8 @@
// Then sort them ascending by first installed time, with package name as tie breaker
for (int index = 0; index < size; index++) {
levelToPackages.valueAt(index).sort((first, second) -> {
- PackageSetting firstPkgSetting = pkgSettingFunction.apply(first);
- PackageSetting secondPkgSetting = pkgSettingFunction.apply(second);
+ PackageStateInternal firstPkgSetting = pkgSettingFunction.apply(first);
+ PackageStateInternal secondPkgSetting = pkgSettingFunction.apply(second);
long firstInstallTime =
firstPkgSetting == null ? -1L : firstPkgSetting.getFirstInstallTime();
@@ -842,8 +844,8 @@
}
@Override
- public void migrateState(@NonNull PackageSetting oldPkgSetting,
- @NonNull PackageSetting newPkgSetting) {
+ public void migrateState(@NonNull PackageStateInternal oldPkgSetting,
+ @NonNull PackageStateInternal newPkgSetting) {
String pkgName = newPkgSetting.getPackageName();
boolean sendBroadcast;
@@ -929,9 +931,9 @@
}
}
- // TODO(b/159952358): Handle valid domainSetIds for PackageSettings with no AndroidPackage
+ // TODO(b/159952358): Handle valid domainSetIds for PackageStateInternals with no AndroidPackage
@Override
- public void addPackage(@NonNull PackageSetting newPkgSetting) {
+ public void addPackage(@NonNull PackageStateInternal newPkgSetting) {
// TODO(b/159952358): Optimize packages without any domains. Those wouldn't have to be in
// the state map, but it would require handling the "migration" case where an app either
// gains or loses all domains.
@@ -1030,7 +1032,7 @@
*
* @return whether or not a broadcast is necessary for this package
*/
- private boolean applyImmutableState(@NonNull PackageSetting pkgSetting,
+ private boolean applyImmutableState(@NonNull PackageStateInternal pkgSetting,
@NonNull ArrayMap<String, Integer> stateMap,
@NonNull ArraySet<String> autoVerifyDomains) {
if (pkgSetting.isSystem()
@@ -1064,7 +1066,7 @@
Function<String, String> pkgNameToSignature = null;
if (includeSignatures) {
pkgNameToSignature = pkgName -> {
- PackageSetting pkgSetting = pkgSettings.apply(pkgName);
+ PackageStateInternal pkgSetting = pkgSettings.apply(pkgName);
if (pkgSetting == null) {
// If querying for a user restored package that isn't installed on the
// device yet, there will be no signature to write out. In that case,
@@ -1193,7 +1195,7 @@
@Override
public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
@Nullable @UserIdInt Integer userId,
- @NonNull Function<String, PackageSetting> pkgSettingFunction)
+ @NonNull Function<String, PackageStateInternal> pkgSettingFunction)
throws NameNotFoundException {
synchronized (mLock) {
mDebug.printState(writer, packageName, userId, pkgSettingFunction, mAttachedPkgStates);
@@ -1226,9 +1228,9 @@
private void printOwnersForPackage(@NonNull IndentingPrintWriter writer,
@NonNull String packageName, @Nullable @UserIdInt Integer userId,
- @NonNull Function<String, PackageSetting> pkgSettingFunction)
+ @NonNull Function<String, PackageStateInternal> pkgSettingFunction)
throws NameNotFoundException {
- PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
+ PackageStateInternal pkgSetting = pkgSettingFunction.apply(packageName);
AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
if (pkg == null) {
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
@@ -1265,7 +1267,7 @@
private void printOwnersForDomain(@NonNull IndentingPrintWriter writer, @NonNull String domain,
@Nullable @UserIdInt Integer userId,
- @NonNull Function<String, PackageSetting> pkgSettingFunction) {
+ @NonNull Function<String, PackageStateInternal> pkgSettingFunction) {
SparseArray<SparseArray<List<String>>> userIdToApprovalLevelToOwners =
new SparseArray<>();
@@ -1326,7 +1328,7 @@
private GetAttachedResult getAndValidateAttachedLocked(@NonNull UUID domainSetId,
@NonNull Set<String> domains, boolean forAutoVerify, int callingUid,
@Nullable Integer userIdForFilter,
- @NonNull Function<String, PackageSetting> pkgSettingFunction)
+ @NonNull Function<String, PackageStateInternal> pkgSettingFunction)
throws NameNotFoundException {
if (domainSetId == null) {
throw new IllegalArgumentException("domainSetId cannot be null");
@@ -1344,7 +1346,7 @@
return GetAttachedResult.error(DomainVerificationManager.ERROR_DOMAIN_SET_ID_INVALID);
}
- PackageSetting pkgSetting = pkgSettingFunction.apply(pkgName);
+ PackageStateInternal pkgSetting = pkgSettingFunction.apply(pkgName);
if (pkgSetting == null || pkgSetting.getPkg() == null) {
throw DomainVerificationUtils.throwPackageUnavailable(pkgName);
}
@@ -1439,7 +1441,7 @@
for (int index = 0; index < size; index++) {
DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
String pkgName = pkgState.getPackageName();
- PackageSetting pkgSetting = pkgSettings.apply(pkgName);
+ PackageStateInternal pkgSetting = pkgSettings.apply(pkgName);
if (pkgSetting == null || pkgSetting.getPkg() == null) {
continue;
}
@@ -1450,7 +1452,7 @@
for (int index = 0; index < size; index++) {
String pkgName = packageNames.get(index);
DomainVerificationPkgState pkgState = mAttachedPkgStates.get(pkgName);
- PackageSetting pkgSetting = pkgSettings.apply(pkgName);
+ PackageStateInternal pkgSetting = pkgSettings.apply(pkgName);
if (pkgSetting == null || pkgSetting.getPkg() == null) {
continue;
}
@@ -1467,7 +1469,7 @@
* Reset states that are mutable by the domain verification agent.
*/
private void resetDomainState(@NonNull ArrayMap<String, Integer> stateMap,
- @NonNull PackageSetting pkgSetting) {
+ @NonNull PackageStateInternal pkgSetting) {
int size = stateMap.size();
for (int index = size - 1; index >= 0; index--) {
Integer state = stateMap.valueAt(index);
@@ -1539,7 +1541,7 @@
@Override
public Pair<List<ResolveInfo>, Integer> filterToApprovedApp(@NonNull Intent intent,
@NonNull List<ResolveInfo> infos, @UserIdInt int userId,
- @NonNull Function<String, PackageSetting> pkgSettingFunction) {
+ @NonNull Function<String, PackageStateInternal> pkgSettingFunction) {
String domain = intent.getData().getHost();
// Collect valid infos
@@ -1597,7 +1599,7 @@
@ApprovalLevel
private int fillMapWithApprovalLevels(@NonNull ArrayMap<ResolveInfo, Integer> inputMap,
@NonNull String domain, @UserIdInt int userId,
- @NonNull Function<String, PackageSetting> pkgSettingFunction) {
+ @NonNull Function<String, PackageStateInternal> pkgSettingFunction) {
int highestApproval = APPROVAL_LEVEL_NONE;
int size = inputMap.size();
for (int index = 0; index < size; index++) {
@@ -1608,7 +1610,7 @@
ResolveInfo info = inputMap.keyAt(index);
final String packageName = info.getComponentInfo().packageName;
- PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
+ PackageStateInternal pkgSetting = pkgSettingFunction.apply(packageName);
if (pkgSetting == null) {
fillInfoMapForSamePackage(inputMap, packageName, APPROVAL_LEVEL_NONE);
continue;
@@ -1634,7 +1636,7 @@
@NonNull
private void filterToLastFirstInstalled(@NonNull ArrayMap<ResolveInfo, Integer> inputMap,
- @NonNull Function<String, PackageSetting> pkgSettingFunction) {
+ @NonNull Function<String, PackageStateInternal> pkgSettingFunction) {
// First, find the package with the latest first install time
String targetPackageName = null;
long latestInstall = Long.MIN_VALUE;
@@ -1642,7 +1644,7 @@
for (int index = 0; index < size; index++) {
ResolveInfo info = inputMap.keyAt(index);
String packageName = info.getComponentInfo().packageName;
- PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
+ PackageStateInternal pkgSetting = pkgSettingFunction.apply(packageName);
if (pkgSetting == null) {
continue;
}
@@ -1665,12 +1667,12 @@
@NonNull
private void filterToLastDeclared(@NonNull List<ResolveInfo> inputList,
- @NonNull Function<String, PackageSetting> pkgSettingFunction) {
+ @NonNull Function<String, PackageStateInternal> pkgSettingFunction) {
// Must call size each time as the size of the list will decrease
for (int index = 0; index < inputList.size(); index++) {
ResolveInfo info = inputList.get(index);
String targetPackageName = info.getComponentInfo().packageName;
- PackageSetting pkgSetting = pkgSettingFunction.apply(targetPackageName);
+ PackageStateInternal pkgSetting = pkgSettingFunction.apply(targetPackageName);
AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
if (pkg == null) {
continue;
@@ -1718,8 +1720,9 @@
}
@Override
- public int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
- @PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId) {
+ public int approvalLevelForDomain(@NonNull PackageStateInternal pkgSetting,
+ @NonNull Intent intent, @PackageManager.ResolveInfoFlags int resolveInfoFlags,
+ @UserIdInt int userId) {
String packageName = pkgSetting.getPackageName();
if (!DomainVerificationUtils.isDomainVerificationIntent(intent, resolveInfoFlags)) {
if (DEBUG_APPROVAL) {
@@ -1738,12 +1741,13 @@
* @param debugObject Should be an {@link Intent} if checking for resolution or a
* {@link String} otherwise.
*/
- private int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull String host,
- boolean includeNegative, @UserIdInt int userId, @NonNull Object debugObject) {
+ private int approvalLevelForDomain(@NonNull PackageStateInternal pkgSetting,
+ @NonNull String host, boolean includeNegative, @UserIdInt int userId,
+ @NonNull Object debugObject) {
int approvalLevel = approvalLevelForDomainInternal(pkgSetting, host, includeNegative,
userId, debugObject);
if (includeNegative && approvalLevel == APPROVAL_LEVEL_NONE) {
- PackageUserState pkgUserState = pkgSetting.readUserState(userId);
+ PackageUserState pkgUserState = pkgSetting.getUserStateOrDefault(userId);
if (!pkgUserState.isInstalled()) {
return APPROVAL_LEVEL_NOT_INSTALLED;
}
@@ -1761,7 +1765,7 @@
return approvalLevel;
}
- private int approvalLevelForDomainInternal(@NonNull PackageSetting pkgSetting,
+ private int approvalLevelForDomainInternal(@NonNull PackageStateInternal pkgSetting,
@NonNull String host, boolean includeNegative, @UserIdInt int userId,
@NonNull Object debugObject) {
String packageName = pkgSetting.getPackageName();
@@ -1775,7 +1779,7 @@
return APPROVAL_LEVEL_UNDECLARED;
}
- final PackageUserState pkgUserState = pkgSetting.readUserState(userId);
+ final PackageUserState pkgUserState = pkgSetting.getUserStates().get(userId);
if (pkgUserState == null) {
if (DEBUG_APPROVAL) {
debugApproval(packageName, debugObject, userId, false,
@@ -1849,7 +1853,7 @@
if (pkg != null) {
// To allow an instant app to immediately open domains after being installed by the
// user, auto approve them for any declared autoVerify domains.
- if (pkgSetting.getInstantApp(userId)
+ if (pkgSetting.getUserStateOrDefault(userId).isInstantApp()
&& mCollector.collectValidAutoVerifyDomains(pkg).contains(host)) {
return APPROVAL_LEVEL_INSTANT_APP;
}
@@ -1928,7 +1932,7 @@
@NonNull
private Pair<List<String>, Integer> getApprovedPackagesLocked(@NonNull String domain,
@UserIdInt int userId, int minimumApproval,
- @NonNull Function<String, PackageSetting> pkgSettingFunction) {
+ @NonNull Function<String, PackageStateInternal> pkgSettingFunction) {
boolean includeNegative = minimumApproval < APPROVAL_LEVEL_NONE;
int highestApproval = minimumApproval;
List<String> approvedPackages = emptyList();
@@ -1938,7 +1942,7 @@
for (int index = 0; index < size; index++) {
DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
String packageName = pkgState.getPackageName();
- PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
+ PackageStateInternal pkgSetting = pkgSettingFunction.apply(packageName);
if (pkgSetting == null) {
continue;
}
@@ -1968,7 +1972,7 @@
final int approvedSize = approvedPackages.size();
for (int index = 0; index < approvedSize; index++) {
String packageName = approvedPackages.get(index);
- PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
+ PackageStateInternal pkgSetting = pkgSettingFunction.apply(packageName);
if (pkgSetting == null) {
continue;
}
@@ -2029,7 +2033,7 @@
}
/**
- * Wraps a {@link Connection} to verify that the {@link PackageSetting} calls do not hold
+ * Wraps a {@link Connection} to verify that the {@link PackageStateInternal} calls do not hold
* {@link #mLock}, as that can cause deadlock when {@link Settings} tries to serialize state to
* disk. Only enabled if {@link Build#IS_USERDEBUG} or {@link Build#IS_ENG} is true.
*/
@@ -2050,23 +2054,24 @@
@Override
public void withPackageSettingsSnapshot(
- @NonNull Consumer<Function<String, PackageSetting>> block) {
+ @NonNull Consumer<Function<String, PackageStateInternal>> block) {
enforceLocking();
mConnection.withPackageSettingsSnapshot(block);
}
@Override
public <Output> Output withPackageSettingsSnapshotReturning(
- @NonNull FunctionalUtils.ThrowingFunction<Function<String, PackageSetting>, Output>
- block) {
+ @NonNull FunctionalUtils.ThrowingFunction<Function<String, PackageStateInternal>,
+ Output> block) {
enforceLocking();
return mConnection.withPackageSettingsSnapshotReturning(block);
}
@Override
public <ExceptionType extends Exception> void withPackageSettingsSnapshotThrowing(
- @NonNull FunctionalUtils.ThrowingCheckedConsumer<Function<String, PackageSetting>,
- ExceptionType> block) throws ExceptionType {
+ @NonNull FunctionalUtils.ThrowingCheckedConsumer<
+ Function<String, PackageStateInternal>, ExceptionType> block)
+ throws ExceptionType {
enforceLocking();
mConnection.withPackageSettingsSnapshotThrowing(block);
}
@@ -2075,7 +2080,8 @@
public <ExceptionOne extends Exception, ExceptionTwo extends Exception> void
withPackageSettingsSnapshotThrowing2(
@NonNull FunctionalUtils.ThrowingChecked2Consumer<
- Function<String, PackageSetting>, ExceptionOne, ExceptionTwo> block)
+ Function<String, PackageStateInternal>, ExceptionOne,
+ ExceptionTwo> block)
throws ExceptionOne, ExceptionTwo {
enforceLocking();
mConnection.withPackageSettingsSnapshotThrowing2(block);
@@ -2085,7 +2091,8 @@
public <Output, ExceptionType extends Exception> Output
withPackageSettingsSnapshotReturningThrowing(
@NonNull FunctionalUtils.ThrowingCheckedFunction<
- Function<String, PackageSetting>, Output, ExceptionType> block)
+ Function<String, PackageStateInternal>, Output,
+ ExceptionType> block)
throws ExceptionType {
enforceLocking();
return mConnection.withPackageSettingsSnapshotReturningThrowing(block);
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
index 3b2990e..015d4e9 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java
@@ -28,8 +28,8 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.PackageSetting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState;
import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
@@ -101,7 +101,7 @@
*/
public void readSettings(@NonNull TypedXmlPullParser parser,
@NonNull DomainVerificationStateMap<DomainVerificationPkgState> liveState,
- @NonNull Function<String, PackageSetting> pkgSettingFunction)
+ @NonNull Function<String, PackageStateInternal> pkgSettingFunction)
throws IOException, XmlPullParserException {
DomainVerificationPersistence.ReadResult result =
DomainVerificationPersistence.readFromXml(parser);
@@ -139,7 +139,7 @@
*/
public void restoreSettings(@NonNull TypedXmlPullParser parser,
@NonNull DomainVerificationStateMap<DomainVerificationPkgState> liveState,
- @NonNull Function<String, PackageSetting> pkgSettingFunction)
+ @NonNull Function<String, PackageStateInternal> pkgSettingFunction)
throws IOException, XmlPullParserException {
// TODO(b/170746586): Restoration assumes user IDs match, which is probably not the case on
// a new device.
@@ -217,8 +217,8 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
public void mergePkgState(@NonNull DomainVerificationPkgState oldState,
@NonNull DomainVerificationPkgState newState,
- @NonNull Function<String, PackageSetting> pkgSettingFunction) {
- PackageSetting pkgSetting = pkgSettingFunction.apply(oldState.getPackageName());
+ @NonNull Function<String, PackageStateInternal> pkgSettingFunction) {
+ PackageStateInternal pkgSetting = pkgSettingFunction.apply(oldState.getPackageName());
AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
Set<String> validDomains = pkg == null
? Collections.emptySet() : mCollector.collectValidAutoVerifyDomains(pkg);
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 0c78494..7826ddf 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -24,6 +24,7 @@
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkIdentity.OEM_PAID;
import static android.net.NetworkIdentity.OEM_PRIVATE;
@@ -158,6 +159,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.StatsEvent;
@@ -1316,25 +1318,30 @@
}
@NonNull private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForOemManaged() {
- final int[] transports = new int[] {MATCH_ETHERNET, MATCH_MOBILE_WILDCARD,
- MATCH_WIFI_WILDCARD};
+ final List<Pair<Integer, Integer>> matchRulesAndTransports = List.of(
+ new Pair(MATCH_ETHERNET, TRANSPORT_ETHERNET),
+ new Pair(MATCH_MOBILE_WILDCARD, TRANSPORT_CELLULAR),
+ new Pair(MATCH_WIFI_WILDCARD, TRANSPORT_WIFI)
+ );
final int[] oemManagedTypes = new int[] {OEM_PAID | OEM_PRIVATE, OEM_PAID, OEM_PRIVATE};
final List<NetworkStatsExt> ret = new ArrayList<>();
- for (final int transport : transports) {
+ for (Pair<Integer, Integer> ruleAndTransport : matchRulesAndTransports) {
+ final Integer matchRule = ruleAndTransport.first;
for (final int oemManaged : oemManagedTypes) {
/* A null subscriberId will set wildcard=true, since we aren't trying to select a
specific ssid or subscriber. */
- final NetworkTemplate template = new NetworkTemplate(transport,
+ final NetworkTemplate template = new NetworkTemplate(matchRule,
/*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null,
METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
oemManaged);
- final NetworkStats stats = getUidNetworkStatsSnapshotForTemplate(template, true);
+ final NetworkStats stats = getUidNetworkStatsSnapshotForTemplate(template, false);
+ final Integer transport = ruleAndTransport.second;
if (stats != null) {
- ret.add(new NetworkStatsExt(sliceNetworkStatsByUidTagAndMetered(stats),
- new int[] {transport}, /*slicedByFgbg=*/true, /*slicedByTag=*/true,
- /*slicedByMetered=*/true, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats),
+ new int[] {transport}, /*slicedByFgbg=*/true, /*slicedByTag=*/false,
+ /*slicedByMetered=*/false, TelephonyManager.NETWORK_TYPE_UNKNOWN,
/*subInfo=*/null, oemManaged));
}
}
diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
index d09eceb..cf212df 100644
--- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
@@ -36,6 +36,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.SparseArray;
+import android.view.Surface;
import com.android.internal.annotations.GuardedBy;
import com.android.server.SystemService;
@@ -136,6 +137,16 @@
return sessionState;
}
+ @GuardedBy("mLock")
+ private ITvIAppSession getSessionLocked(SessionState sessionState) {
+ ITvIAppSession session = sessionState.mSession;
+ if (session == null) {
+ throw new IllegalStateException("Session not yet created for token "
+ + sessionState.mSessionToken);
+ }
+ return session;
+ }
+
private final class BinderService extends ITvIAppManager.Stub {
@Override
@@ -234,6 +245,55 @@
Slogf.e(TAG, "error in start", e);
}
}
+
+ @Override
+ public void setSurface(IBinder sessionToken, Surface surface, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "setSurface");
+ SessionState sessionState = null;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ sessionState = getSessionStateLocked(sessionToken, callingUid,
+ resolvedUserId);
+ getSessionLocked(sessionState).setSurface(surface);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slogf.e(TAG, "error in setSurface", e);
+ }
+ }
+ } finally {
+ if (surface != null) {
+ // surface is not used in TvIAppManagerService.
+ surface.release();
+ }
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void dispatchSurfaceChanged(IBinder sessionToken, int format, int width,
+ int height, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "dispatchSurfaceChanged");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+ resolvedUserId);
+ getSessionLocked(sessionState).dispatchSurfaceChanged(format, width,
+ height);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slogf.e(TAG, "error in dispatchSurfaceChanged", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
}
@GuardedBy("mLock")
@@ -616,6 +676,25 @@
}
}
+ @Override
+ public void onLayoutSurface(int left, int top, int right, int bottom) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slogf.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top
+ + ", right=" + right + ", bottom=" + bottom + ",)");
+ }
+ if (mSessionState.mSession == null || mSessionState.mClient == null) {
+ return;
+ }
+ try {
+ mSessionState.mClient.onLayoutSurface(left, top, right, bottom,
+ mSessionState.mSeq);
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in onLayoutSurface", e);
+ }
+ }
+ }
+
@GuardedBy("mLock")
private boolean addSessionTokenToClientStateLocked(ITvIAppSession session) {
try {
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 7dec4e7..239a916 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -1663,8 +1663,6 @@
} /* validationStatusCallback */);
agent.register();
- agent.setUnderlyingNetworks(
- mUnderlying == null ? null : Collections.singletonList(mUnderlying.network));
agent.markConnected();
return agent;
@@ -2039,6 +2037,7 @@
"Unknown transport type or missing TransportInfo/NetworkSpecifier for"
+ " non-null underlying network");
}
+ builder.setUnderlyingNetworks(List.of(underlying.network));
} else {
Slog.wtf(
TAG,
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index c715c39..15db790 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -11,7 +11,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -190,7 +189,7 @@
@VisibleForTesting
boolean allDrawn() {
- return mAssociatedTransitionInfo != null && mAssociatedTransitionInfo.allDrawn();
+ return mAssociatedTransitionInfo != null && mAssociatedTransitionInfo.mIsDrawn;
}
boolean hasActiveTransitionInfo() {
@@ -224,8 +223,8 @@
final boolean mProcessRunning;
/** whether the process of the launching activity didn't have any active activity. */
final boolean mProcessSwitch;
- /** The activities that should be drawn. */
- final ArrayList<ActivityRecord> mPendingDrawActivities = new ArrayList<>(2);
+ /** Whether the last launched activity has reported drawn. */
+ boolean mIsDrawn;
/** The latest activity to have been launched. */
@NonNull ActivityRecord mLastLaunchedActivity;
@@ -318,10 +317,7 @@
mLastLaunchedActivity.mLaunchRootTask = null;
}
mLastLaunchedActivity = r;
- if (!r.noDisplay && !r.isReportedDrawn()) {
- if (DEBUG_METRICS) Slog.i(TAG, "Add pending draw " + r);
- mPendingDrawActivities.add(r);
- }
+ mIsDrawn = r.isReportedDrawn();
}
/** Returns {@code true} if the incoming activity can belong to this transition. */
@@ -332,29 +328,7 @@
/** @return {@code true} if the activity matches a launched activity in this transition. */
boolean contains(ActivityRecord r) {
- return r != null && (r == mLastLaunchedActivity || mPendingDrawActivities.contains(r));
- }
-
- /** Called when the activity is drawn or won't be drawn. */
- void removePendingDrawActivity(ActivityRecord r) {
- if (DEBUG_METRICS) Slog.i(TAG, "Remove pending draw " + r);
- mPendingDrawActivities.remove(r);
- }
-
- boolean allDrawn() {
- return mPendingDrawActivities.isEmpty();
- }
-
- /** Only keep the records which can be drawn. */
- void updatePendingDraw(boolean keepInitializing) {
- for (int i = mPendingDrawActivities.size() - 1; i >= 0; i--) {
- final ActivityRecord r = mPendingDrawActivities.get(i);
- if (!r.mVisibleRequested
- && !(keepInitializing && r.isState(ActivityRecord.State.INITIALIZING))) {
- if (DEBUG_METRICS) Slog.i(TAG, "Discard pending draw " + r);
- mPendingDrawActivities.remove(i);
- }
- }
+ return r == mLastLaunchedActivity;
}
/**
@@ -377,7 +351,7 @@
@Override
public String toString() {
return "TransitionInfo{" + Integer.toHexString(System.identityHashCode(this))
- + " a=" + mLastLaunchedActivity + " ua=" + mPendingDrawActivities + "}";
+ + " a=" + mLastLaunchedActivity + " d=" + mIsDrawn + "}";
}
}
@@ -473,33 +447,31 @@
mLaunchObserver = new LaunchObserverRegistryImpl(looper);
}
+ private void logWindowState(String state, int durationSecs) {
+ mMetricsLogger.count(state, durationSecs);
+ }
+
void logWindowState() {
final long now = SystemClock.elapsedRealtime() / 1000;
if (mWindowState != WINDOW_STATE_INVALID) {
// We log even if the window state hasn't changed, because the user might remain in
// home/fullscreen move forever and we would like to track this kind of behavior
// too.
- mMetricsLogger.count(TRON_WINDOW_STATE_VARZ_STRINGS[mWindowState],
- (int) (now - mLastLogTimeSecs));
+ mLoggerHandler.sendMessage(PooledLambda.obtainMessage(
+ ActivityMetricsLogger::logWindowState, this,
+ TRON_WINDOW_STATE_VARZ_STRINGS[mWindowState], (int) (now - mLastLogTimeSecs)));
}
mLastLogTimeSecs = now;
mWindowState = WINDOW_STATE_INVALID;
- Task rootTask = mSupervisor.mRootWindowContainer.getTopDisplayFocusedRootTask();
- if (rootTask == null) {
- return;
- }
-
- if (rootTask.isActivityTypeAssistant()) {
+ final Task focusedTask = mSupervisor.mRootWindowContainer.getTopDisplayFocusedRootTask();
+ if (focusedTask == null) return;
+ if (focusedTask.isActivityTypeAssistant()) {
mWindowState = WINDOW_STATE_ASSISTANT;
return;
}
- @WindowingMode int windowingMode = rootTask.getWindowingMode();
- if (windowingMode == WINDOWING_MODE_PINNED) {
- rootTask = mSupervisor.mRootWindowContainer.findRootTaskBehind(rootTask);
- windowingMode = rootTask.getWindowingMode();
- }
+ @WindowingMode final int windowingMode = focusedTask.getWindowingMode();
switch (windowingMode) {
case WINDOWING_MODE_FULLSCREEN:
mWindowState = WINDOW_STATE_STANDARD;
@@ -516,7 +488,7 @@
break;
default:
if (windowingMode != WINDOWING_MODE_UNDEFINED) {
- throw new IllegalStateException("Unknown windowing mode for task=" + rootTask
+ Slog.wtf(TAG, "Unknown windowing mode for task=" + focusedTask
+ " windowingMode=" + windowingMode);
}
}
@@ -683,8 +655,7 @@
// visible such as after the top task is finished.
for (int i = mTransitionInfoList.size() - 2; i >= 0; i--) {
final TransitionInfo prevInfo = mTransitionInfoList.get(i);
- prevInfo.updatePendingDraw(false /* keepInitializing */);
- if (prevInfo.allDrawn()) {
+ if (prevInfo.mIsDrawn || !prevInfo.mLastLaunchedActivity.mVisibleRequested) {
abort(prevInfo, "nothing will be drawn");
}
}
@@ -711,17 +682,16 @@
if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn " + r);
final TransitionInfo info = getActiveTransitionInfo(r);
- if (info == null || info.allDrawn()) {
- if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn no activity to be drawn");
+ if (info == null || info.mIsDrawn) {
+ if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn not pending drawn " + info);
return null;
}
// Always calculate the delay because the caller may need to know the individual drawn time.
info.mWindowsDrawnDelayMs = info.calculateDelay(timestampNs);
- info.removePendingDrawActivity(r);
- info.updatePendingDraw(false /* keepInitializing */);
+ info.mIsDrawn = true;
final TransitionInfoSnapshot infoSnapshot = new TransitionInfoSnapshot(info);
- if (info.mLoggedTransitionStarting && info.allDrawn()) {
- done(false /* abort */, info, "notifyWindowsDrawn - all windows drawn", timestampNs);
+ if (info.mLoggedTransitionStarting) {
+ done(false /* abort */, info, "notifyWindowsDrawn", timestampNs);
}
if (r.mWmService.isRecentsAnimationTarget(r)) {
r.mWmService.getRecentsAnimationController().logRecentsAnimationStartTime(
@@ -770,12 +740,8 @@
info.mCurrentTransitionDelayMs = info.calculateDelay(timestampNs);
info.mReason = activityToReason.valueAt(index);
info.mLoggedTransitionStarting = true;
- // Do not remove activity in initializing state because the transition may be started
- // by starting window. The initializing activity may be requested to visible soon.
- info.updatePendingDraw(true /* keepInitializing */);
- if (info.allDrawn()) {
- done(false /* abort */, info, "notifyTransitionStarting - all windows drawn",
- timestampNs);
+ if (info.mIsDrawn) {
+ done(false /* abort */, info, "notifyTransitionStarting drawn", timestampNs);
}
}
}
@@ -828,12 +794,9 @@
return;
}
if (!r.mVisibleRequested || r.finishing) {
- info.removePendingDrawActivity(r);
- if (info.mLastLaunchedActivity == r) {
- // Check if the tracker can be cancelled because the last launched activity may be
- // no longer visible.
- scheduleCheckActivityToBeDrawn(r, 0 /* delay */);
- }
+ // Check if the tracker can be cancelled because the last launched activity may be
+ // no longer visible.
+ scheduleCheckActivityToBeDrawn(r, 0 /* delay */);
}
}
@@ -852,17 +815,12 @@
// If we have an active transition that's waiting on a certain activity that will be
// invisible now, we'll never get onWindowsDrawn, so abort the transition if necessary.
- // We have no active transitions.
+ // We have no active transitions. Or the notified activity whose visibility changed is
+ // no longer the launched activity, then we can still wait to get onWindowsDrawn.
if (info == null) {
return;
}
- // The notified activity whose visibility changed is no longer the launched activity.
- // We can still wait to get onWindowsDrawn.
- if (info.mLastLaunchedActivity != r) {
- return;
- }
-
// If the task of the launched activity contains any activity to be drawn, then the
// window drawn event should report later to complete the transition. Otherwise all
// activities in this task may be finished, invisible or drawn, so the transition event
@@ -945,7 +903,6 @@
}
logAppTransitionFinished(info, isHibernating != null ? isHibernating : false);
}
- info.mPendingDrawActivities.clear();
mTransitionInfoList.remove(info);
}
@@ -1122,7 +1079,7 @@
if (info == null) {
return null;
}
- if (!info.allDrawn() && info.mPendingFullyDrawn == null) {
+ if (!info.mIsDrawn && info.mPendingFullyDrawn == null) {
// There are still undrawn activities, postpone reporting fully drawn until all of its
// windows are drawn. So that is closer to an usable state.
info.mPendingFullyDrawn = () -> {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 7fea29c7..b6e5876 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2633,7 +2633,11 @@
if (windowingMode == WINDOWING_MODE_PINNED && info.supportsPictureInPicture()) {
return false;
}
- if (WindowConfiguration.inMultiWindowMode(windowingMode) && supportsMultiWindow()
+ // Activity should be resizable if the task is.
+ final boolean supportsMultiWindow = task != null
+ ? task.supportsMultiWindow() || supportsMultiWindow()
+ : supportsMultiWindow();
+ if (WindowConfiguration.inMultiWindowMode(windowingMode) && supportsMultiWindow
&& !mAtmService.mForceResizableActivities) {
// The non resizable app will be letterboxed instead of being forced resizable.
return false;
@@ -2710,7 +2714,7 @@
final ActivityInfo.WindowLayout windowLayout = info.windowLayout;
return windowLayout == null
|| tda.supportsActivityMinWidthHeightMultiWindow(windowLayout.minWidth,
- windowLayout.minHeight);
+ windowLayout.minHeight, info);
}
/**
@@ -5695,7 +5699,7 @@
destroyImmediately("stop-config");
mRootWindowContainer.resumeFocusedTasksTopActivities();
} else {
- mRootWindowContainer.updatePreviousProcess(this);
+ mAtmService.updatePreviousProcess(this);
}
}
mTaskSupervisor.checkReadyForSleepLocked(true /* allowDelay */);
@@ -7134,7 +7138,11 @@
return false;
}
}
- return !isResizeable() && (info.isFixedOrientation() || hasFixedAspectRatio())
+ // Activity should be resizable if the task is.
+ final boolean isResizeable = task != null
+ ? task.isResizeable() || isResizeable()
+ : isResizeable();
+ return !isResizeable && (info.isFixedOrientation() || hasFixedAspectRatio())
// The configuration of non-standard type should be enforced by system.
// {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} is set when this activity is
// added to a task, but this function is called when resolving the launch params, at
@@ -7504,7 +7512,11 @@
// orientation with insets applied.
return;
}
- if (WindowConfiguration.inMultiWindowMode(windowingMode) && isResizeable()) {
+ // Activity should be resizable if the task is.
+ final boolean isResizeable = task != null
+ ? task.isResizeable() || isResizeable()
+ : isResizeable();
+ if (WindowConfiguration.inMultiWindowMode(windowingMode) && isResizeable) {
// Ignore orientation request for resizable apps in multi window.
return;
}
@@ -8972,7 +8984,7 @@
final int dh = rotated ? display.mBaseDisplayWidth : display.mBaseDisplayHeight;
final DisplayCutout cutout = display.calculateDisplayCutoutForRotation(rotation)
.getDisplayCutout();
- policy.getNonDecorInsetsLw(rotation, dw, dh, cutout, mNonDecorInsets[rotation]);
+ policy.getNonDecorInsetsLw(rotation, cutout, mNonDecorInsets[rotation]);
mStableInsets[rotation].set(mNonDecorInsets[rotation]);
policy.convertNonDecorInsetsToStableInsets(mStableInsets[rotation], rotation);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index b966ed1..00eb0d3 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -88,6 +88,7 @@
import android.app.PendingIntent;
import android.app.ProfilerInfo;
import android.app.WaitResult;
+import android.app.WindowConfiguration;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
@@ -1745,6 +1746,16 @@
mIntent.setFlags(mLaunchFlags);
+ boolean dreamStopping = false;
+
+ for (ActivityRecord stoppingActivity : mSupervisor.mStoppingActivities) {
+ if (stoppingActivity.getActivityType()
+ == WindowConfiguration.ACTIVITY_TYPE_DREAM) {
+ dreamStopping = true;
+ break;
+ }
+ }
+
// Get top task at beginning because the order may be changed when reusing existing task.
final Task prevTopTask = mPreferredTaskDisplayArea.getFocusedRootTask();
final Task reusedTask = getReusableTask();
@@ -1805,7 +1816,8 @@
if (!mAvoidMoveToFront && mDoResume) {
mTargetRootTask.getRootTask().moveToFront("reuseOrNewTask", targetTask);
- if (!mTargetRootTask.isTopRootTaskInDisplayArea() && mService.mInternal.isDreaming()) {
+ if (!mTargetRootTask.isTopRootTaskInDisplayArea() && mService.mInternal.isDreaming()
+ && !dreamStopping) {
// Launching underneath dream activity (fullscreen, always-on-top). Run the launch-
// -behind transition so the Activity gets created and starts in visible state.
mLaunchTaskBehind = true;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 64950c7..7fa9861 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -342,8 +342,8 @@
public abstract void onProcessMapped(int pid, WindowProcessController proc);
public abstract void onProcessUnMapped(int pid);
- public abstract void onPackageDataCleared(String name);
- public abstract void onPackageUninstalled(String name);
+ public abstract void onPackageDataCleared(String name, int userId);
+ public abstract void onPackageUninstalled(String name, int userId);
public abstract void onPackageAdded(String name, boolean replacing);
public abstract void onPackageReplaced(ApplicationInfo aInfo);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 690397f..9455ce6 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -398,7 +398,7 @@
*/
volatile WindowProcessController mPreviousProcess;
/** The time at which the previous process was last visible. */
- long mPreviousProcessVisibleTime;
+ private long mPreviousProcessVisibleTime;
/** List of intents that were used to start the most recent tasks. */
private RecentTasks mRecentTasks;
@@ -956,7 +956,7 @@
mActivityClientController = new ActivityClientController(this);
mTaskChangeNotificationController =
- new TaskChangeNotificationController(mGlobalLock, mTaskSupervisor, mH);
+ new TaskChangeNotificationController(mTaskSupervisor, mH);
mLockTaskController = new LockTaskController(mContext, mTaskSupervisor, mH,
mTaskChangeNotificationController);
mActivityStartController = new ActivityStartController(this);
@@ -4634,6 +4634,23 @@
mTopApp = top != null ? top.app : null;
}
+ /**
+ * The process state of previous activity is more important than the regular background to keep
+ * around in case the user wants to return to it.
+ */
+ void updatePreviousProcess(ActivityRecord stoppedActivity) {
+ if (stoppedActivity.app != null && mTopApp != null
+ // Don't replace the previous process if the stopped one is the top, e.g. sleeping.
+ && stoppedActivity.app != mTopApp
+ // The stopped activity must have been visible later than the previous.
+ && stoppedActivity.lastVisibleTime > mPreviousProcessVisibleTime
+ // Home has its own retained state, so don't let it occupy the previous.
+ && stoppedActivity.app != mHomeProcess) {
+ mPreviousProcess = stoppedActivity.app;
+ mPreviousProcessVisibleTime = stoppedActivity.lastVisibleTime;
+ }
+ }
+
void updateActivityUsageStats(ActivityRecord activity, int event) {
ComponentName taskRoot = null;
final Task task = activity.getTask();
@@ -5488,10 +5505,8 @@
@Override
public boolean isGetTasksAllowed(String caller, int callingPid, int callingUid) {
- synchronized (mGlobalLock) {
- return ActivityTaskManagerService.this.isGetTasksAllowed(
- caller, callingPid, callingUid);
- }
+ return ActivityTaskManagerService.this.isGetTasksAllowed(
+ caller, callingPid, callingUid);
}
@HotPath(caller = HotPath.PROCESS_CHANGE)
@@ -5658,19 +5673,20 @@
}
@Override
- public void onPackageDataCleared(String name) {
+ public void onPackageDataCleared(String name, int userId) {
synchronized (mGlobalLock) {
mCompatModePackages.handlePackageDataClearedLocked(name);
mAppWarnings.onPackageDataCleared(name);
+ mPackageConfigPersister.onPackageDataCleared(name, userId);
}
}
@Override
- public void onPackageUninstalled(String name) {
+ public void onPackageUninstalled(String name, int userId) {
synchronized (mGlobalLock) {
mAppWarnings.onPackageUninstalled(name);
mCompatModePackages.handlePackageUninstalledLocked(name);
- mPackageConfigPersister.onPackageUninstall(name);
+ mPackageConfigPersister.onPackageUninstall(name, userId);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 94cd77a..2049962 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -147,6 +147,7 @@
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
import com.android.server.am.UserState;
+import com.android.server.utils.Slogf;
import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
import java.io.FileDescriptor;
@@ -1277,10 +1278,9 @@
/**
* Called when all resumed tasks/root-tasks are idle.
- * @return the state of mService.mAm.mBooting before this was called.
*/
@GuardedBy("mService")
- private boolean checkFinishBootingLocked() {
+ private void checkFinishBootingLocked() {
final boolean booting = mService.isBooting();
boolean enableScreen = false;
mService.setBooting(false);
@@ -1291,15 +1291,12 @@
if (booting || enableScreen) {
mService.postFinishBooting(booting, enableScreen);
}
- return booting;
}
void activityIdleInternal(ActivityRecord r, boolean fromTimeout,
boolean processPausingActivities, Configuration config) {
if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + r);
- boolean booting = false;
-
if (r != null) {
if (DEBUG_IDLE) Slog.d(TAG_IDLE, "activityIdleInternal: Callers="
+ Debug.getCallers(4));
@@ -1322,13 +1319,11 @@
// us, we can now deliver.
r.idle = true;
- //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
-
// Check if able to finish booting when device is booting and all resumed activities
// are idle.
if ((mService.isBooting() && mRootWindowContainer.allResumedActivitiesIdle())
|| fromTimeout) {
- booting = checkFinishBootingLocked();
+ checkFinishBootingLocked();
}
// When activity is idle, we consider the relaunch must be successful, so let's clear
@@ -1356,15 +1351,18 @@
// Atomically retrieve all of the other things to do.
processStoppingAndFinishingActivities(r, processPausingActivities, "idle");
+ if (DEBUG_IDLE) {
+ Slogf.i(TAG, "activityIdleInternal(): r=%s, mStartingUsers=%s", r, mStartingUsers);
+ }
+
if (!mStartingUsers.isEmpty()) {
final ArrayList<UserState> startingUsers = new ArrayList<>(mStartingUsers);
mStartingUsers.clear();
-
- if (!booting) {
- // Complete user switch.
- for (int i = 0; i < startingUsers.size(); i++) {
- mService.mAmInternal.finishUserSwitch(startingUsers.get(i));
- }
+ // Complete user switch.
+ for (int i = 0; i < startingUsers.size(); i++) {
+ UserState userState = startingUsers.get(i);
+ Slogf.i(TAG, "finishing switch of user %d", userState.mHandle.getIdentifier());
+ mService.mAmInternal.finishUserSwitch(userState);
}
}
@@ -2012,6 +2010,7 @@
final void scheduleIdle() {
if (!mHandler.hasMessages(IDLE_NOW_MSG)) {
+ if (DEBUG_IDLE) Slog.d(TAG_IDLE, "scheduleIdle: Callers=" + Debug.getCallers(4));
mHandler.sendEmptyMessage(IDLE_NOW_MSG);
}
}
@@ -2049,6 +2048,16 @@
// Update the current top activity.
mTopResumedActivity = topRootTask.getTopResumedActivity();
+ // Update process state if there is no activity state change (e.g. focus change between
+ // multi-window mode activities) to make sure that the current top has top oom-adj.
+ // If the previous top is null, there should be activity state change from it, Then the
+ // process state should also have been updated so no need to update again.
+ if (mTopResumedActivity != null && prevTopActivity != null) {
+ if (mTopResumedActivity.app != null) {
+ mTopResumedActivity.app.addToPendingTop();
+ }
+ mService.updateOomAdj();
+ }
scheduleTopResumedActivityStateIfNeeded();
mService.updateTopApp(mTopResumedActivity);
@@ -2206,10 +2215,6 @@
task.mTaskId, reason, topActivity.info.applicationInfo.packageName);
}
- void logRootTaskState() {
- mActivityMetricsLogger.logWindowState();
- }
-
void scheduleUpdateMultiWindowMode(Task task) {
final PooledConsumer c = PooledLambda.obtainConsumer(
ActivityTaskSupervisor::addToMultiWindowModeChangedList, this,
diff --git a/services/core/java/com/android/server/wm/AnrController.java b/services/core/java/com/android/server/wm/AnrController.java
index fa43b39..98cd40a 100644
--- a/services/core/java/com/android/server/wm/AnrController.java
+++ b/services/core/java/com/android/server/wm/AnrController.java
@@ -213,7 +213,7 @@
}
}
- String criticalEvents = CriticalEventLog.getInstance().logLinesForAnrFile();
+ String criticalEvents = CriticalEventLog.getInstance().logLinesForSystemServerTraceFile();
final File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
null /* processCpuTracker */, null /* lastPids */, nativePids,
null /* logExceptionCreatingFile */, "Pre-dump", criticalEvents);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index f3d46f0..241e7e2 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2027,7 +2027,7 @@
final int appWidth = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
displayCutout);
- final int appHeight = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode,
+ final int appHeight = mDisplayPolicy.getNonDecorDisplayHeight(dh, rotation,
displayCutout);
mDisplayInfo.rotation = rotation;
mDisplayInfo.logicalWidth = dw;
@@ -2165,9 +2165,9 @@
int rotation, int uiMode, DisplayCutout displayCutout) {
final int appWidth = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
displayCutout);
- final int appHeight = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode,
+ final int appHeight = mDisplayPolicy.getNonDecorDisplayHeight(dh, rotation,
displayCutout);
- mDisplayPolicy.getNonDecorInsetsLw(rotation, dw, dh, displayCutout, mTmpRect);
+ mDisplayPolicy.getNonDecorInsetsLw(rotation, displayCutout, mTmpRect);
final int leftInset = mTmpRect.left;
final int topInset = mTmpRect.top;
// AppBounds at the root level should mirror the app screen size.
@@ -2321,7 +2321,7 @@
rotation).getDisplayCutout();
dm.noncompatWidthPixels = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
displayCutout);
- dm.noncompatHeightPixels = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode,
+ dm.noncompatHeightPixels = mDisplayPolicy.getNonDecorDisplayHeight(dh, rotation,
displayCutout);
float scale = CompatibilityInfo.computeCompatibleScaling(dm, null);
int size = (int)(((dm.noncompatWidthPixels / scale) / dm.density) + .5f);
@@ -2375,7 +2375,7 @@
// Get the app screen size at this rotation.
int w = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode, displayCutout);
- int h = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode, displayCutout);
+ int h = mDisplayPolicy.getNonDecorDisplayHeight(dh, rotation, displayCutout);
// Compute the screen layout size class for this rotation.
int longSize = w;
@@ -4930,6 +4930,7 @@
if (imeTarget != null && !(imeTarget.mActivityRecord != null
&& imeTarget.mActivityRecord.hasStartingWindow())) {
final boolean canImeTargetSetRelativeLayer = imeTarget.getSurfaceControl() != null
+ && imeTarget == mImeControlTarget
&& !imeTarget.inMultiWindowMode()
&& imeTarget.mToken.getActivity(app -> app.isAnimating(TRANSITION | PARENTS,
ANIMATION_TYPE_ALL & ~ANIMATION_TYPE_RECENTS)) == null;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 180ee61..2ea6334 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -19,10 +19,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
-import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
-import static android.util.RotationUtils.deltaRotation;
-import static android.util.RotationUtils.rotateBounds;
import static android.view.Display.TYPE_INTERNAL;
import static android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES;
import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
@@ -35,7 +31,6 @@
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.InsetsState.ITYPE_TOP_MANDATORY_GESTURES;
import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
-import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
@@ -98,7 +93,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import android.Manifest.permission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Px;
@@ -108,7 +102,6 @@
import android.app.ResourcesManager;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.PixelFormat;
@@ -123,9 +116,9 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.util.ArraySet;
import android.util.PrintWriterPrinter;
import android.util.Slog;
-import android.util.SparseArray;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Gravity;
@@ -177,8 +170,6 @@
public class DisplayPolicy {
private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayPolicy" : TAG_WM;
- private static final boolean ALTERNATE_CAR_MODE_NAV_SIZE = false;
-
// The panic gesture may become active only after the keyguard is dismissed and the immersive
// app shows again. If that doesn't happen for 30s we drop the gesture.
private static final long PANIC_GESTURE_EXPIRATION = 30000;
@@ -268,10 +259,6 @@
private WindowState mNavigationBar = null;
@NavigationBarPosition
private int mNavigationBarPosition = NAV_BAR_BOTTOM;
- private int[] mNavigationBarHeightForRotationDefault = new int[4];
- private int[] mNavigationBarWidthForRotationDefault = new int[4];
- private int[] mNavigationBarHeightForRotationInCarMode = new int[4];
- private int[] mNavigationBarWidthForRotationInCarMode = new int[4];
// Alternative status bar for when flexible insets mapping is used to place the status bar on
// another side of the screen.
@@ -294,15 +281,12 @@
@WindowManagerPolicy.AltBarPosition
private int mExtraNavBarAltPosition = ALT_BAR_UNKNOWN;
- /** See {@link #getNavigationBarFrameHeight} */
- private int[] mNavigationBarFrameHeightForRotationDefault = new int[4];
+ private final ArraySet<WindowState> mInsetsSourceWindowsExceptIme = new ArraySet<>();
private boolean mIsFreeformWindowOverlappingWithNavBar;
private boolean mLastImmersiveMode;
- private final SparseArray<Rect> mBarContentFrames = new SparseArray<>();
-
// The windows we were told about in focusChanged.
private WindowState mFocusedWindow;
private WindowState mLastFocusedWindow;
@@ -350,11 +334,11 @@
private long mPendingPanicGestureUptime;
private static final Rect sTmpRect = new Rect();
- private static final Rect sTmpNavFrame = new Rect();
- private static final Rect sTmpStatusFrame = new Rect();
- private static final Rect sTmpDecorFrame = new Rect();
private static final Rect sTmpLastParentFrame = new Rect();
- private static final Rect sTmpDisplayFrameBounds = new Rect();
+ private static final Rect sTmpDisplayCutoutSafe = new Rect();
+ private static final Rect sTmpDisplayFrame = new Rect();
+ private static final Rect sTmpParentFrame = new Rect();
+ private static final Rect sTmpFrame = new Rect();
private final WindowLayout mWindowLayout = new WindowLayout();
@@ -444,11 +428,6 @@
final int displayId = displayContent.getDisplayId();
- if (!INSETS_LAYOUT_GENERALIZATION) {
- mBarContentFrames.put(TYPE_STATUS_BAR, new Rect());
- mBarContentFrames.put(TYPE_NAVIGATION_BAR, new Rect());
- }
-
final Resources r = mContext.getResources();
mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer);
mDeskDockEnablesAccelerometer = r.getBoolean(R.bool.config_deskDockEnablesAccelerometer);
@@ -862,11 +841,6 @@
return true;
}
- private boolean hasStatusBarServicePermission(int pid, int uid) {
- return mContext.checkPermission(permission.STATUS_BAR_SERVICE, pid, uid)
- == PackageManager.PERMISSION_GRANTED;
- }
-
/**
* Only trusted overlays are allowed to use FLAG_SLIPPERY.
*/
@@ -1082,28 +1056,6 @@
return ADD_OKAY;
}
- private void getRotatedWindowBounds(DisplayFrames displayFrames, WindowState windowState,
- Rect outBounds) {
- outBounds.set(windowState.getBounds());
-
- int windowRotation = windowState.getWindowConfiguration().getRotation();
- if (windowRotation == displayFrames.mRotation) {
- return;
- }
-
- // Get displayFrames bounds as it is on WindowState's rotation.
- final int deltaRotation = deltaRotation(windowRotation, displayFrames.mRotation);
- if (deltaRotation == Surface.ROTATION_90 || deltaRotation == Surface.ROTATION_270) {
- sTmpDisplayFrameBounds.set(
- 0, 0, displayFrames.mDisplayHeight, displayFrames.mDisplayWidth);
- } else {
- sTmpDisplayFrameBounds.set(
- 0, 0, displayFrames.mDisplayWidth, displayFrames.mDisplayHeight);
- }
- // Rotate the WindowState's bounds based on the displayFrames rotation
- rotateBounds(outBounds, sTmpDisplayFrameBounds, deltaRotation);
- }
-
/**
* Called when a window is being added to the system. Must not throw an exception.
*
@@ -1117,12 +1069,6 @@
break;
case TYPE_STATUS_BAR:
mStatusBar = win;
- final TriConsumer<DisplayFrames, WindowState, Rect> frameProvider =
- (displayFrames, windowState, rect) -> {
- if (!INSETS_LAYOUT_GENERALIZATION) {
- rect.bottom = rect.top + getStatusBarHeight(displayFrames);
- }
- };
final TriConsumer<DisplayFrames, WindowState, Rect> gestureFrameProvider =
(displayFrames, windowState, rect) -> {
rect.bottom = rect.top + getStatusBarHeight(displayFrames);
@@ -1131,35 +1077,24 @@
if (cutout != null) {
final Rect top = cutout.getBoundingRectTop();
if (!top.isEmpty()) {
- rect.bottom = rect.bottom + mDisplayCutoutTouchableRegionSize;
+ rect.bottom = Math.max(rect.bottom,
+ top.bottom + mDisplayCutoutTouchableRegionSize);
}
}
};
- mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, win, frameProvider);
+ mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, win, null);
mDisplayContent.setInsetProvider(
ITYPE_TOP_MANDATORY_GESTURES, win, gestureFrameProvider);
- mDisplayContent.setInsetProvider(ITYPE_TOP_TAPPABLE_ELEMENT, win, frameProvider);
+ mDisplayContent.setInsetProvider(ITYPE_TOP_TAPPABLE_ELEMENT, win, null);
+ mInsetsSourceWindowsExceptIme.add(win);
break;
case TYPE_NAVIGATION_BAR:
mNavigationBar = win;
mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win,
(displayFrames, windowState, inOutFrame) -> {
- if (INSETS_LAYOUT_GENERALIZATION) {
+ if (!mNavButtonForcedVisible) {
inOutFrame.inset(windowState.getLayoutingAttrs(
displayFrames.mRotation).providedInternalInsets);
- } else {
- // In Gesture Nav, navigation bar frame is larger than frame to
- // calculate inset.
- if (navigationBarPosition(displayFrames.mDisplayWidth,
- displayFrames.mDisplayHeight,
- displayFrames.mRotation) == NAV_BAR_BOTTOM
- && !mNavButtonForcedVisible) {
- sTmpRect.set(inOutFrame);
- sTmpRect.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
- inOutFrame.top = sTmpRect.bottom
- - getNavigationBarHeight(displayFrames.mRotation,
- mDisplayContent.getConfiguration().uiMode);
- }
}
},
@@ -1197,6 +1132,7 @@
inOutFrame.setEmpty();
}
});
+ mInsetsSourceWindowsExceptIme.add(win);
if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar);
break;
default:
@@ -1204,10 +1140,10 @@
for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) {
final TriConsumer<DisplayFrames, WindowState, Rect> imeFrameProvider =
!attrs.providedInternalImeInsets.equals(Insets.NONE)
- ? (displayFrames, windowState, inOutFrame) ->
- inOutFrame.inset(windowState.getLayoutingAttrs(
+ ? (displayFrames, windowState, inOutFrame) ->
+ inOutFrame.inset(windowState.getLayoutingAttrs(
displayFrames.mRotation).providedInternalImeInsets)
- : null;
+ : null;
switch (insetsType) {
case ITYPE_STATUS_BAR:
mStatusBarAlt = win;
@@ -1226,15 +1162,11 @@
mExtraNavBarAltPosition = getAltBarPosition(attrs);
break;
}
- if (!INSETS_LAYOUT_GENERALIZATION) {
- mDisplayContent.setInsetProvider(insetsType, win, null,
- imeFrameProvider);
- } else {
- mDisplayContent.setInsetProvider(insetsType, win, (displayFrames,
- windowState, inOutFrame) -> inOutFrame.inset(
- windowState.getLayoutingAttrs(displayFrames.mRotation)
- .providedInternalInsets), imeFrameProvider);
- }
+ mDisplayContent.setInsetProvider(insetsType, win, (displayFrames,
+ windowState, inOutFrame) -> inOutFrame.inset(
+ windowState.getLayoutingAttrs(displayFrames.mRotation)
+ .providedInternalInsets), imeFrameProvider);
+ mInsetsSourceWindowsExceptIme.add(win);
}
}
break;
@@ -1259,9 +1191,8 @@
TriConsumer<DisplayFrames, WindowState, Rect> getImeSourceFrameProvider() {
return (displayFrames, windowState, inOutFrame) -> {
- if (mNavigationBar != null && navigationBarPosition(displayFrames.mDisplayWidth,
- displayFrames.mDisplayHeight,
- displayFrames.mRotation) == NAV_BAR_BOTTOM) {
+ if (mNavigationBar != null && navigationBarPosition(displayFrames.mRotation)
+ == NAV_BAR_BOTTOM) {
// In gesture navigation, nav bar frame is larger than frame to calculate insets.
// IME should not provide frame which is smaller than the nav bar frame. Otherwise,
// nav bar might be overlapped with the content of the client when IME is shown.
@@ -1319,18 +1250,15 @@
if (mLastFocusedWindow == win) {
mLastFocusedWindow = null;
}
+ mInsetsSourceWindowsExceptIme.remove(win);
}
private int getStatusBarHeight(DisplayFrames displayFrames) {
int statusBarHeight;
- if (INSETS_LAYOUT_GENERALIZATION) {
- if (mStatusBar != null) {
- statusBarHeight = mStatusBar.getLayoutingAttrs(displayFrames.mRotation).height;
- } else {
- statusBarHeight = 0;
- }
+ if (mStatusBar != null) {
+ statusBarHeight = mStatusBar.getLayoutingAttrs(displayFrames.mRotation).height;
} else {
- statusBarHeight = mStatusBarHeightForRotation[displayFrames.mRotation];
+ statusBarHeight = 0;
}
return Math.max(statusBarHeight, displayFrames.mDisplayCutoutSafe.top);
}
@@ -1503,89 +1431,22 @@
return mForceShowSystemBars;
}
- private void simulateLayoutDecorWindow(WindowState win, DisplayFrames displayFrames,
- WindowFrames simulatedWindowFrames, SparseArray<Rect> contentFrames,
- Consumer<Rect> layout) {
- win.setSimulatedWindowFrames(simulatedWindowFrames);
- final int requestedHeight = win.mRequestedHeight;
- final int requestedWidth = win.mRequestedWidth;
- if (INSETS_LAYOUT_GENERALIZATION) {
- // Without a full layout process, in order to layout the system bars correctly, we need
- // to set the requested size and the initial display frames to the window.
- WindowManager.LayoutParams params = win.getLayoutingAttrs(displayFrames.mRotation);
- win.setRequestedSize(params.width, params.height);
- sTmpDecorFrame.set(0, 0, displayFrames.mDisplayWidth, displayFrames.mDisplayHeight);
- simulatedWindowFrames.setFrames(sTmpDecorFrame /* parentFrame */,
- sTmpDecorFrame /* displayFrame */);
- simulatedWindowFrames.mIsSimulatingDecorWindow = true;
- }
- final Rect contentFrame = new Rect();
- try {
- layout.accept(contentFrame);
- } finally {
- win.setSimulatedWindowFrames(null);
- if (INSETS_LAYOUT_GENERALIZATION) {
- win.setRequestedSize(requestedWidth, requestedHeight);
- }
- }
- if (!INSETS_LAYOUT_GENERALIZATION) {
- contentFrames.put(win.mAttrs.type, contentFrame);
- }
- mDisplayContent.getInsetsStateController().computeSimulatedState(
- win, displayFrames, simulatedWindowFrames);
- }
-
/**
* Computes the frames of display (its logical size, rotation and cutout should already be set)
* used to layout window. This method only changes the given display frames, insets state and
* some temporal states, but doesn't change the window frames used to show on screen.
*/
- void simulateLayoutDisplay(DisplayFrames displayFrames, SparseArray<Rect> barContentFrames) {
- if (INSETS_LAYOUT_GENERALIZATION) {
- final InsetsStateController insetsStateController =
- mDisplayContent.getInsetsStateController();
- for (int type = 0; type < InsetsState.SIZE; type++) {
- final InsetsSourceProvider provider =
- insetsStateController.peekSourceProvider(type);
- if (provider == null || !provider.hasWindow()
- || provider.mWin.getControllableInsetProvider() != provider) {
- continue;
- }
- final WindowFrames simulatedWindowFrames = new WindowFrames();
- simulateLayoutDecorWindow(provider.mWin, displayFrames, simulatedWindowFrames,
- barContentFrames,
- contentFrame -> simulateLayoutForContentFrame(displayFrames,
- provider.mWin, contentFrame));
- }
- } else {
- if (mNavigationBar != null) {
- final WindowFrames simulatedWindowFrames = new WindowFrames();
- simulateLayoutDecorWindow(mNavigationBar, displayFrames, simulatedWindowFrames,
- barContentFrames, contentFrame -> layoutNavigationBar(displayFrames,
- contentFrame));
- }
- if (mStatusBar != null) {
- final WindowFrames simulatedWindowFrames = new WindowFrames();
- simulateLayoutDecorWindow(mStatusBar, displayFrames, simulatedWindowFrames,
- barContentFrames,
- contentFrame -> layoutStatusBar(displayFrames, contentFrame));
- }
- if (mExtraNavBarAlt != null) {
- // There's no pre-defined behavior for the extra navigation bar, we need to use the
- // new flexible insets logic anyway.
- final WindowFrames simulatedWindowFrames = new WindowFrames();
- simulateLayoutDecorWindow(mExtraNavBarAlt, displayFrames, simulatedWindowFrames,
- barContentFrames,
- contentFrame -> simulateLayoutForContentFrame(displayFrames,
- mExtraNavBarAlt, contentFrame));
- }
- if (mClimateBarAlt != null) {
- final WindowFrames simulatedWindowFrames = new WindowFrames();
- simulateLayoutDecorWindow(mClimateBarAlt, displayFrames, simulatedWindowFrames,
- barContentFrames,
- contentFrame -> simulateLayoutForContentFrame(displayFrames,
- mClimateBarAlt, contentFrame));
- }
+ void simulateLayoutDisplay(DisplayFrames displayFrames) {
+ final InsetsStateController controller = mDisplayContent.getInsetsStateController();
+ for (int i = mInsetsSourceWindowsExceptIme.size() - 1; i >= 0; i--) {
+ final WindowState win = mInsetsSourceWindowsExceptIme.valueAt(i);
+ mWindowLayout.computeWindowFrames(win.getLayoutingAttrs(displayFrames.mRotation),
+ displayFrames.mInsetsState, displayFrames.mDisplayCutoutSafe,
+ displayFrames.mUnrestricted, win.getWindowingMode(), UNSPECIFIED_LENGTH,
+ UNSPECIFIED_LENGTH, win.getRequestedVisibilities(),
+ null /* attachedWindowFrame */, win.mGlobalScale,
+ sTmpDisplayFrame, sTmpParentFrame, sTmpFrame);
+ controller.computeSimulatedState(win, displayFrames, sTmpFrame);
}
}
@@ -1593,105 +1454,6 @@
mSystemGestures.onDisplayInfoChanged(info);
}
- private void layoutStatusBar(DisplayFrames displayFrames, Rect contentFrame) {
- // decide where the status bar goes ahead of time
- if (mStatusBar == null) {
- return;
- }
- // apply any status bar insets
- getRotatedWindowBounds(displayFrames, mStatusBar, sTmpStatusFrame);
- final WindowFrames windowFrames = mStatusBar.getLayoutingWindowFrames();
- windowFrames.setFrames(sTmpStatusFrame /* parentFrame */,
- sTmpStatusFrame /* displayFrame */);
- // Let the status bar determine its size.
- mStatusBar.computeFrameAndUpdateSourceFrame(displayFrames);
-
- // For layout, the status bar is always at the top with our fixed height.
- int statusBarBottom = displayFrames.mUnrestricted.top
- + mStatusBarHeightForRotation[displayFrames.mRotation];
-
- if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) {
- // Make sure that the zone we're avoiding for the cutout is at least as tall as the
- // status bar; otherwise fullscreen apps will end up cutting halfway into the status
- // bar.
- displayFrames.mDisplayCutoutSafe.top = Math.max(displayFrames.mDisplayCutoutSafe.top,
- statusBarBottom);
- }
-
- sTmpRect.set(windowFrames.mFrame);
- sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
- sTmpRect.top = windowFrames.mFrame.top; // Ignore top display cutout inset
- sTmpRect.bottom = statusBarBottom; // Use collapsed status bar size
- contentFrame.set(sTmpRect);
- }
-
- private int layoutNavigationBar(DisplayFrames displayFrames, Rect contentFrame) {
- if (mNavigationBar == null) {
- return NAV_BAR_INVALID;
- }
-
- final int uiMode = mDisplayContent.getConfiguration().uiMode;
- final Rect navigationFrame = sTmpNavFrame;
- // Force the navigation bar to its appropriate place and size. We need to do this directly,
- // instead of relying on it to bubble up from the nav bar, because this needs to change
- // atomically with screen rotations.
- final int rotation = displayFrames.mRotation;
- final int displayHeight = displayFrames.mDisplayHeight;
- final int displayWidth = displayFrames.mDisplayWidth;
- final int navBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation);
-
- getRotatedWindowBounds(displayFrames, mNavigationBar, navigationFrame);
-
- final Rect cutoutSafeUnrestricted = sTmpRect;
- cutoutSafeUnrestricted.set(displayFrames.mUnrestricted);
- cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
-
- if (navBarPosition == NAV_BAR_BOTTOM) {
- // It's a system nav bar or a portrait screen; nav bar goes on bottom.
- navigationFrame.top = Math.min(cutoutSafeUnrestricted.bottom, navigationFrame.bottom)
- - getNavigationBarFrameHeight(rotation, uiMode);
- } else if (navBarPosition == NAV_BAR_RIGHT) {
- // Landscape screen; nav bar goes to the right.
- navigationFrame.left = Math.min(cutoutSafeUnrestricted.right, navigationFrame.right)
- - getNavigationBarWidth(rotation, uiMode, navBarPosition);
- } else if (navBarPosition == NAV_BAR_LEFT) {
- // Seascape screen; nav bar goes to the left.
- navigationFrame.right = Math.max(cutoutSafeUnrestricted.left, navigationFrame.left)
- + getNavigationBarWidth(rotation, uiMode, navBarPosition);
- }
-
- // Compute the final frame.
- final WindowFrames windowFrames = mNavigationBar.getLayoutingWindowFrames();
- windowFrames.setFrames(navigationFrame /* parentFrame */,
- navigationFrame /* displayFrame */);
- mNavigationBar.computeFrameAndUpdateSourceFrame(displayFrames);
- sTmpRect.set(windowFrames.mFrame);
- sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
- contentFrame.set(sTmpRect);
-
- if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame);
- return navBarPosition;
- }
-
- private void simulateLayoutForContentFrame(DisplayFrames displayFrames, WindowState win,
- Rect simulatedContentFrame) {
- layoutWindowLw(win, null /* attached */, displayFrames);
- final Rect contentFrame = sTmpRect;
- contentFrame.set(win.getLayoutingWindowFrames().mFrame);
- // Excluding the display cutout before set to the simulated content frame.
- contentFrame.intersect(displayFrames.mDisplayCutoutSafe);
- simulatedContentFrame.set(contentFrame);
- }
-
- private boolean canReceiveInput(WindowState win) {
- boolean notFocusable =
- (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0;
- boolean altFocusableIm =
- (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0;
- boolean notFocusableForIm = notFocusable ^ altFocusableIm;
- return !notFocusableForIm;
- }
-
/**
* Called for each window attached to the window manager as layout is proceeding. The
* implementation of this function must take care of setting the window's frame, either here or
@@ -1704,60 +1466,37 @@
* @param displayFrames The display frames.
*/
public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
- if (win == mNavigationBar && !INSETS_LAYOUT_GENERALIZATION) {
- mNavigationBarPosition = layoutNavigationBar(displayFrames,
- mBarContentFrames.get(TYPE_NAVIGATION_BAR));
- return;
- }
- if ((win == mStatusBar && !canReceiveInput(win)) && !INSETS_LAYOUT_GENERALIZATION) {
- layoutStatusBar(displayFrames, mBarContentFrames.get(TYPE_STATUS_BAR));
- return;
- }
- final WindowManager.LayoutParams attrs = win.getLayoutingAttrs(displayFrames.mRotation);
- final int type = attrs.type;
- final int fl = attrs.flags;
- final int sim = attrs.softInputMode;
-
+ // This window might be in the simulated environment.
+ // We invoke this to get the proper DisplayFrames.
displayFrames = win.getDisplayFrames(displayFrames);
- final WindowFrames windowFrames = win.getLayoutingWindowFrames();
+ final WindowManager.LayoutParams attrs = win.getLayoutingAttrs(displayFrames.mRotation);
+ final WindowFrames windowFrames = win.getWindowFrames();
final Rect pf = windowFrames.mParentFrame;
final Rect df = windowFrames.mDisplayFrame;
final Rect f = windowFrames.mFrame;
final Rect attachedWindowFrame = attached != null ? attached.getFrame() : null;
+
+ // If this window has different LayoutParams for rotations, we cannot trust its requested
+ // size. Because it might have not sent its requested size for the new rotation.
+ final boolean trustedSize = attrs == win.mAttrs;
+ final int requestedWidth = trustedSize ? win.mRequestedWidth : UNSPECIFIED_LENGTH;
+ final int requestedHeight = trustedSize ? win.mRequestedHeight : UNSPECIFIED_LENGTH;
+
sTmpLastParentFrame.set(pf);
- final Rect winBounds;
- final int requestedWidth;
- final int requestedHeight;
- if (windowFrames.mIsSimulatingDecorWindow) {
- // Override the bounds in window token has many side effects. Directly use the display
- // frame set for the simulated layout.
- winBounds = df;
-
- // The view hierarchy has not been measured in the simulated layout. Use
- // UNSPECIFIED_LENGTH as the requested width and height so that WindowLayout will choose
- // the proper values in this case.
- requestedWidth = UNSPECIFIED_LENGTH;
- requestedHeight = UNSPECIFIED_LENGTH;
- } else {
- winBounds = win.getBounds();
- requestedWidth = win.mRequestedWidth;
- requestedHeight = win.mRequestedHeight;
- }
-
final boolean clippedByDisplayCutout = mWindowLayout.computeWindowFrames(attrs,
win.getInsetsState(), displayFrames.mDisplayCutoutSafe,
- winBounds, win.getWindowingMode(), requestedWidth, requestedHeight,
+ win.getBounds(), win.getWindowingMode(), requestedWidth, requestedHeight,
win.getRequestedVisibilities(), attachedWindowFrame, win.mGlobalScale,
df, pf, f);
windowFrames.setParentFrameWasClippedByDisplayCutout(clippedByDisplayCutout);
if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle()
- + ": sim=#" + Integer.toHexString(sim)
- + " attach=" + attached + " type=" + type
- + " flags=" + ViewDebug.flagsToString(LayoutParams.class, "flags", fl)
+ + ": sim=#" + Integer.toHexString(attrs.softInputMode)
+ + " attach=" + attached + " type=" + attrs.type
+ + " flags=" + ViewDebug.flagsToString(LayoutParams.class, "flags", attrs.flags)
+ " pf=" + pf.toShortString() + " df=" + df.toShortString()
+ " f=" + f.toShortString());
@@ -1765,9 +1504,7 @@
windowFrames.setContentChanged(true);
}
- if (!windowFrames.mIsSimulatingDecorWindow) {
- win.setFrame();
- }
+ win.setFrame();
}
WindowState getTopFullscreenOpaqueWindow() {
@@ -1805,12 +1542,11 @@
*/
public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs,
WindowState attached, WindowState imeTarget) {
- if (INSETS_LAYOUT_GENERALIZATION && attrs.type == TYPE_NAVIGATION_BAR) {
+ if (attrs.type == TYPE_NAVIGATION_BAR) {
// Keep mNavigationBarPosition updated to make sure the transient detection and bar
// color control is working correctly.
final DisplayFrames displayFrames = mDisplayContent.mDisplayFrames;
- mNavigationBarPosition = navigationBarPosition(displayFrames.mDisplayWidth,
- displayFrames.mDisplayHeight, displayFrames.mRotation);
+ mNavigationBarPosition = navigationBarPosition(displayFrames.mRotation);
}
final boolean affectsSystemUi = win.canAffectSystemUiFlags();
if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": affectsSystemUi=" + affectsSystemUi);
@@ -2058,7 +1794,6 @@
final int upsideDownRotation = displayRotation.getUpsideDownRotation();
final int landscapeRotation = displayRotation.getLandscapeRotation();
final int seascapeRotation = displayRotation.getSeascapeRotation();
- final int uiMode = mService.mPolicy.getUiMode();
if (hasStatusBar()) {
mStatusBarHeightForRotation[portraitRotation] =
@@ -2078,46 +1813,6 @@
mDisplayCutoutTouchableRegionSize = 0;
}
- // Height of the navigation bar when presented horizontally at bottom
- mNavigationBarHeightForRotationDefault[portraitRotation] =
- mNavigationBarHeightForRotationDefault[upsideDownRotation] =
- res.getDimensionPixelSize(R.dimen.navigation_bar_height);
- mNavigationBarHeightForRotationDefault[landscapeRotation] =
- mNavigationBarHeightForRotationDefault[seascapeRotation] =
- res.getDimensionPixelSize(R.dimen.navigation_bar_height_landscape);
-
- // Height of the navigation bar frame when presented horizontally at bottom
- mNavigationBarFrameHeightForRotationDefault[portraitRotation] =
- mNavigationBarFrameHeightForRotationDefault[upsideDownRotation] =
- res.getDimensionPixelSize(R.dimen.navigation_bar_frame_height);
- mNavigationBarFrameHeightForRotationDefault[landscapeRotation] =
- mNavigationBarFrameHeightForRotationDefault[seascapeRotation] =
- res.getDimensionPixelSize(R.dimen.navigation_bar_frame_height_landscape);
-
- // Width of the navigation bar when presented vertically along one side
- mNavigationBarWidthForRotationDefault[portraitRotation] =
- mNavigationBarWidthForRotationDefault[upsideDownRotation] =
- mNavigationBarWidthForRotationDefault[landscapeRotation] =
- mNavigationBarWidthForRotationDefault[seascapeRotation] =
- res.getDimensionPixelSize(R.dimen.navigation_bar_width);
-
- if (ALTERNATE_CAR_MODE_NAV_SIZE) {
- // Height of the navigation bar when presented horizontally at bottom
- mNavigationBarHeightForRotationInCarMode[portraitRotation] =
- mNavigationBarHeightForRotationInCarMode[upsideDownRotation] =
- res.getDimensionPixelSize(R.dimen.navigation_bar_height_car_mode);
- mNavigationBarHeightForRotationInCarMode[landscapeRotation] =
- mNavigationBarHeightForRotationInCarMode[seascapeRotation] =
- res.getDimensionPixelSize(R.dimen.navigation_bar_height_landscape_car_mode);
-
- // Width of the navigation bar when presented vertically along one side
- mNavigationBarWidthForRotationInCarMode[portraitRotation] =
- mNavigationBarWidthForRotationInCarMode[upsideDownRotation] =
- mNavigationBarWidthForRotationInCarMode[landscapeRotation] =
- mNavigationBarWidthForRotationInCarMode[seascapeRotation] =
- res.getDimensionPixelSize(R.dimen.navigation_bar_width_car_mode);
- }
-
mNavBarOpacityMode = res.getInteger(R.integer.config_navBarOpacityMode);
mLeftGestureInset = mGestureNavigationSettingsObserver.getLeftSensitivity(res);
mRightGestureInset = mGestureNavigationSettingsObserver.getRightSensitivity(res);
@@ -2130,7 +1825,7 @@
// This should calculate how much above the frame we accept gestures.
mBottomGestureAdditionalInset =
res.getDimensionPixelSize(R.dimen.navigation_bar_gesture_height)
- - getNavigationBarFrameHeight(portraitRotation, uiMode);
+ - getNavigationBarFrameHeight(portraitRotation);
updateConfigurationAndScreenSizeDependentBehaviors();
@@ -2199,37 +1894,29 @@
}
private int getNavigationBarWidth(int rotation, int uiMode, int position) {
- if (INSETS_LAYOUT_GENERALIZATION) {
- if (mNavigationBar == null) {
+ if (mNavigationBar == null) {
+ return 0;
+ }
+ LayoutParams lp = mNavigationBar.mAttrs;
+ if (lp.paramsForRotation != null
+ && lp.paramsForRotation.length == 4
+ && lp.paramsForRotation[rotation] != null) {
+ lp = lp.paramsForRotation[rotation];
+ }
+ if (position == NAV_BAR_LEFT) {
+ if (lp.width > lp.providedInternalInsets.right) {
+ return lp.width - lp.providedInternalInsets.right;
+ } else {
return 0;
}
- LayoutParams lp = mNavigationBar.mAttrs;
- if (lp.paramsForRotation != null
- && lp.paramsForRotation.length == 4
- && lp.paramsForRotation[rotation] != null) {
- lp = lp.paramsForRotation[rotation];
- }
- if (position == NAV_BAR_LEFT) {
- if (lp.width > lp.providedInternalInsets.right) {
- return lp.width - lp.providedInternalInsets.right;
- } else {
- return 0;
- }
- } else if (position == NAV_BAR_RIGHT) {
- if (lp.width > lp.providedInternalInsets.left) {
- return lp.width - lp.providedInternalInsets.left;
- } else {
- return 0;
- }
- }
- return lp.width;
- } else {
- if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
- return mNavigationBarWidthForRotationInCarMode[rotation];
+ } else if (position == NAV_BAR_RIGHT) {
+ if (lp.width > lp.providedInternalInsets.left) {
+ return lp.width - lp.providedInternalInsets.left;
} else {
- return mNavigationBarWidthForRotationDefault[rotation];
+ return 0;
}
}
+ return lp.width;
}
void notifyDisplayReady() {
@@ -2256,7 +1943,7 @@
DisplayCutout displayCutout) {
int width = fullWidth;
if (hasNavigationBar()) {
- final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation);
+ final int navBarPosition = navigationBarPosition(rotation);
if (navBarPosition == NAV_BAR_LEFT || navBarPosition == NAV_BAR_RIGHT) {
width -= getNavigationBarWidth(rotation, uiMode, navBarPosition);
}
@@ -2268,23 +1955,15 @@
}
@VisibleForTesting
- int getNavigationBarHeight(int rotation, int uiMode) {
- if (INSETS_LAYOUT_GENERALIZATION) {
- if (mNavigationBar == null) {
- return 0;
- }
- LayoutParams lp = mNavigationBar.getLayoutingAttrs(rotation);
- if (lp.height < lp.providedInternalInsets.top) {
- return 0;
- }
- return lp.height - lp.providedInternalInsets.top;
- } else {
- if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
- return mNavigationBarHeightForRotationInCarMode[rotation];
- } else {
- return mNavigationBarHeightForRotationDefault[rotation];
- }
+ int getNavigationBarHeight(int rotation) {
+ if (mNavigationBar == null) {
+ return 0;
}
+ LayoutParams lp = mNavigationBar.getLayoutingAttrs(rotation);
+ if (lp.height < lp.providedInternalInsets.top) {
+ return 0;
+ }
+ return lp.height - lp.providedInternalInsets.top;
}
/**
@@ -2296,22 +1975,13 @@
* In car mode it will return the same height as {@link #getNavigationBarHeight}
*
* @param rotation specifies rotation to return dimension from
- * @param uiMode to determine if in car mode
* @return navigation bar frame height
*/
- private int getNavigationBarFrameHeight(int rotation, int uiMode) {
- if (INSETS_LAYOUT_GENERALIZATION) {
- if (mNavigationBar == null) {
- return 0;
- }
- return mNavigationBar.mAttrs.height;
- } else {
- if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
- return mNavigationBarHeightForRotationInCarMode[rotation];
- } else {
- return mNavigationBarFrameHeightForRotationDefault[rotation];
- }
+ private int getNavigationBarFrameHeight(int rotation) {
+ if (mNavigationBar == null) {
+ return 0;
}
+ return mNavigationBar.getLayoutingAttrs(rotation).height;
}
/**
@@ -2319,14 +1989,11 @@
* decorations that could never be removed in Honeycomb. That is, system bar or
* button bar.
*/
- public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode,
- DisplayCutout displayCutout) {
+ public int getNonDecorDisplayHeight(int fullHeight, int rotation, DisplayCutout displayCutout) {
int height = fullHeight;
- if (hasNavigationBar()) {
- final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation);
- if (navBarPosition == NAV_BAR_BOTTOM) {
- height -= getNavigationBarHeight(rotation, uiMode);
- }
+ final int navBarPosition = navigationBarPosition(rotation);
+ if (navBarPosition == NAV_BAR_BOTTOM) {
+ height -= getNavigationBarHeight(rotation);
}
if (displayCutout != null) {
height -= displayCutout.getSafeInsetTop() + displayCutout.getSafeInsetBottom();
@@ -2348,7 +2015,7 @@
/**
* Return the available screen height that we should report for the
* configuration. This must be no larger than
- * {@link #getNonDecorDisplayHeight(int, int, int, int, DisplayCutout)}; it may be smaller
+ * {@link #getNonDecorDisplayHeight(int, int, DisplayCutout)}; it may be smaller
* than that to account for more transient decoration like a status bar.
*/
public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode,
@@ -2363,7 +2030,7 @@
// bar height.
statusBarHeight = Math.max(0, statusBarHeight - displayCutout.getSafeInsetTop());
}
- return getNonDecorDisplayHeight(fullWidth, fullHeight, rotation, uiMode, displayCutout)
+ return getNonDecorDisplayHeight(fullHeight, rotation, displayCutout)
- statusBarHeight;
}
@@ -2396,40 +2063,35 @@
* Calculates the stable insets without running a layout.
*
* @param displayRotation the current display rotation
- * @param displayWidth the current display width
- * @param displayHeight the current display height
* @param displayCutout the current display cutout
* @param outInsets the insets to return
*/
- public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
- DisplayCutout displayCutout, Rect outInsets) {
+ public void getStableInsetsLw(int displayRotation, DisplayCutout displayCutout,
+ Rect outInsets) {
outInsets.setEmpty();
// Navigation bar and status bar.
- getNonDecorInsetsLw(displayRotation, displayWidth, displayHeight, displayCutout, outInsets);
+ getNonDecorInsetsLw(displayRotation, displayCutout, outInsets);
convertNonDecorInsetsToStableInsets(outInsets, displayRotation);
}
/**
* Calculates the insets for the areas that could never be removed in Honeycomb, i.e. system
* bar or button bar. See {@link #getNonDecorDisplayWidth}.
- *
- * @param displayRotation the current display rotation
- * @param displayWidth the current display width
- * @param displayHeight the current display height
+ * @param displayRotation the current display rotation
* @param displayCutout the current display cutout
* @param outInsets the insets to return
*/
- public void getNonDecorInsetsLw(int displayRotation, int displayWidth, int displayHeight,
- DisplayCutout displayCutout, Rect outInsets) {
+ public void getNonDecorInsetsLw(int displayRotation, DisplayCutout displayCutout,
+ Rect outInsets) {
outInsets.setEmpty();
// Only navigation bar
if (hasNavigationBar()) {
final int uiMode = mService.mPolicy.getUiMode();
- int position = navigationBarPosition(displayWidth, displayHeight, displayRotation);
+ int position = navigationBarPosition(displayRotation);
if (position == NAV_BAR_BOTTOM) {
- outInsets.bottom = getNavigationBarHeight(displayRotation, uiMode);
+ outInsets.bottom = getNavigationBarHeight(displayRotation);
} else if (position == NAV_BAR_RIGHT) {
outInsets.right = getNavigationBarWidth(displayRotation, uiMode, position);
} else if (position == NAV_BAR_LEFT) {
@@ -2458,8 +2120,8 @@
}
@NavigationBarPosition
- int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) {
- if (INSETS_LAYOUT_GENERALIZATION && mNavigationBar != null) {
+ int navigationBarPosition(int displayRotation) {
+ if (mNavigationBar != null) {
final int gravity = mNavigationBar.getLayoutingAttrs(displayRotation).gravity;
switch (gravity) {
case Gravity.LEFT:
@@ -2470,14 +2132,7 @@
return NAV_BAR_BOTTOM;
}
}
- if (navigationBarCanMove() && displayWidth > displayHeight) {
- if (displayRotation == Surface.ROTATION_270) {
- return NAV_BAR_LEFT;
- } else if (displayRotation == Surface.ROTATION_90) {
- return NAV_BAR_RIGHT;
- }
- }
- return NAV_BAR_BOTTOM;
+ return NAV_BAR_INVALID;
}
/**
@@ -2491,16 +2146,6 @@
return mNavigationBarPosition;
}
- @WindowManagerPolicy.AltBarPosition
- int getAlternateStatusBarPosition() {
- return mStatusBarAltPosition;
- }
-
- @WindowManagerPolicy.AltBarPosition
- int getAlternateNavBarPosition() {
- return mNavigationBarAltPosition;
- }
-
/**
* A new window has been focused.
*/
@@ -2586,10 +2231,15 @@
}
void updateSystemBarAttributes() {
+ WindowState winCandidate = mFocusedWindow;
+ if (winCandidate == null && mTopFullscreenOpaqueWindowState != null
+ && (mTopFullscreenOpaqueWindowState.mAttrs.flags
+ & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0) {
+ // Only focusable window can take system bar control.
+ winCandidate = mTopFullscreenOpaqueWindowState;
+ }
// If there is no window focused, there will be nobody to handle the events
// anyway, so just hang on in whatever state we're in until things settle down.
- WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow
- : mTopFullscreenOpaqueWindowState;
if (winCandidate == null) {
return;
}
@@ -2790,25 +2440,22 @@
return source != null && Rect.intersects(win.getFrame(), source.getFrame());
}
- private Rect getBarContentFrameForWindow(WindowState win, int windowType) {
- final Rect rotatedBarFrame = win.mToken.getFixedRotationBarContentFrame(windowType);
- if (rotatedBarFrame != null) {
- return rotatedBarFrame;
- }
- if (!INSETS_LAYOUT_GENERALIZATION) {
- return mBarContentFrames.get(windowType);
- }
- // We only need a window specific information for the fixed rotation, use raw insets state
- // for all other cases.
- InsetsState insetsState = mDisplayContent.getInsetsStateController().getRawInsetsState();
+ private Rect getBarContentFrameForWindow(WindowState win, @InternalInsetsType int type) {
+ final DisplayFrames displayFrames = win.getDisplayFrames(mDisplayContent.mDisplayFrames);
+ final InsetsState state = displayFrames.mInsetsState;
final Rect tmpRect = new Rect();
- if (windowType == TYPE_NAVIGATION_BAR) {
- tmpRect.set(insetsState.getSource(InsetsState.ITYPE_NAVIGATION_BAR).getFrame());
+ sTmpDisplayCutoutSafe.set(displayFrames.mDisplayCutoutSafe);
+ if (type == ITYPE_STATUS_BAR) {
+ // The status bar content can extend into regular display cutout insets but not
+ // waterfall insets.
+ sTmpDisplayCutoutSafe.top =
+ Math.max(state.getDisplayCutout().getWaterfallInsets().top, 0);
}
- if (windowType == TYPE_STATUS_BAR) {
- tmpRect.set(insetsState.getSource(InsetsState.ITYPE_STATUS_BAR).getFrame());
+ final InsetsSource source = state.peekSource(type);
+ if (source != null) {
+ tmpRect.set(source.getFrame());
+ tmpRect.intersect(sTmpDisplayCutoutSafe);
}
- tmpRect.intersect(mDisplayContent.mDisplayFrames.mDisplayCutoutSafe);
return tmpRect;
}
@@ -2822,11 +2469,11 @@
* be drawn over letterboxed activity.
*/
@VisibleForTesting
- boolean isFullyTransparentAllowed(WindowState win, int windowType) {
+ boolean isFullyTransparentAllowed(WindowState win, @InternalInsetsType int type) {
if (win == null) {
return true;
}
- return win.isFullyTransparentBarAllowed(getBarContentFrameForWindow(win, windowType));
+ return win.isFullyTransparentBarAllowed(getBarContentFrameForWindow(win, type));
}
private boolean drawsBarBackground(WindowState win) {
@@ -2849,7 +2496,7 @@
for (int i = mStatusBarBackgroundWindows.size() - 1; i >= 0; i--) {
final WindowState window = mStatusBarBackgroundWindows.get(i);
drawBackground &= drawsBarBackground(window);
- isFullyTransparentAllowed &= isFullyTransparentAllowed(window, TYPE_STATUS_BAR);
+ isFullyTransparentAllowed &= isFullyTransparentAllowed(window, ITYPE_STATUS_BAR);
}
if (drawBackground) {
@@ -2889,7 +2536,7 @@
}
}
- if (!isFullyTransparentAllowed(mNavBarBackgroundWindow, TYPE_NAVIGATION_BAR)) {
+ if (!isFullyTransparentAllowed(mNavBarBackgroundWindow, ITYPE_NAVIGATION_BAR)) {
appearance |= APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 34e8149..b6552cb 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -1330,7 +1330,7 @@
case ActivityInfo.SCREEN_ORIENTATION_USER:
case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
// Works with any rotation except upside down.
- return (preferredRotation >= 0) && (preferredRotation != mUpsideDownRotation);
+ return (preferredRotation >= 0) && (preferredRotation != Surface.ROTATION_180);
}
return false;
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index cddb1e7..badb1f5 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -119,8 +119,12 @@
if (adjacentTaskFragments != null && adjacentTaskFragments.contains(
childTaskFragment)) {
- // Everything behind two adjacent TaskFragments are occluded.
- mBehindFullyOccludedContainer = true;
+ if (!childTaskFragment.isTranslucent(starting)
+ && !childTaskFragment.getAdjacentTaskFragment().isTranslucent(
+ starting)) {
+ // Everything behind two adjacent TaskFragments are occluded.
+ mBehindFullyOccludedContainer = true;
+ }
continue;
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index d202587..af91726 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -108,6 +108,16 @@
private final boolean mControllable;
+ /**
+ * Whether to forced the dimensions of the source window to the inset frame and crop out any
+ * overflow.
+ * Used to crop the taskbar inset source when a task animation is occurring to hide the taskbar
+ * rounded corners overlays.
+ *
+ * TODO: Remove when we enable shell transitions (b/202383002)
+ */
+ private boolean mCropToProvidingInsets = false;
+
InsetsSourceProvider(InsetsSource source, InsetsStateController stateController,
DisplayContent displayContent) {
mClientVisible = InsetsState.getDefaultVisibility(source.getType());
@@ -236,12 +246,12 @@
}
/** @return A new source computed by the specified window frame in the given display frames. */
- InsetsSource createSimulatedSource(DisplayFrames displayFrames, WindowFrames windowFrames) {
+ InsetsSource createSimulatedSource(DisplayFrames displayFrames, Rect winFrame) {
// Don't copy visible frame because it might not be calculated in the provided display
// frames and it is not significant for this usage.
final InsetsSource source = new InsetsSource(mSource.getType());
source.setVisible(mSource.isVisible());
- mTmpRect.set(windowFrames.mFrame);
+ mTmpRect.set(winFrame);
if (mFrameProvider != null) {
mFrameProvider.accept(displayFrames, mWin, mTmpRect);
}
@@ -303,6 +313,62 @@
mFakeControlTarget = fakeTarget;
}
+ /**
+ * Ensures that the inset source window is cropped so that anything that doesn't fit within the
+ * inset frame is cropped out until removeCropToProvidingInsetsBounds is called.
+ *
+ * The inset source surface will get cropped to the be of the size of the insets it's providing.
+ *
+ * For example, for the taskbar window which serves as the ITYPE_EXTRA_NAVIGATION_BAR inset
+ * source, the window is larger than the insets because of the rounded corners overlay, but
+ * during task animations we want to make sure that the overlay is cropped out of the window so
+ * that they don't hide the window animations.
+ *
+ * @param t The transaction to use to apply immediate overflow cropping operations.
+ *
+ * NOTE: The relies on the inset source window to have a leash (usually this would be a leash
+ * for the ANIMATION_TYPE_INSETS_CONTROL animation if the inset is controlled by the client)
+ *
+ * TODO: Remove when we migrate over to shell transitions (b/202383002)
+ */
+ void setCropToProvidingInsetsBounds(Transaction t) {
+ mCropToProvidingInsets = true;
+
+ if (mWin != null && mWin.mSurfaceAnimator.hasLeash()) {
+ // apply to existing leash
+ t.setWindowCrop(mWin.mSurfaceAnimator.mLeash, getProvidingInsetsBoundsCropRect());
+ }
+ }
+
+ /**
+ * Removes any overflow cropping and future cropping to the inset source window's leash that may
+ * have been set with a call to setCropToProvidingInsetsBounds().
+ * @param t The transaction to use to apply immediate removal of overflow cropping.
+ *
+ * TODO: Remove when we migrate over to shell transitions (b/202383002)
+ */
+ void removeCropToProvidingInsetsBounds(Transaction t) {
+ mCropToProvidingInsets = false;
+
+ // apply to existing leash
+ if (mWin != null && mWin.mSurfaceAnimator.hasLeash()) {
+ t.setWindowCrop(mWin.mSurfaceAnimator.mLeash, null);
+ }
+ }
+
+ private Rect getProvidingInsetsBoundsCropRect() {
+ Rect sourceWindowFrame = mWin.getFrame();
+ Rect insetFrame = getSource().getFrame();
+
+ // The rectangle in buffer space we want to crop to
+ return new Rect(
+ insetFrame.left - sourceWindowFrame.left,
+ insetFrame.top - sourceWindowFrame.top,
+ insetFrame.right - sourceWindowFrame.left,
+ insetFrame.bottom - sourceWindowFrame.top
+ );
+ }
+
void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) {
if (mSeamlessRotating) {
// We are un-rotating the window against the display rotation. We don't want the target
@@ -548,6 +614,11 @@
mCapturedLeash = animationLeash;
t.setPosition(mCapturedLeash, mSurfacePosition.x, mSurfacePosition.y);
+
+ if (mCropToProvidingInsets) {
+ // Apply crop to hide overflow
+ t.setWindowCrop(mCapturedLeash, getProvidingInsetsBoundsCropRect());
+ }
}
@Override
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 3f573fa..405a9e5 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -40,6 +40,7 @@
import android.annotation.Nullable;
import android.app.WindowConfiguration;
import android.app.WindowConfiguration.WindowingMode;
+import android.graphics.Rect;
import android.os.Trace;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -385,15 +386,14 @@
*
* @param win The owner window of insets provider.
* @param displayFrames The display frames to create insets source.
- * @param windowFrames The specified frames to represent the owner window.
+ * @param winFrame The frame of the insets source window.
*/
- void computeSimulatedState(WindowState win, DisplayFrames displayFrames,
- WindowFrames windowFrames) {
+ void computeSimulatedState(WindowState win, DisplayFrames displayFrames, Rect winFrame) {
final InsetsState state = displayFrames.mInsetsState;
for (int i = mProviders.size() - 1; i >= 0; i--) {
final InsetsSourceProvider provider = mProviders.valueAt(i);
if (provider.mWin == win) {
- state.addSource(provider.createSimulatedSource(displayFrames, windowFrames));
+ state.addSource(provider.createSimulatedSource(displayFrames, winFrame));
}
}
}
diff --git a/services/core/java/com/android/server/wm/PackageConfigPersister.java b/services/core/java/com/android/server/wm/PackageConfigPersister.java
index 6de8ef7..5145e8e 100644
--- a/services/core/java/com/android/server/wm/PackageConfigPersister.java
+++ b/services/core/java/com/android/server/wm/PackageConfigPersister.java
@@ -243,12 +243,16 @@
}
@GuardedBy("mLock")
- void onPackageUninstall(String packageName) {
+ void onPackageUninstall(String packageName, int userId) {
synchronized (mLock) {
- for (int i = mModified.size() - 1; i >= 0; i--) {
- final int userId = mModified.keyAt(i);
- removePackage(packageName, userId);
- }
+ removePackage(packageName, userId);
+ }
+ }
+
+ @GuardedBy("mLock")
+ void onPackageDataCleared(String packageName, int userId) {
+ synchronized (mLock) {
+ removePackage(packageName, userId);
}
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index ee05523..6d96cf0 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -473,7 +473,8 @@
*/
static void notifyAnimationCancelBeforeStart(IRecentsAnimationRunner recentsAnimationRunner) {
try {
- recentsAnimationRunner.onAnimationCanceled(null /* taskSnapshot */);
+ recentsAnimationRunner.onAnimationCanceled(null /* taskIds */,
+ null /* taskSnapshots */);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to cancel recents animation before start", e);
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 918eee9..634f489 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -852,37 +852,37 @@
mCanceled = true;
if (screenshot && !mPendingAnimations.isEmpty()) {
- final TaskAnimationAdapter adapter = mPendingAnimations.get(0);
- final Task task = adapter.mTask;
- // Screen shot previous task when next task starts transition and notify the runner.
- // We will actually finish the animation once the runner calls cleanUpScreenshot().
- final TaskSnapshot taskSnapshot = screenshotRecentTask(task);
+ final ArrayMap<Task, TaskSnapshot> snapshotMap = screenshotRecentTasks();
mPendingCancelWithScreenshotReorderMode = reorderMode;
- try {
- mRunner.onAnimationCanceled(taskSnapshot);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to cancel recents animation", e);
- }
- if (taskSnapshot != null) {
- // Defer until the runner calls back to cleanupScreenshot()
- adapter.setSnapshotOverlay(taskSnapshot);
+
+ if (!snapshotMap.isEmpty()) {
+ try {
+ int[] taskIds = new int[snapshotMap.size()];
+ TaskSnapshot[] snapshots = new TaskSnapshot[snapshotMap.size()];
+ for (int i = snapshotMap.size() - 1; i >= 0; i--) {
+ taskIds[i] = snapshotMap.keyAt(i).mTaskId;
+ snapshots[i] = snapshotMap.valueAt(i);
+ }
+ mRunner.onAnimationCanceled(taskIds, snapshots);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to cancel recents animation", e);
+ }
// Schedule a new failsafe for if the runner doesn't clean up the screenshot
scheduleFailsafe();
- } else {
- // Do a normal cancel since we couldn't screenshot
- mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
+ return;
}
- } else {
- // Otherwise, notify the runner and clean up the animation immediately
- // Note: In the fallback case, this can trigger multiple onAnimationCancel() calls
- // to the runner if we this actually triggers cancel twice on the caller
- try {
- mRunner.onAnimationCanceled(null /* taskSnapshot */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to cancel recents animation", e);
- }
- mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
+ // Fallback to a normal cancel since we couldn't screenshot
}
+
+ // Notify the runner and clean up the animation immediately
+ // Note: In the fallback case, this can trigger multiple onAnimationCancel() calls
+ // to the runner if we this actually triggers cancel twice on the caller
+ try {
+ mRunner.onAnimationCanceled(null /* taskIds */, null /* taskSnapshots */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to cancel recents animation", e);
+ }
+ mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
}
}
@@ -956,13 +956,23 @@
return mRequestDeferCancelUntilNextTransition && mCancelDeferredWithScreenshot;
}
- TaskSnapshot screenshotRecentTask(Task task) {
+ private ArrayMap<Task, TaskSnapshot> screenshotRecentTasks() {
final TaskSnapshotController snapshotController = mService.mTaskSnapshotController;
- final ArraySet<Task> tasks = Sets.newArraySet(task);
- snapshotController.snapshotTasks(tasks);
- snapshotController.addSkipClosingAppSnapshotTasks(tasks);
- return snapshotController.getSnapshot(task.mTaskId, task.mUserId,
- false /* restoreFromDisk */, false /* isLowResolution */);
+ final ArrayMap<Task, TaskSnapshot> snapshotMap = new ArrayMap<>();
+ for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+ final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
+ final Task task = adapter.mTask;
+ snapshotController.recordTaskSnapshot(task, false /* allowSnapshotHome */);
+ final TaskSnapshot snapshot = snapshotController.getSnapshot(task.mTaskId, task.mUserId,
+ false /* restoreFromDisk */, false /* isLowResolution */);
+ if (snapshot != null) {
+ snapshotMap.put(task, snapshot);
+ // Defer until the runner calls back to cleanupScreenshot()
+ adapter.setSnapshotOverlay(snapshot);
+ }
+ }
+ snapshotController.addSkipClosingAppSnapshotTasks(snapshotMap.keySet());
+ return snapshotMap;
}
void cleanupAnimation(@ReorderMode int reorderMode) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index e10ae37..c28d089 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1894,34 +1894,6 @@
return task != null && task == getTopDisplayFocusedRootTask();
}
- void updatePreviousProcess(ActivityRecord r) {
- // Now that this process has stopped, we may want to consider it to be the previous app to
- // try to keep around in case the user wants to return to it.
-
- // First, found out what is currently the foreground app, so that we don't blow away the
- // previous app if this activity is being hosted by the process that is actually still the
- // foreground.
- WindowProcessController fgApp = getItemFromRootTasks(rootTask -> {
- if (isTopDisplayFocusedRootTask(rootTask)) {
- final ActivityRecord resumedActivity = rootTask.getTopResumedActivity();
- if (resumedActivity != null) {
- return resumedActivity.app;
- } else if (rootTask.getTopPausingActivity() != null) {
- return rootTask.getTopPausingActivity().app;
- }
- }
- return null;
- });
-
- // Now set this one as the previous process, only if that really makes sense to.
- if (r.hasProcess() && fgApp != null && r.app != fgApp
- && r.lastVisibleTime > mService.mPreviousProcessVisibleTime
- && r.app != mService.mHomeProcess) {
- mService.mPreviousProcess = r.app;
- mService.mPreviousProcessVisibleTime = r.lastVisibleTime;
- }
- }
-
boolean attachApplication(WindowProcessController app) throws RemoteException {
try {
return mAttachApplicationHelper.process(app);
@@ -2583,26 +2555,6 @@
mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs);
}
- Task findRootTaskBehind(Task rootTask) {
- final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
- if (taskDisplayArea != null) {
- final boolean[] hasFound = new boolean[1];
- // TODO(b/175136051): should this be only the direct child root task?
- final Task rootTaskBehind = taskDisplayArea.getRootTask(task -> {
- if (hasFound[0]) {
- return true;
- }
- hasFound[0] = task == rootTask;
- return false;
- });
- if (rootTaskBehind != null) {
- return rootTaskBehind;
- }
- }
- throw new IllegalStateException("Failed to find a root task behind root task =" + rootTask
- + " in=" + taskDisplayArea);
- }
-
Configuration getDisplayOverrideConfiguration(int displayId) {
final DisplayContent displayContent = getDisplayContentOrCreate(displayId);
if (displayContent == null) {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index b8b9bcc..005544b 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -387,7 +387,7 @@
final ShortcutServiceInternal shortcutService =
LocalServices.getService(ShortcutServiceInternal.class);
final Intent[] shortcutIntents = shortcutService.createShortcutIntents(
- callingUid, callingPackage, packageName, shortcutId,
+ UserHandle.getUserId(callingUid), callingPackage, packageName, shortcutId,
user.getIdentifier(), callingPid, callingUid);
if (shortcutIntents == null || shortcutIntents.length == 0) {
throw new IllegalArgumentException("Invalid shortcut id");
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index d712bbf..50c9b31 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -188,6 +188,10 @@
mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
if (snapshotAnim != null) {
mSnapshot = freezer.takeSnapshotForAnimation();
+ if (mSnapshot == null) {
+ Slog.e(TAG, "No snapshot target to start animation on for " + mAnimatable);
+ return;
+ }
mSnapshot.startAnimation(t, snapshotAnim, type);
}
}
diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java
index c667db8..a7ef36b 100644
--- a/services/core/java/com/android/server/wm/SurfaceFreezer.java
+++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java
@@ -26,6 +26,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
+import android.util.Slog;
import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
@@ -49,8 +50,10 @@
*/
class SurfaceFreezer {
- private final Freezable mAnimatable;
- private final WindowManagerService mWmService;
+ private static final String TAG = "SurfaceFreezer";
+
+ private final @NonNull Freezable mAnimatable;
+ private final @NonNull WindowManagerService mWmService;
@VisibleForTesting
SurfaceControl mLeash;
Snapshot mSnapshot = null;
@@ -59,7 +62,7 @@
/**
* @param animatable The object to animate.
*/
- SurfaceFreezer(Freezable animatable, WindowManagerService service) {
+ SurfaceFreezer(@NonNull Freezable animatable, @NonNull WindowManagerService service) {
mAnimatable = animatable;
mWmService = service;
}
@@ -75,6 +78,7 @@
*/
void freeze(SurfaceControl.Transaction t, Rect startBounds, Point relativePosition,
@Nullable SurfaceControl freezeTarget) {
+ reset(t);
mFreezeBounds.set(startBounds);
mLeash = SurfaceAnimator.createAnimationLeash(mAnimatable, mAnimatable.getSurfaceControl(),
@@ -85,11 +89,14 @@
freezeTarget = freezeTarget != null ? freezeTarget : mAnimatable.getFreezeSnapshotTarget();
if (freezeTarget != null) {
- SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer = createSnapshotBuffer(
+ SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer = createSnapshotBufferInner(
freezeTarget, startBounds);
final HardwareBuffer buffer = screenshotBuffer == null ? null
: screenshotBuffer.getHardwareBuffer();
if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
+ // This can happen when display is not ready.
+ Slog.w(TAG, "Failed to capture screenshot for " + mAnimatable);
+ unfreeze(t);
return;
}
mSnapshot = new Snapshot(t, screenshotBuffer, mLeash);
@@ -123,6 +130,11 @@
* snapshot.
*/
void unfreeze(SurfaceControl.Transaction t) {
+ unfreezeInner(t);
+ mAnimatable.onUnfrozen();
+ }
+
+ private void unfreezeInner(SurfaceControl.Transaction t) {
if (mSnapshot != null) {
mSnapshot.cancelAnimation(t, false /* restarting */);
mSnapshot = null;
@@ -139,6 +151,22 @@
}
}
+ /** Resets the snapshot before taking another one if the animation hasn't been started yet. */
+ private void reset(SurfaceControl.Transaction t) {
+ // Those would have been taken by the SurfaceAnimator if the animation has been started, so
+ // we can remove the leash directly.
+ // No need to reset the mAnimatable leash, as this is called before a new animation leash is
+ // created, so another #onAnimationLeashCreated will be called.
+ if (mSnapshot != null) {
+ mSnapshot.destroy(t);
+ mSnapshot = null;
+ }
+ if (mLeash != null) {
+ t.remove(mLeash);
+ mLeash = null;
+ }
+ }
+
void setLayer(SurfaceControl.Transaction t, int layer) {
if (mLeash != null) {
t.setLayer(mLeash, layer);
@@ -171,6 +199,18 @@
return SurfaceControl.captureLayers(captureArgs);
}
+ @VisibleForTesting
+ SurfaceControl.ScreenshotHardwareBuffer createSnapshotBufferInner(
+ SurfaceControl target, Rect bounds) {
+ return createSnapshotBuffer(target, bounds);
+ }
+
+ @VisibleForTesting
+ GraphicBuffer createFromHardwareBufferInner(
+ SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer) {
+ return GraphicBuffer.createFromHardwareBuffer(screenshotBuffer.getHardwareBuffer());
+ }
+
class Snapshot {
private SurfaceControl mSurfaceControl;
private AnimationAdapter mAnimation;
@@ -181,10 +221,7 @@
*/
Snapshot(SurfaceControl.Transaction t,
SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer, SurfaceControl parent) {
- // We can't use a delegating constructor since we need to
- // reference this::onAnimationFinished
- GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
- screenshotBuffer.getHardwareBuffer());
+ GraphicBuffer graphicBuffer = createFromHardwareBufferInner(screenshotBuffer);
mSurfaceControl = mAnimatable.makeAnimationLeash()
.setName("snapshot anim: " + mAnimatable.toString())
@@ -258,5 +295,8 @@
* will be generated (but the rest of the freezing logic will still happen).
*/
@Nullable SurfaceControl getFreezeSnapshotTarget();
+
+ /** Called when the {@link #unfreeze(SurfaceControl.Transaction)} is called. */
+ void onUnfrozen();
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 9767c8b..367feac 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -155,6 +155,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
@@ -178,9 +179,11 @@
import android.util.TypedXmlSerializer;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
+import android.view.InsetsState;
import android.view.RemoteAnimationAdapter;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.view.TaskTransitionSpec;
import android.view.WindowManager;
import android.view.WindowManager.TransitionOldType;
import android.window.ITaskOrganizer;
@@ -214,7 +217,6 @@
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
-import java.util.function.Function;
import java.util.function.Predicate;
/**
@@ -2804,6 +2806,30 @@
return;
}
+ /**
+ * Account for specified insets to crop the animation bounds by to avoid the animation
+ * occurring over "out of bounds" regions
+ *
+ * For example this is used to make sure the tasks are cropped to be fully above the
+ * taskbar when animating.
+ *
+ * @param animationBounds The animations bounds to adjust to account for the custom spec insets.
+ */
+ void adjustAnimationBoundsForTransition(Rect animationBounds) {
+ TaskTransitionSpec spec = mWmService.mTaskTransitionSpec;
+ if (spec != null) {
+ for (@InsetsState.InternalInsetsType int insetType : spec.animationBoundInsets) {
+ InsetsSourceProvider insetProvider = getDisplayContent()
+ .getInsetsStateController()
+ .getSourceProvider(insetType);
+
+ Insets insets = insetProvider.getSource().calculateVisibleInsets(
+ animationBounds);
+ animationBounds.inset(insets);
+ }
+ }
+ }
+
void setDragResizing(boolean dragResizing, int dragResizeMode) {
if (mDragResizing != dragResizing) {
// No need to check if the mode is allowed if it's leaving dragResize
@@ -3199,12 +3225,6 @@
return isRootTask() && callback.test(this) ? this : null;
}
- @Nullable
- @Override
- <R> R getItemFromRootTasks(Function<Task, R> callback, boolean traverseTopToBottom) {
- return isRootTask() ? callback.apply(this) : null;
- }
-
/**
* @param canAffectSystemUiFlags If false, all windows in this task can not affect SystemUI
* flags. See {@link WindowState#canAffectSystemUiFlags()}.
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 04ec4bd..3b79274 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -29,12 +29,12 @@
import android.os.RemoteException;
import android.window.TaskSnapshot;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.SomeArgs;
import java.util.ArrayList;
class TaskChangeNotificationController {
- private static final int LOG_TASK_STATE_MSG = 1;
private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG = 2;
private static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 3;
private static final int NOTIFY_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 4;
@@ -64,12 +64,11 @@
// Delay in notifying task stack change listeners (in millis)
private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
- // Global lock used by the service the instantiate objects of this class.
- private final Object mServiceLock;
private final ActivityTaskSupervisor mTaskSupervisor;
private final Handler mHandler;
// Task stack change listeners in a remote process.
+ @GuardedBy("mRemoteTaskStackListeners")
private final RemoteCallbackList<ITaskStackListener> mRemoteTaskStackListeners =
new RemoteCallbackList<>();
@@ -77,6 +76,7 @@
* Task stack change listeners in a local process. Tracked separately so that they can be
* called on the same thread.
*/
+ @GuardedBy("mLocalTaskStackListeners")
private final ArrayList<ITaskStackListener> mLocalTaskStackListeners = new ArrayList<>();
private final TaskStackConsumer mNotifyTaskStackChanged = (l, m) -> {
@@ -195,12 +195,6 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case LOG_TASK_STATE_MSG: {
- synchronized (mServiceLock) {
- mTaskSupervisor.logRootTaskState();
- }
- break;
- }
case NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG:
forAllRemoteListeners(mNotifyTaskStackChanged, msg);
break;
@@ -283,41 +277,39 @@
}
}
- public TaskChangeNotificationController(Object serviceLock,
- ActivityTaskSupervisor taskSupervisor, Handler handler) {
- mServiceLock = serviceLock;
+ TaskChangeNotificationController(ActivityTaskSupervisor taskSupervisor, Handler handler) {
mTaskSupervisor = taskSupervisor;
mHandler = new MainHandler(handler.getLooper());
}
public void registerTaskStackListener(ITaskStackListener listener) {
- synchronized (mServiceLock) {
- if (listener != null) {
- if (Binder.getCallingPid() == android.os.Process.myPid()) {
- if (!mLocalTaskStackListeners.contains(listener)) {
- mLocalTaskStackListeners.add(listener);
- }
- } else {
- mRemoteTaskStackListeners.register(listener);
+ if (listener instanceof Binder) {
+ synchronized (mLocalTaskStackListeners) {
+ if (!mLocalTaskStackListeners.contains(listener)) {
+ mLocalTaskStackListeners.add(listener);
}
}
+ } else if (listener != null) {
+ synchronized (mRemoteTaskStackListeners) {
+ mRemoteTaskStackListeners.register(listener);
+ }
}
}
public void unregisterTaskStackListener(ITaskStackListener listener) {
- synchronized (mServiceLock) {
- if (listener != null) {
- if (Binder.getCallingPid() == android.os.Process.myPid()) {
- mLocalTaskStackListeners.remove(listener);
- } else {
- mRemoteTaskStackListeners.unregister(listener);
- }
+ if (listener instanceof Binder) {
+ synchronized (mLocalTaskStackListeners) {
+ mLocalTaskStackListeners.remove(listener);
+ }
+ } else if (listener != null) {
+ synchronized (mRemoteTaskStackListeners) {
+ mRemoteTaskStackListeners.unregister(listener);
}
}
}
private void forAllRemoteListeners(TaskStackConsumer callback, Message message) {
- synchronized (mServiceLock) {
+ synchronized (mRemoteTaskStackListeners) {
for (int i = mRemoteTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
try {
// Make a one-way callback to the listener
@@ -331,7 +323,7 @@
}
private void forAllLocalListeners(TaskStackConsumer callback, Message message) {
- synchronized (mServiceLock) {
+ synchronized (mLocalTaskStackListeners) {
for (int i = mLocalTaskStackListeners.size() - 1; i >= 0; i--) {
try {
callback.accept(mLocalTaskStackListeners.get(i), message);
@@ -344,7 +336,7 @@
/** Notifies all listeners when the task stack has changed. */
void notifyTaskStackChanged() {
- mHandler.sendEmptyMessage(LOG_TASK_STATE_MSG);
+ mTaskSupervisor.getActivityMetricsLogger().logWindowState();
mHandler.removeMessages(NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG);
final Message msg = mHandler.obtainMessage(NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG);
forAllLocalListeners(mNotifyTaskStackChanged, msg);
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index d4035b5..4d986a2 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -46,6 +46,7 @@
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.WindowConfiguration;
+import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.os.UserHandle;
import android.util.IntArray;
@@ -145,7 +146,8 @@
/**
* A launch root task for activity launching with {@link FLAG_ACTIVITY_LAUNCH_ADJACENT} flag.
*/
- private Task mLaunchAdjacentFlagRootTask;
+ @VisibleForTesting
+ Task mLaunchAdjacentFlagRootTask;
/**
* A focusable root task that is purposely to be positioned at the top. Although the root
@@ -1632,13 +1634,17 @@
* Whether we can show activity requesting the given min width/height in multi window below
* this {@link TaskDisplayArea}.
*/
- boolean supportsActivityMinWidthHeightMultiWindow(int minWidth, int minHeight) {
- final int configRespectsActivityMinWidthHeightMultiWindow =
- mAtmService.mRespectsActivityMinWidthHeightMultiWindow;
+ boolean supportsActivityMinWidthHeightMultiWindow(int minWidth, int minHeight,
+ @Nullable ActivityInfo activityInfo) {
+ if (activityInfo != null && !activityInfo.shouldCheckMinWidthHeightForMultiWindow()) {
+ return true;
+ }
if (minWidth <= 0 && minHeight <= 0) {
// No request min width/height.
return true;
}
+ final int configRespectsActivityMinWidthHeightMultiWindow =
+ mAtmService.mRespectsActivityMinWidthHeightMultiWindow;
if (configRespectsActivityMinWidthHeightMultiWindow == -1) {
// Device override to ignore min width/height.
return true;
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index b8393f6..49bbd8a 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -774,6 +774,7 @@
boolean gotOpaqueSplitScreenPrimary = false;
boolean gotOpaqueSplitScreenSecondary = false;
boolean gotTranslucentFullscreen = false;
+ boolean gotTranslucentAdjacent = false;
boolean gotTranslucentSplitScreenPrimary = false;
boolean gotTranslucentSplitScreenSecondary = false;
boolean shouldBeVisible = true;
@@ -802,6 +803,18 @@
final boolean hasRunningActivities = hasRunningActivity(other);
if (other == this) {
+ if (!adjacentTaskFragments.isEmpty() && !gotTranslucentAdjacent) {
+ // The z-order of this TaskFragment is in middle of two adjacent TaskFragments
+ // and it cannot be visible if the TaskFragment on top is not translucent and
+ // is fully occluding this one.
+ for (int j = adjacentTaskFragments.size() - 1; j >= 0; --j) {
+ final TaskFragment taskFragment = adjacentTaskFragments.get(j);
+ if (!taskFragment.isTranslucent(starting)
+ && taskFragment.getBounds().contains(this.getBounds())) {
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+ }
+ }
// Should be visible if there is no other fragment occluding it, unless it doesn't
// have any running activities, not starting one and not home stack.
shouldBeVisible = hasRunningActivities
@@ -871,6 +884,7 @@
|| otherTaskFrag.mAdjacentTaskFragment.isTranslucent(starting)) {
// Can be visible behind a translucent adjacent TaskFragments.
gotTranslucentFullscreen = true;
+ gotTranslucentAdjacent = true;
continue;
}
// Can not be visible behind adjacent TaskFragments.
@@ -1754,7 +1768,9 @@
return false;
}
- return tda.supportsActivityMinWidthHeightMultiWindow(mMinWidth, mMinHeight);
+ final ActivityRecord rootActivity = getTask().getRootActivity();
+ return tda.supportsActivityMinWidthHeightMultiWindow(mMinWidth, mMinHeight,
+ rootActivity != null ? rootActivity.info : null);
}
private int getTaskId() {
@@ -1982,8 +1998,8 @@
mTmpBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy();
- policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
- displayInfo.logicalHeight, displayInfo.displayCutout, mTmpInsets);
+ policy.getNonDecorInsetsLw(displayInfo.rotation,
+ displayInfo.displayCutout, mTmpInsets);
intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, mTmpInsets);
policy.convertNonDecorInsetsToStableInsets(mTmpInsets, displayInfo.rotation);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 6aa707c..bf11ebc 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -69,6 +69,7 @@
import com.google.android.collect.Sets;
import java.io.PrintWriter;
+import java.util.Set;
/**
* When an app token becomes invisible, we take a snapshot (bitmap) of the corresponding task and
@@ -184,7 +185,7 @@
* calling {@link #snapshotTasks} to ensure that the task has an up-to-date snapshot.
*/
@VisibleForTesting
- void addSkipClosingAppSnapshotTasks(ArraySet<Task> tasks) {
+ void addSkipClosingAppSnapshotTasks(Set<Task> tasks) {
if (shouldDisableSnapshots()) {
return;
}
@@ -297,11 +298,13 @@
final WindowState mainWindow = result.second;
final Rect contentInsets = getSystemBarInsets(task.getBounds(),
mainWindow.getInsetsStateWithVisibilityOverride());
- InsetUtils.addInsets(contentInsets, activity.getLetterboxInsets());
+ final Rect letterboxInsets = activity.getLetterboxInsets();
+ InsetUtils.addInsets(contentInsets, letterboxInsets);
builder.setIsRealSnapshot(true);
builder.setId(System.currentTimeMillis());
builder.setContentInsets(contentInsets);
+ builder.setLetterboxInsets(letterboxInsets);
final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE;
final boolean isShowWallpaper = mainWindow.hasWallpaper();
@@ -580,7 +583,8 @@
return null;
}
final Rect contentInsets = new Rect(systemBarInsets);
- InsetUtils.addInsets(contentInsets, topChild.getLetterboxInsets());
+ final Rect letterboxInsets = topChild.getLetterboxInsets();
+ InsetUtils.addInsets(contentInsets, letterboxInsets);
// Note, the app theme snapshot is never translucent because we enforce a non-translucent
// color above
@@ -589,9 +593,9 @@
topChild.mActivityComponent, hwBitmap.getHardwareBuffer(),
hwBitmap.getColorSpace(), mainWindow.getConfiguration().orientation,
mainWindow.getWindowConfiguration().getRotation(), new Point(taskWidth, taskHeight),
- contentInsets, false /* isLowResolution */, false /* isRealSnapshot */,
- task.getWindowingMode(), getAppearance(task), false /* isTranslucent */,
- false /* hasImeSurface */);
+ contentInsets, letterboxInsets, false /* isLowResolution */,
+ false /* isRealSnapshot */, task.getWindowingMode(),
+ getAppearance(task), false /* isTranslucent */, false /* hasImeSurface */);
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
index d3bfbab..9189e51 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
@@ -20,7 +20,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.app.ActivityManager;
-import android.window.TaskSnapshot;
import android.content.ComponentName;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
@@ -30,6 +29,7 @@
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.util.Slog;
+import android.window.TaskSnapshot;
import com.android.server.wm.nano.WindowManagerProtos.TaskSnapshotProto;
@@ -196,6 +196,8 @@
return new TaskSnapshot(proto.id, topActivityComponent, buffer,
hwBitmap.getColorSpace(), proto.orientation, proto.rotation, taskSize,
new Rect(proto.insetLeft, proto.insetTop, proto.insetRight, proto.insetBottom),
+ new Rect(proto.letterboxInsetLeft, proto.letterboxInsetTop,
+ proto.letterboxInsetRight, proto.letterboxInsetBottom),
loadLowResolutionBitmap, proto.isRealSnapshot, proto.windowingMode,
proto.appearance, proto.isTranslucent, false /* hasImeSurface */);
} catch (IOException e) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 60c4766..9fbcd7c 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -23,7 +23,6 @@
import android.annotation.NonNull;
import android.annotation.TestApi;
-import android.window.TaskSnapshot;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.os.Process;
@@ -31,6 +30,7 @@
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
+import android.window.TaskSnapshot;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -380,6 +380,10 @@
proto.insetTop = mSnapshot.getContentInsets().top;
proto.insetRight = mSnapshot.getContentInsets().right;
proto.insetBottom = mSnapshot.getContentInsets().bottom;
+ proto.letterboxInsetLeft = mSnapshot.getLetterboxInsets().left;
+ proto.letterboxInsetTop = mSnapshot.getLetterboxInsets().top;
+ proto.letterboxInsetRight = mSnapshot.getLetterboxInsets().right;
+ proto.letterboxInsetBottom = mSnapshot.getLetterboxInsets().bottom;
proto.isRealSnapshot = mSnapshot.isRealSnapshot();
proto.windowingMode = mSnapshot.getWindowingMode();
proto.appearance = mSnapshot.getAppearance();
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index a21e4f2..e054570 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -27,6 +27,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.IApplicationThread;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.RemoteException;
@@ -68,6 +69,7 @@
private ITransitionPlayer mTransitionPlayer;
final TransitionMetricsReporter mTransitionMetricsReporter = new TransitionMetricsReporter();
+ private IApplicationThread mTransitionPlayerThread;
final ActivityTaskManagerService mAtm;
final TaskSnapshotController mTaskSnapshotController;
@@ -136,7 +138,8 @@
return mCollectingTransition;
}
- void registerTransitionPlayer(@Nullable ITransitionPlayer player) {
+ void registerTransitionPlayer(@Nullable ITransitionPlayer player,
+ @Nullable IApplicationThread appThread) {
try {
// Note: asBinder() can be null if player is same process (likely in a test).
if (mTransitionPlayer != null) {
@@ -149,6 +152,7 @@
player.asBinder().linkToDeath(mTransitionPlayerDeath, 0);
}
mTransitionPlayer = player;
+ mTransitionPlayerThread = appThread;
} catch (RemoteException e) {
throw new RuntimeException("Unable to set transition player");
}
@@ -362,6 +366,9 @@
}
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Finish Transition: %s", record);
mPlayingTransitions.remove(record);
+ if (mPlayingTransitions.isEmpty()) {
+ setAnimationRunning(false /* running */);
+ }
record.finishTransition();
mRunningLock.doNotifyLocked();
}
@@ -371,9 +378,22 @@
throw new IllegalStateException("Trying to move non-collecting transition to playing");
}
mCollectingTransition = null;
+ if (mPlayingTransitions.isEmpty()) {
+ setAnimationRunning(true /* running */);
+ }
mPlayingTransitions.add(transition);
}
+ private void setAnimationRunning(boolean running) {
+ if (mTransitionPlayerThread == null) return;
+ final WindowProcessController wpc = mAtm.getProcessController(mTransitionPlayerThread);
+ if (wpc == null) {
+ Slog.w(TAG, "Unable to find process for player thread=" + mTransitionPlayerThread);
+ return;
+ }
+ wpc.setRunningRemoteAnimation(running);
+ }
+
void abort(Transition transition) {
if (transition != mCollectingTransition) {
throw new IllegalStateException("Too late to abort.");
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index b4e07d3..9865506 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -108,6 +108,7 @@
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
import java.util.function.Consumer;
@@ -613,7 +614,6 @@
final DisplayContent dc = getDisplayContent();
if (dc != null) {
mSurfaceFreezer.unfreeze(getSyncTransaction());
- dc.mChangingContainers.remove(this);
}
while (!mChildren.isEmpty()) {
final E child = mChildren.peekLast();
@@ -1051,9 +1051,6 @@
// part of a change transition.
if (!visible) {
mSurfaceFreezer.unfreeze(getSyncTransaction());
- if (mDisplayContent != null) {
- mDisplayContent.mChangingContainers.remove(this);
- }
}
WindowContainer parent = getParent();
if (parent != null) {
@@ -2116,47 +2113,6 @@
}
/**
- * Finds the first non {@code null} return value from calling the callback on all root
- * {@link Task} at or below this container.
- * @param callback Applies on each root {@link Task} found and stops the search if it
- * returns non {@code null}.
- * @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in
- * terms of z-order, else from bottom-to-top.
- * @return the first returned object that is not {@code null}. Returns {@code null} if not
- * found.
- */
- @Nullable
- <R> R getItemFromRootTasks(Function<Task, R> callback, boolean traverseTopToBottom) {
- int count = mChildren.size();
- if (traverseTopToBottom) {
- for (int i = count - 1; i >= 0; --i) {
- R result = (R) mChildren.get(i).getItemFromRootTasks(callback, traverseTopToBottom);
- if (result != null) {
- return result;
- }
- }
- } else {
- for (int i = 0; i < count; i++) {
- R result = (R) mChildren.get(i).getItemFromRootTasks(callback, traverseTopToBottom);
- if (result != null) {
- return result;
- }
- // Root tasks may be removed from this display. Ensure each task will be processed
- // and the loop will end.
- int newCount = mChildren.size();
- i -= count - newCount;
- count = newCount;
- }
- }
- return null;
- }
-
- @Nullable
- <R> R getItemFromRootTasks(Function<Task, R> callback) {
- return getItemFromRootTasks(callback, true /* traverseTopToBottom */);
- }
-
- /**
* Returns 1, 0, or -1 depending on if this container is greater than, equal to, or lesser than
* the input container in terms of z-order.
*/
@@ -2631,6 +2587,13 @@
}
@Override
+ public void onUnfrozen() {
+ if (mDisplayContent != null) {
+ mDisplayContent.mChangingContainers.remove(this);
+ }
+ }
+
+ @Override
public Builder makeAnimationLeash() {
return makeSurface().setContainerLayer();
}
@@ -2715,6 +2678,9 @@
// Separate position and size for use in animators.
final Rect screenBounds = getAnimationBounds(appRootTaskClipMode);
mTmpRect.set(screenBounds);
+ if (this.asTask() != null && isTaskTransitOld(transit)) {
+ this.asTask().adjustAnimationBoundsForTransition(mTmpRect);
+ }
getAnimationPosition(mTmpPoint);
mTmpRect.offsetTo(0, 0);
@@ -2810,6 +2776,11 @@
if (isTaskTransitOld(transit)) {
animationRunnerBuilder.setTaskBackgroundColor(getTaskAnimationBackgroundColor());
+ // TODO: Remove when we migrate to shell (b/202383002)
+ if (mWmService.mTaskTransitionSpec != null) {
+ animationRunnerBuilder.hideInsetSourceViewOverflows(
+ mWmService.mTaskTransitionSpec.animationBoundInsets);
+ }
}
animationRunnerBuilder.build()
@@ -3554,6 +3525,26 @@
}
}
+ private void hideInsetSourceViewOverflows(Set<Integer> insetTypes) {
+ final ArrayList<SurfaceControl> surfaceControls =
+ new ArrayList<>(insetTypes.size());
+
+ for (int insetType : insetTypes) {
+ InsetsSourceProvider insetProvider = getDisplayContent().getInsetsStateController()
+ .getSourceProvider(insetType);
+
+ // Will apply it immediately to current leash and to all future inset animations
+ // until we disable it.
+ insetProvider.setCropToProvidingInsetsBounds(getPendingTransaction());
+
+ // Only clear the size restriction of the inset once the surface animation is over
+ // and not if it's canceled to be replace by another animation.
+ mOnAnimationFinished.add(() -> {
+ insetProvider.removeCropToProvidingInsetsBounds(getPendingTransaction());
+ });
+ }
+ }
+
private IAnimationStarter build() {
return (Transaction t, AnimationAdapter adapter, boolean hidden,
@AnimationType int type, @Nullable AnimationAdapter snapshotAnim) -> {
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index c3d3c82..a307ee8 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -72,11 +72,6 @@
final Rect mCompatFrame = new Rect();
/**
- * {@code true} if the window frame is a simulated frame and attached to a decor window.
- */
- boolean mIsSimulatingDecorWindow = false;
-
- /**
* Whether the parent frame would have been different if there was no display cutout.
*/
private boolean mParentFrameWasClippedByDisplayCutout;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 60c660f..af59a1d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -6935,8 +6935,7 @@
final DisplayContent dc = mRoot.getDisplayContent(displayId);
if (dc != null) {
final DisplayInfo di = dc.getDisplayInfo();
- dc.getDisplayPolicy().getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
- di.displayCutout, outInsets);
+ dc.getDisplayPolicy().getStableInsetsLw(di.rotation, di.displayCutout, outInsets);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 79a46ea..f6c8356 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -45,6 +45,7 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
+import android.app.IApplicationThread;
import android.app.WindowConfiguration;
import android.content.ActivityNotFoundException;
import android.content.Intent;
@@ -565,17 +566,17 @@
case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: {
final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
final Task task = wc != null ? wc.asTask() : null;
+ final boolean clearRoot = hop.getToTop();
if (task == null) {
throw new IllegalArgumentException("Cannot set non-task as launch root: " + wc);
} else if (!task.mCreatedByOrganizer) {
throw new UnsupportedOperationException(
"Cannot set non-organized task as adjacent flag root: " + wc);
- } else if (task.getAdjacentTaskFragment() == null) {
+ } else if (task.getAdjacentTaskFragment() == null && !clearRoot) {
throw new UnsupportedOperationException(
"Cannot set non-adjacent task as adjacent flag root: " + wc);
}
- final boolean clearRoot = hop.getToTop();
task.getDisplayArea().setLaunchAdjacentFlagRootTask(clearRoot ? null : task);
break;
}
@@ -1030,10 +1031,18 @@
@Override
public void registerTransitionPlayer(ITransitionPlayer player) {
enforceTaskPermission("registerTransitionPlayer()");
+ final int callerPid = Binder.getCallingPid();
+ final int callerUid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- mTransitionController.registerTransitionPlayer(player);
+ final WindowProcessController wpc =
+ mService.getProcessController(callerPid, callerUid);
+ IApplicationThread appThread = null;
+ if (wpc != null) {
+ appThread = wpc.getThread();
+ }
+ mTransitionController.registerTransitionPlayer(player, appThread);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -1176,6 +1185,12 @@
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
return;
}
+ if (!ownerActivity.isResizeable()) {
+ final IllegalArgumentException exception = new IllegalArgumentException("Not allowed"
+ + " to operate with non-resizable owner Activity");
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+ return;
+ }
// The ownerActivity has to belong to the same app as the root Activity of the target Task.
final ActivityRecord rootActivity = ownerActivity.getTask().getRootActivity();
if (rootActivity.getUid() != ownerActivity.getUid()) {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 81878e3..1cfbe07 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1110,7 +1110,7 @@
void updateProcessInfo(boolean updateServiceConnectionActivities, boolean activityChange,
boolean updateOomAdj, boolean addPendingTopUid) {
if (addPendingTopUid) {
- mAtm.mAmInternal.addPendingTopUid(mUid, mPid);
+ addToPendingTop();
}
if (updateOomAdj) {
prepareOomAdjustment();
@@ -1121,6 +1121,11 @@
mAtm.mH.sendMessage(m);
}
+ /** Makes the process have top state before oom-adj is computed from a posted message. */
+ void addToPendingTop() {
+ mAtm.mAmInternal.addPendingTopUid(mUid, mPid);
+ }
+
void updateServiceConnectionActivities() {
// Posting on handler so WM lock isn't held when we call into AM.
mAtm.mH.sendMessage(PooledLambda.obtainMessage(
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 3a98a37..855756c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -33,7 +33,6 @@
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.SurfaceControl.Transaction;
import static android.view.SurfaceControl.getGlobalTransaction;
-import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
@@ -467,9 +466,6 @@
private final ClientWindowFrames mClientWindowFrames = new ClientWindowFrames();
- /** The frames used to compute a temporal layout appearance. */
- private WindowFrames mSimulatedWindowFrames;
-
/**
* List of rects where system gestures should be ignored.
*
@@ -1308,10 +1304,6 @@
}
}
- // TODO(b/161810301): Remove this after INSETS_LAYOUT_GENERALIZATION is removed.
- void computeFrameAndUpdateSourceFrame(DisplayFrames displayFrames) {
- }
-
@Override
public Rect getBounds() {
// The window bounds are used for layout in screen coordinates. If the token has bounds for
@@ -1352,9 +1344,6 @@
}
WindowManager.LayoutParams getLayoutingAttrs(int rotation) {
- if (!INSETS_LAYOUT_GENERALIZATION) {
- return mAttrs;
- }
final WindowManager.LayoutParams[] paramsForRotation = mAttrs.paramsForRotation;
if (paramsForRotation == null || paramsForRotation.length != 4
|| paramsForRotation[rotation] == null) {
@@ -1521,9 +1510,9 @@
/** @return The display frames in use by this window. */
DisplayFrames getDisplayFrames(DisplayFrames originalFrames) {
- final DisplayFrames diplayFrames = mToken.getFixedRotationTransformDisplayFrames();
- if (diplayFrames != null) {
- return diplayFrames;
+ final DisplayFrames displayFrames = mToken.getFixedRotationTransformDisplayFrames();
+ if (displayFrames != null) {
+ return displayFrames;
}
return originalFrames;
}
@@ -5510,22 +5499,6 @@
return mWindowFrames;
}
- /**
- * If the simulated frame is set, the computed result won't be used in real layout. So this
- * frames must be cleared when the simulated computation is done.
- */
- void setSimulatedWindowFrames(WindowFrames windowFrames) {
- mSimulatedWindowFrames = windowFrames;
- }
-
- /**
- * Use this method only when the simulated frames may be set, so it is clearer that the calling
- * path may be used to simulate layout.
- */
- WindowFrames getLayoutingWindowFrames() {
- return mSimulatedWindowFrames != null ? mSimulatedWindowFrames : mWindowFrames;
- }
-
void resetContentChanged() {
mWindowFrames.setContentChanged(false);
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 34b63ac..b1cad7c 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -16,11 +16,9 @@
package com.android.server.wm;
-import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
@@ -47,7 +45,6 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
-import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayAdjustments.FixedRotationAdjustments;
import android.view.DisplayInfo;
@@ -133,7 +130,6 @@
*/
final ArrayList<WindowToken> mAssociatedTokens = new ArrayList<>(3);
final ArrayList<WindowContainer<?>> mRotatedContainers = new ArrayList<>(3);
- final SparseArray<Rect> mBarContentFrames = new SparseArray<>();
boolean mIsTransforming = true;
FixedRotationTransformState(DisplayInfo rotatedDisplayInfo,
@@ -438,27 +434,6 @@
: null;
}
- Rect getFixedRotationBarContentFrame(int windowType) {
- if (!isFixedRotationTransforming()) {
- return null;
- }
- if (!INSETS_LAYOUT_GENERALIZATION) {
- return mFixedRotationTransformState.mBarContentFrames.get(windowType);
- }
- final DisplayFrames displayFrames = mFixedRotationTransformState.mDisplayFrames;
- final Rect tmpRect = new Rect();
- if (windowType == TYPE_NAVIGATION_BAR) {
- tmpRect.set(displayFrames.mInsetsState.getSource(InsetsState.ITYPE_NAVIGATION_BAR)
- .getFrame());
- }
- if (windowType == TYPE_STATUS_BAR) {
- tmpRect.set(displayFrames.mInsetsState.getSource(InsetsState.ITYPE_STATUS_BAR)
- .getFrame());
- }
- tmpRect.intersect(displayFrames.mDisplayCutoutSafe);
- return tmpRect;
- }
-
InsetsState getFixedRotationTransformInsetsState() {
return isFixedRotationTransforming()
? mFixedRotationTransformState.mDisplayFrames.mInsetsState
@@ -474,8 +449,7 @@
mFixedRotationTransformState = new FixedRotationTransformState(info, displayFrames,
new Configuration(config), mDisplayContent.getRotation());
mFixedRotationTransformState.mAssociatedTokens.add(this);
- mDisplayContent.getDisplayPolicy().simulateLayoutDisplay(displayFrames,
- mFixedRotationTransformState.mBarContentFrames);
+ mDisplayContent.getDisplayPolicy().simulateLayoutDisplay(displayFrames);
onFixedRotationStatePrepared();
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 7e092c1..978eb1d 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -147,7 +147,7 @@
"android.hardware.broadcastradio@1.0",
"android.hardware.broadcastradio@1.1",
"android.hardware.contexthub@1.0",
- "android.hardware.gnss-V1-cpp",
+ "android.hardware.gnss-V2-cpp",
"android.hardware.gnss@1.0",
"android.hardware.gnss@1.1",
"android.hardware.gnss@2.0",
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 1639d82..6be872f 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -19,16 +19,16 @@
#define LOG_NDEBUG 0
#include <android/hardware/gnss/1.0/IGnss.h>
-#include <android/hardware/gnss/1.1/IGnss.h>
-#include <android/hardware/gnss/2.0/IGnss.h>
-#include <android/hardware/gnss/2.1/IGnss.h>
-
#include <android/hardware/gnss/1.0/IGnssMeasurement.h>
+#include <android/hardware/gnss/1.1/IGnss.h>
#include <android/hardware/gnss/1.1/IGnssMeasurement.h>
+#include <android/hardware/gnss/2.0/IGnss.h>
#include <android/hardware/gnss/2.0/IGnssMeasurement.h>
+#include <android/hardware/gnss/2.1/IGnss.h>
#include <android/hardware/gnss/2.1/IGnssAntennaInfo.h>
#include <android/hardware/gnss/2.1/IGnssMeasurement.h>
#include <android/hardware/gnss/BnGnss.h>
+#include <android/hardware/gnss/BnGnssBatchingCallback.h>
#include <android/hardware/gnss/BnGnssCallback.h>
#include <android/hardware/gnss/BnGnssMeasurementCallback.h>
#include <android/hardware/gnss/BnGnssPowerIndicationCallback.h>
@@ -36,8 +36,19 @@
#include <android/hardware/gnss/measurement_corrections/1.0/IMeasurementCorrections.h>
#include <android/hardware/gnss/measurement_corrections/1.1/IMeasurementCorrections.h>
#include <android/hardware/gnss/visibility_control/1.0/IGnssVisibilityControl.h>
+#include <arpa/inet.h>
#include <binder/IServiceManager.h>
+#include <linux/in.h>
+#include <linux/in6.h>
#include <nativehelper/JNIHelp.h>
+#include <pthread.h>
+#include <string.h>
+#include <utils/SystemClock.h>
+
+#include <cinttypes>
+#include <iomanip>
+#include <limits>
+
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"
#include "gnss/GnssAntennaInfoCallback.h"
@@ -49,16 +60,6 @@
#include "utils/Log.h"
#include "utils/misc.h"
-#include <arpa/inet.h>
-#include <cinttypes>
-#include <iomanip>
-#include <limits>
-#include <linux/in.h>
-#include <linux/in6.h>
-#include <pthread.h>
-#include <string.h>
-#include <utils/SystemClock.h>
-
static jclass class_location;
static jclass class_gnssNavigationMessage;
static jclass class_gnssPowerStats;
@@ -198,10 +199,13 @@
using android::hardware::gnss::IGnssPowerIndicationCallback;
using android::hardware::gnss::PsdsType;
using IGnssAidl = android::hardware::gnss::IGnss;
+using IGnssBatchingAidl = android::hardware::gnss::IGnssBatching;
+using IGnssBatchingCallbackAidl = android::hardware::gnss::IGnssBatchingCallback;
using IGnssCallbackAidl = android::hardware::gnss::IGnssCallback;
using IGnssPsdsAidl = android::hardware::gnss::IGnssPsds;
using IGnssPsdsCallbackAidl = android::hardware::gnss::IGnssPsdsCallback;
using IGnssConfigurationAidl = android::hardware::gnss::IGnssConfiguration;
+using GnssLocationAidl = android::hardware::gnss::GnssLocation;
struct GnssDeathRecipient : virtual public hidl_death_recipient
{
@@ -223,6 +227,7 @@
sp<IGnss_V2_0> gnssHal_V2_0 = nullptr;
sp<IGnss_V2_1> gnssHal_V2_1 = nullptr;
sp<IGnssAidl> gnssHalAidl = nullptr;
+sp<IGnssBatchingAidl> gnssBatchingAidlIface = nullptr;
sp<IGnssPsdsAidl> gnssPsdsAidlIface = nullptr;
sp<IGnssXtra> gnssXtraIface = nullptr;
sp<IAGnssRil_V1_0> agnssRilIface = nullptr;
@@ -296,11 +301,54 @@
const char* mNativeString;
};
+static jobject translateGnssLocation(JNIEnv* env, const GnssLocationAidl& location) {
+ JavaObject object(env, class_location, method_locationCtor, "gps");
+
+ uint32_t flags = static_cast<uint32_t>(location.gnssLocationFlags);
+ if (flags & GnssLocationAidl::HAS_LAT_LONG) {
+ SET(Latitude, location.latitudeDegrees);
+ SET(Longitude, location.longitudeDegrees);
+ }
+ if (flags & GnssLocationAidl::HAS_ALTITUDE) {
+ SET(Altitude, location.altitudeMeters);
+ }
+ if (flags & GnssLocationAidl::HAS_SPEED) {
+ SET(Speed, (float)location.speedMetersPerSec);
+ }
+ if (flags & GnssLocationAidl::HAS_BEARING) {
+ SET(Bearing, (float)location.bearingDegrees);
+ }
+ if (flags & GnssLocationAidl::HAS_HORIZONTAL_ACCURACY) {
+ SET(Accuracy, (float)location.horizontalAccuracyMeters);
+ }
+ if (flags & GnssLocationAidl::HAS_VERTICAL_ACCURACY) {
+ SET(VerticalAccuracyMeters, (float)location.verticalAccuracyMeters);
+ }
+ if (flags & GnssLocationAidl::HAS_SPEED_ACCURACY) {
+ SET(SpeedAccuracyMetersPerSecond, (float)location.speedAccuracyMetersPerSecond);
+ }
+ if (flags & GnssLocationAidl::HAS_BEARING_ACCURACY) {
+ SET(BearingAccuracyDegrees, (float)location.bearingAccuracyDegrees);
+ }
+ SET(Time, location.timestampMillis);
+
+ flags = static_cast<uint32_t>(location.elapsedRealtime.flags);
+ if (flags & android::hardware::gnss::ElapsedRealtime::HAS_TIMESTAMP_NS) {
+ SET(ElapsedRealtimeNanos, location.elapsedRealtime.timestampNs);
+ }
+ if (flags & android::hardware::gnss::ElapsedRealtime::HAS_TIME_UNCERTAINTY_NS) {
+ SET(ElapsedRealtimeUncertaintyNanos,
+ static_cast<double>(location.elapsedRealtime.timeUncertaintyNs));
+ }
+
+ return object.get();
+}
+
static jobject translateGnssLocation(JNIEnv* env,
const GnssLocation_V1_0& location) {
JavaObject object(env, class_location, method_locationCtor, "gps");
- uint16_t flags = static_cast<uint32_t>(location.gnssLocationFlags);
+ uint16_t flags = static_cast<uint16_t>(location.gnssLocationFlags);
if (flags & GnssLocationFlags::HAS_LAT_LONG) {
SET(Latitude, location.latitudeDegrees);
SET(Longitude, location.longitudeDegrees);
@@ -1199,6 +1247,17 @@
}
/*
+ * GnssBatchingCallbackAidl class implements the callback methods required by the
+ * android::hardware::gnss::IGnssBatching interface.
+ */
+struct GnssBatchingCallbackAidl : public android::hardware::gnss::BnGnssBatchingCallback {
+ Status gnssLocationBatchCb(const std::vector<GnssLocationAidl>& locations) {
+ GnssBatchingCallbackUtil::gnssLocationBatchCbImpl(hidl_vec<GnssLocationAidl>(locations));
+ return Status::ok();
+ }
+};
+
+/*
* GnssBatchingCallback_V1_0 class implements the callback methods required by the
* IGnssBatching 1.0 interface.
*/
@@ -1612,8 +1671,13 @@
gnssGeofencingIface = gnssGeofencing;
}
- // If IGnssBatching.hal@2.0 is not supported, use IGnssBatching.hal@1.0
- if (gnssHal_V2_0 != nullptr) {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ sp<IGnssBatchingAidl> gnssBatchingAidl;
+ auto status = gnssHalAidl->getExtensionGnssBatching(&gnssBatchingAidl);
+ if (checkAidlStatus(status, "Unable to get a handle to GnssBatching interface.")) {
+ gnssBatchingAidlIface = gnssBatchingAidl;
+ }
+ } else if (gnssHal_V2_0 != nullptr) {
auto gnssBatching_V2_0 = gnssHal_V2_0->getExtensionGnssBatching_2_0();
if (!gnssBatching_V2_0.isOk()) {
ALOGD("Unable to get a handle to GnssBatching_V2_0");
@@ -2722,19 +2786,29 @@
}
static jint android_location_gnss_hal_GnssNative_get_batch_size(JNIEnv*) {
- if (gnssBatchingIface == nullptr) {
- return 0; // batching not supported, size = 0
+ if (gnssBatchingAidlIface != nullptr) {
+ int size = 0;
+ auto status = gnssBatchingAidlIface->getBatchSize(&size);
+ if (!checkAidlStatus(status, "IGnssBatchingAidl getBatchSize() failed")) {
+ return 0;
+ }
+ return size;
+ } else if (gnssBatchingIface != nullptr) {
+ auto result = gnssBatchingIface->getBatchSize();
+ if (!checkHidlReturn(result, "IGnssBatching getBatchSize() failed.")) {
+ return 0; // failure in binder, don't support batching
+ }
+ return static_cast<jint>(result);
}
- auto result = gnssBatchingIface->getBatchSize();
- if (!checkHidlReturn(result, "IGnssBatching getBatchSize() failed.")) {
- return 0; // failure in binder, don't support batching
- }
-
- return static_cast<jint>(result);
+ return 0; // batching not supported, size = 0
}
static jboolean android_location_gnss_hal_GnssNative_init_batching(JNIEnv*, jclass) {
- if (gnssBatchingIface_V2_0 != nullptr) {
+ if (gnssBatchingAidlIface != nullptr) {
+ sp<IGnssBatchingCallbackAidl> gnssBatchingCbIface = new GnssBatchingCallbackAidl();
+ auto status = gnssBatchingAidlIface->init(gnssBatchingCbIface);
+ return checkAidlStatus(status, "IGnssBatchingAidl init() failed.");
+ } else if (gnssBatchingIface_V2_0 != nullptr) {
sp<IGnssBatchingCallback_V2_0> gnssBatchingCbIface_V2_0 = new GnssBatchingCallback_V2_0();
auto result = gnssBatchingIface_V2_0->init_2_0(gnssBatchingCbIface_V2_0);
return checkHidlReturn(result, "IGnssBatching init_2_0() failed.");
@@ -2748,19 +2822,18 @@
}
static void android_location_gnss_hal_GnssNative_cleanup_batching(JNIEnv*, jclass) {
- if (gnssBatchingIface == nullptr) {
- return; // batching not supported
+ if (gnssBatchingAidlIface != nullptr) {
+ auto status = gnssBatchingAidlIface->cleanup();
+ checkAidlStatus(status, "IGnssBatchingAidl cleanup() failed");
+ } else if (gnssBatchingIface != nullptr) {
+ auto result = gnssBatchingIface->cleanup();
+ checkHidlReturn(result, "IGnssBatching cleanup() failed.");
}
- auto result = gnssBatchingIface->cleanup();
- checkHidlReturn(result, "IGnssBatching cleanup() failed.");
+ return;
}
static jboolean android_location_gnss_hal_GnssNative_start_batch(JNIEnv*, jclass, jlong periodNanos,
jboolean wakeOnFifoFull) {
- if (gnssBatchingIface == nullptr) {
- return JNI_FALSE; // batching not supported
- }
-
IGnssBatching_V1_0::Options options;
options.periodNanos = periodNanos;
if (wakeOnFifoFull) {
@@ -2769,24 +2842,36 @@
options.flags = 0;
}
- auto result = gnssBatchingIface->start(options);
- return checkHidlReturn(result, "IGnssBatching start() failed.");
+ if (gnssBatchingAidlIface != nullptr) {
+ auto status = gnssBatchingAidlIface->start(periodNanos, (int)options.flags);
+ return checkAidlStatus(status, "IGnssBatchingAidl start() failed.");
+ } else if (gnssBatchingIface != nullptr) {
+ auto result = gnssBatchingIface->start(options);
+ return checkHidlReturn(result, "IGnssBatching start() failed.");
+ }
+ return JNI_FALSE; // batching not supported
}
static void android_location_gnss_hal_GnssNative_flush_batch(JNIEnv*, jclass) {
- if (gnssBatchingIface == nullptr) {
- return; // batching not supported
+ if (gnssBatchingAidlIface != nullptr) {
+ auto status = gnssBatchingAidlIface->flush();
+ checkAidlStatus(status, "IGnssBatchingAidl flush() failed.");
+ } else if (gnssBatchingIface != nullptr) {
+ auto result = gnssBatchingIface->flush();
+ checkHidlReturn(result, "IGnssBatching flush() failed.");
}
- auto result = gnssBatchingIface->flush();
- checkHidlReturn(result, "IGnssBatching flush() failed.");
+ return;
}
static jboolean android_location_gnss_hal_GnssNative_stop_batch(JNIEnv*, jclass) {
- if (gnssBatchingIface == nullptr) {
- return JNI_FALSE; // batching not supported
+ if (gnssBatchingAidlIface != nullptr) {
+ auto status = gnssBatchingAidlIface->stop();
+ return checkAidlStatus(status, "IGnssBatchingAidl stop() failed.");
+ } else if (gnssBatchingIface != nullptr) {
+ auto result = gnssBatchingIface->stop();
+ return checkHidlReturn(result, "IGnssBatching stop() failed.");
}
- auto result = gnssBatchingIface->stop();
- return checkHidlReturn(result, "IGnssBatching stop() failed.");
+ return JNI_FALSE; // batching not supported
}
static jboolean android_location_GnssVisibilityControl_enable_nfw_location_access(
diff --git a/services/core/jni/gnss/Android.bp b/services/core/jni/gnss/Android.bp
index 444cb8b..9085fa7 100644
--- a/services/core/jni/gnss/Android.bp
+++ b/services/core/jni/gnss/Android.bp
@@ -40,7 +40,7 @@
"liblog",
"libnativehelper",
"libutils",
- "android.hardware.gnss-V1-cpp",
+ "android.hardware.gnss-V2-cpp",
"android.hardware.gnss@1.0",
"android.hardware.gnss@1.1",
"android.hardware.gnss@2.0",
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 34576a6..92c0a34 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -699,6 +699,14 @@
private DevicePolicyConstants mConstants;
+ /**
+ * User to be switched to on {@code logoutUser()}.
+ *
+ * <p>Only used on devices with headless system user mode
+ */
+ @GuardedBy("getLockObject()")
+ private @UserIdInt int mLogoutUserId = UserHandle.USER_NULL;
+
private static final boolean ENABLE_LOCK_GUARD = true;
/**
@@ -3831,9 +3839,17 @@
}
}
+ private boolean notSupportedOnAutomotive(String method) {
+ if (mIsAutomotive) {
+ Slogf.i(LOG_TAG, "%s is not supported on automotive builds", method);
+ return true;
+ }
+ return false;
+ }
+
@Override
public void setPasswordQuality(ComponentName who, int quality, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || notSupportedOnAutomotive("setPasswordQuality")) {
return;
}
Objects.requireNonNull(who, "ComponentName is null");
@@ -4068,7 +4084,7 @@
@Override
public void setPasswordMinimumLength(ComponentName who, int length, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || notSupportedOnAutomotive("setPasswordMinimumLength")) {
return;
}
Objects.requireNonNull(who, "ComponentName is null");
@@ -4350,7 +4366,7 @@
@Override
public void setPasswordMinimumUpperCase(ComponentName who, int length, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || notSupportedOnAutomotive("setPasswordMinimumUpperCase")) {
return;
}
Objects.requireNonNull(who, "ComponentName is null");
@@ -4383,6 +4399,9 @@
@Override
public void setPasswordMinimumLowerCase(ComponentName who, int length, boolean parent) {
+ if (notSupportedOnAutomotive("setPasswordMinimumLowerCase")) {
+ return;
+ }
Objects.requireNonNull(who, "ComponentName is null");
final int userId = mInjector.userHandleGetCallingUserId();
synchronized (getLockObject()) {
@@ -4413,7 +4432,7 @@
@Override
public void setPasswordMinimumLetters(ComponentName who, int length, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || notSupportedOnAutomotive("setPasswordMinimumLetters")) {
return;
}
Objects.requireNonNull(who, "ComponentName is null");
@@ -4445,7 +4464,7 @@
@Override
public void setPasswordMinimumNumeric(ComponentName who, int length, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || notSupportedOnAutomotive("setPasswordMinimumNumeric")) {
return;
}
Objects.requireNonNull(who, "ComponentName is null");
@@ -4477,7 +4496,7 @@
@Override
public void setPasswordMinimumSymbols(ComponentName who, int length, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || notSupportedOnAutomotive("setPasswordMinimumSymbols")) {
return;
}
Objects.requireNonNull(who, "ComponentName is null");
@@ -4509,7 +4528,7 @@
@Override
public void setPasswordMinimumNonLetter(ComponentName who, int length, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || notSupportedOnAutomotive("setPasswordMinimumNonLetter")) {
return;
}
Objects.requireNonNull(who, "ComponentName is null");
@@ -9670,6 +9689,7 @@
mStatLogger.dump(pw);
pw.println();
pw.println("Encryption Status: " + getEncryptionStatusName(getEncryptionStatus()));
+ pw.println("Logout user: " + getLogoutUserId());
pw.println();
if (mPendingUserCreatedCallbackTokens.isEmpty()) {
@@ -10780,6 +10800,9 @@
Preconditions.checkCallAuthorization(isDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SWITCH_USER);
+ boolean switched = false;
+ // Save previous logout user id in case of failure
+ int logoutUserId = getLogoutUserId();
synchronized (getLockObject()) {
long id = mInjector.binderClearCallingIdentity();
try {
@@ -10787,16 +10810,55 @@
if (userHandle != null) {
userId = userHandle.getIdentifier();
}
- return mInjector.getIActivityManager().switchUser(userId);
+ Slogf.i(LOG_TAG, "Switching to user %d (logout user is %d)", userId, logoutUserId);
+ setLogoutUserIdLocked(UserHandle.USER_CURRENT);
+ switched = mInjector.getIActivityManager().switchUser(userId);
+ if (!switched) {
+ Slogf.w(LOG_TAG, "Failed to switch to user %d", userId);
+ }
+ return switched;
} catch (RemoteException e) {
Slogf.e(LOG_TAG, "Couldn't switch user", e);
return false;
} finally {
mInjector.binderRestoreCallingIdentity(id);
+ if (!switched) {
+ setLogoutUserIdLocked(logoutUserId);
+ }
}
}
}
+ private @UserIdInt int getLogoutUserId() {
+ if (!mInjector.userManagerIsHeadlessSystemUserMode()) {
+ // mLogoutUserId is USER_SYSTEM as well, but there's no need to acquire the lock
+ return UserHandle.USER_SYSTEM;
+ }
+ synchronized (getLockObject()) {
+ return mLogoutUserId;
+ }
+ }
+
+ private void setLogoutUserId(@UserIdInt int userId) {
+ if (!mInjector.userManagerIsHeadlessSystemUserMode()) return; // ignore
+
+ synchronized (getLockObject()) {
+ setLogoutUserIdLocked(userId);
+ }
+ }
+
+ @GuardedBy("getLockObject()")
+ private void setLogoutUserIdLocked(@UserIdInt int userId) {
+ if (!mInjector.userManagerIsHeadlessSystemUserMode()) return; // ignore
+
+ if (userId == UserHandle.USER_CURRENT) {
+ userId = getCurrentForegroundUserId();
+ }
+
+ Slogf.d(LOG_TAG, "setLogoutUserId(): %d -> %d", mLogoutUserId, userId);
+ mLogoutUserId = userId;
+ }
+
@Override
public int startUserInBackground(ComponentName who, UserHandle userHandle) {
Objects.requireNonNull(who, "ComponentName is null");
@@ -10818,10 +10880,11 @@
return UserManager.USER_OPERATION_ERROR_MAX_RUNNING_USERS;
}
+ Slogf.i(LOG_TAG, "Starting user %d in background", userId);
if (mInjector.getIActivityManager().startUserInBackground(userId)) {
- Slogf.i(LOG_TAG, "Started used %d in background", userId);
return UserManager.USER_OPERATION_SUCCESS;
} else {
+ Slogf.w(LOG_TAG, "failed to start user %d in background", userId);
return UserManager.USER_OPERATION_ERROR_UNKNOWN;
}
} catch (RemoteException e) {
@@ -10869,13 +10932,30 @@
return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
}
+ // TODO(b/204585343): remove the headless system user check?
+ if (mInjector.userManagerIsHeadlessSystemUserMode() && callingUserId != mInjector
+ .binderWithCleanCallingIdentity(() -> getCurrentForegroundUserId())) {
+ Slogf.d(LOG_TAG, "logoutUser(): user %d is in background, just stopping, not switching",
+ callingUserId);
+ return stopUserUnchecked(callingUserId);
+ }
+
+ int logoutUserId = getLogoutUserId();
+ if (logoutUserId == UserHandle.USER_NULL) {
+ // Could happen on devices using headless system user mode when called before calling
+ // switchUser() or startUserInBackground() first
+ Slogf.w(LOG_TAG, "logoutUser(): could not determine which user to switch to");
+ return UserManager.USER_OPERATION_ERROR_UNKNOWN;
+ }
final long id = mInjector.binderClearCallingIdentity();
try {
- if (!mInjector.getIActivityManager().switchUser(UserHandle.USER_SYSTEM)) {
- Slogf.w(LOG_TAG, "Failed to switch to primary user");
- // This should never happen as target user is UserHandle.USER_SYSTEM
+ Slogf.i(LOG_TAG, "logoutUser(): switching to user %d", logoutUserId);
+ if (!mInjector.getIActivityManager().switchUser(logoutUserId)) {
+ Slogf.w(LOG_TAG, "Failed to switch to user %d", logoutUserId);
+ // This should never happen as target user is determined by getPreviousUserId()
return UserManager.USER_OPERATION_ERROR_UNKNOWN;
}
+ setLogoutUserId(UserHandle.USER_CURRENT);
} catch (RemoteException e) {
// Same process, should not happen.
return UserManager.USER_OPERATION_ERROR_UNKNOWN;
@@ -10886,7 +10966,8 @@
return stopUserUnchecked(callingUserId);
}
- private int stopUserUnchecked(int userId) {
+ private int stopUserUnchecked(@UserIdInt int userId) {
+ Slogf.i(LOG_TAG, "Stopping user %d", userId);
final long id = mInjector.binderClearCallingIdentity();
try {
switch (mInjector.getIActivityManager().stopUser(userId, true /*force*/, null)) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
index d7cbd9b..4620ea6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
@@ -219,7 +219,8 @@
try {
final IntentFilter filterFinished =
new IntentFilter(ACTION_REMOTE_BUGREPORT_DISPATCH, BUGREPORT_MIMETYPE);
- mContext.registerReceiver(mRemoteBugreportFinishedReceiver, filterFinished);
+ mContext.registerReceiver(mRemoteBugreportFinishedReceiver, filterFinished,
+ Context.RECEIVER_EXPORTED);
} catch (IntentFilter.MalformedMimeTypeException e) {
// should never happen, as setting a constant
Slogf.w(LOG_TAG, e, "Failed to set type %s", BUGREPORT_MIMETYPE);
diff --git a/services/java/com/android/server/BootUserInitializer.java b/services/java/com/android/server/BootUserInitializer.java
new file mode 100644
index 0000000..46e59a9
--- /dev/null
+++ b/services/java/com/android/server/BootUserInitializer.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server;
+
+import android.annotation.UserIdInt;
+import android.content.ContentResolver;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+
+import com.android.server.am.ActivityManagerService;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.utils.Slogf;
+import com.android.server.utils.TimingsTraceAndSlog;
+
+import java.util.List;
+
+/**
+ * Class responsible for booting the device in the proper user on headless system user mode.
+ *
+ */
+// TODO(b/204091126): STOPSHIP - provide proper APIs
+final class BootUserInitializer {
+
+ private static final String TAG = BootUserInitializer.class.getSimpleName();
+
+ // TODO(b/204091126): STOPSHIP - set to false or dynamic value
+ private static final boolean DEBUG = true;
+
+ private final ActivityManagerService mAms;
+ private final ContentResolver mContentResolver;
+
+ BootUserInitializer(ActivityManagerService am, ContentResolver contentResolver) {
+ mAms = am;
+ mContentResolver = contentResolver;
+ }
+
+ public void init(TimingsTraceAndSlog t) {
+ Slogf.i(TAG, "init())");
+
+ // TODO(b/204091126): in the long term, we need to decide who's reponsible for that,
+ // this class or the setup wizard app
+ provisionHeadlessSystemUser();
+
+ UserManagerInternal um = LocalServices.getService(UserManagerInternal.class);
+ t.traceBegin("get-existing-users");
+ List<UserInfo> existingUsers = um.getUsers(/* excludeDying= */ true);
+ t.traceEnd();
+
+ Slogf.d(TAG, "%d existing users", existingUsers.size());
+
+ int initialUserId = UserHandle.USER_NULL;
+
+ for (int i = 0; i < existingUsers.size(); i++) {
+ UserInfo user = existingUsers.get(i);
+ if (DEBUG) {
+ Slogf.d(TAG, "User at position %d: %s", i, user.toFullString());
+ }
+ if (user.id != UserHandle.USER_SYSTEM && user.isFull()) {
+ if (DEBUG) {
+ Slogf.d(TAG, "Found initial user: %d", user.id);
+ }
+ initialUserId = user.id;
+ break;
+ }
+ }
+
+ if (initialUserId == UserHandle.USER_NULL) {
+ Slogf.d(TAG, "Creating initial user");
+ t.traceBegin("create-initial-user");
+ try {
+ // TODO(b/204091126): proper name for user
+ UserInfo newUser = um.createUserEvenWhenDisallowed("Real User",
+ UserManager.USER_TYPE_FULL_SECONDARY, UserInfo.FLAG_ADMIN,
+ /* disallowedPackages= */ null, /* token= */ null);
+ Slogf.i(TAG, "Created initial user: %s", newUser.toFullString());
+ initialUserId = newUser.id;
+ } catch (Exception e) {
+ Slogf.wtf(TAG, "failed to created initial user", e);
+ return;
+ } finally {
+ t.traceEnd(); // create-initial-user
+ }
+ }
+
+ unlockSystemUser(t);
+ switchToInitialUser(initialUserId);
+ }
+
+ private void provisionHeadlessSystemUser() {
+ if (isDeviceProvisioned()) {
+ Slogf.d(TAG, "provisionHeadlessSystemUser(): already provisioned");
+ return;
+ }
+
+ Slogf.i(TAG, "Marking USER_SETUP_COMPLETE for system user");
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.USER_SETUP_COMPLETE, 1);
+ Slogf.i(TAG, "Marking DEVICE_PROVISIONED for system user");
+ Settings.Global.putInt(mContentResolver, Settings.Global.DEVICE_PROVISIONED, 1);
+ }
+
+ private boolean isDeviceProvisioned() {
+ try {
+ return Settings.Global.getInt(mContentResolver,
+ Settings.Global.DEVICE_PROVISIONED) == 1;
+ } catch (Exception e) {
+ Slogf.wtf(TAG, "DEVICE_PROVISIONED setting not found.", e);
+ return false;
+ }
+ }
+
+ // NOTE: Mostly copied from Automotive's InitialUserSetter
+ private void unlockSystemUser(TimingsTraceAndSlog t) {
+ Slogf.i(TAG, "Unlocking system user");
+ t.traceBegin("unlock-system-user");
+ try {
+ // This is for force changing state into RUNNING_LOCKED. Otherwise unlock does not
+ // update the state and USER_SYSTEM unlock happens twice.
+ t.traceBegin("am.startUser");
+ boolean started = mAms.startUserInBackgroundWithListener(UserHandle.USER_SYSTEM,
+ /* listener= */ null);
+ t.traceEnd();
+ if (!started) {
+ Slogf.w(TAG, "could not restart system user in background; trying unlock instead");
+ t.traceBegin("am.unlockUser");
+ boolean unlocked = mAms.unlockUser(UserHandle.USER_SYSTEM, /* token= */ null,
+ /* secret= */ null, /* listener= */ null);
+ t.traceEnd();
+ if (!unlocked) {
+ Slogf.w(TAG, "could not unlock system user either");
+ return;
+ }
+ }
+ } finally {
+ t.traceEnd();
+ }
+ }
+
+ private void switchToInitialUser(@UserIdInt int initialUserId) {
+ Slogf.i(TAG, "Switching to initial user %d", initialUserId);
+ boolean started = mAms.startUserInForegroundWithListener(initialUserId,
+ /* unlockListener= */ null);
+ if (!started) {
+ Slogf.wtf(TAG, "Failed to start user %d in foreground", initialUserId);
+ }
+ }
+}
diff --git a/services/java/com/android/server/SystemConfigService.java b/services/java/com/android/server/SystemConfigService.java
index 3a9b2dc..cb52e5f 100644
--- a/services/java/com/android/server/SystemConfigService.java
+++ b/services/java/com/android/server/SystemConfigService.java
@@ -19,6 +19,7 @@
import static java.util.stream.Collectors.toMap;
import android.Manifest;
+import android.content.ComponentName;
import android.content.Context;
import android.os.ISystemConfig;
import android.util.ArrayMap;
@@ -87,14 +88,14 @@
}
@Override
- public List<String> getEnabledComponentOverrides(String packageName) {
+ public List<ComponentName> getEnabledComponentOverrides(String packageName) {
ArrayMap<String, Boolean> systemComponents = SystemConfig.getInstance()
.getComponentsEnabledStates(packageName);
- List<String> enabledComponent = new ArrayList<>();
+ List<ComponentName> enabledComponent = new ArrayList<>();
if (systemComponents != null) {
for (Map.Entry<String, Boolean> entry : systemComponents.entrySet()) {
if (Boolean.TRUE.equals(entry.getValue())) {
- enabledComponent.add(entry.getKey());
+ enabledComponent.add(new ComponentName(packageName, entry.getKey()));
}
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 8c414b9..b44b692 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -73,6 +73,7 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.UserManager;
import android.os.storage.IStorageManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
@@ -2768,7 +2769,9 @@
}, WEBVIEW_PREPARATION);
}
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ boolean isAutomotive = mPackageManager
+ .hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+ if (isAutomotive) {
t.traceBegin("StartCarServiceHelperService");
final SystemService cshs = mSystemServiceManager
.startService(CAR_SERVICE_HELPER_SERVICE_CLASS);
@@ -2877,6 +2880,13 @@
mSystemServiceManager.startBootPhase(t, SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
t.traceEnd();
+ if (UserManager.isHeadlessSystemUserMode() && !isAutomotive) {
+ // TODO(b/204091126): remove isAutomotive check once the workflow is finalized
+ t.traceBegin("BootUserInitializer");
+ new BootUserInitializer(mActivityManagerService, mContentResolver).init(t);
+ t.traceEnd();
+ }
+
t.traceBegin("StartNetworkStack");
try {
// Note : the network stack is creating on-demand objects that need to send
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 09a831e..0c3f1dd 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -41,6 +41,7 @@
sdk_version: "module_current",
min_sdk_version: "30",
libs: [
+ "framework-annotations-lib",
"unsupportedappusage",
"framework-wifi-util-lib",
"framework-connectivity",
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index fdf23d3..4fb801e 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -23,7 +23,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ResolveInfo;
import android.os.Handler;
import android.os.IBinder.DeathRecipient;
import android.os.Looper;
@@ -32,8 +31,6 @@
import android.os.SystemProperties;
import android.os.UpdateEngine;
import android.os.UpdateEngineCallback;
-import android.os.UserHandle;
-import android.os.UserManager;
import android.provider.DeviceConfig;
import android.util.Log;
@@ -45,9 +42,6 @@
import com.android.server.wm.ActivityMetricsLaunchObserverRegistry;
import com.android.server.wm.ActivityTaskManagerInternal;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
@@ -306,79 +300,27 @@
return;
}
- if (!getUploaderEnabledConfig(getContext())) {
- return;
- }
-
+ Context context = getContext();
new Thread(() -> {
try {
- Context context = getContext();
- final String uploaderPkg = getUploaderPackageName(context);
- final String uploaderAction = getUploaderActionName(context);
- String reportUuid = mIProfcollect.report();
+ // Prepare profile report
+ String reportName = mIProfcollect.report() + ".zip";
- final int profileId = getBBProfileId();
- String reportDir = "/data/user/" + profileId
- + "/com.google.android.apps.internal.betterbug/cache/";
- String reportPath = reportDir + reportUuid + ".zip";
-
- if (!Files.exists(Paths.get(reportDir))) {
- Log.i(LOG_TAG, "Destination directory does not exist, abort upload.");
+ if (!context.getResources().getBoolean(
+ R.bool.config_profcollectReportUploaderEnabled)) {
+ Log.i(LOG_TAG, "Upload is not enabled.");
return;
}
- Intent uploadIntent =
- new Intent(uploaderAction)
- .setPackage(uploaderPkg)
- .putExtra("EXTRA_DESTINATION", "PROFCOLLECT")
- .putExtra("EXTRA_PACKAGE_NAME", getContext().getPackageName())
- .putExtra("EXTRA_PROFILE_PATH", reportPath)
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-
- List<ResolveInfo> receivers =
- context.getPackageManager().queryBroadcastReceivers(uploadIntent, 0);
- if (receivers == null || receivers.isEmpty()) {
- Log.i(LOG_TAG, "No one to receive upload intent, abort upload.");
- return;
- }
- mIProfcollect.copy_report_to_bb(profileId, reportUuid);
- context.sendBroadcast(uploadIntent);
- mIProfcollect.delete_report(reportUuid);
+ // Upload the report
+ Intent intent = new Intent()
+ .setPackage("com.android.shell")
+ .setAction("com.android.shell.action.PROFCOLLECT_UPLOAD")
+ .putExtra("filename", reportName);
+ context.sendBroadcast(intent);
} catch (RemoteException e) {
Log.e(LOG_TAG, e.getMessage());
}
}).start();
}
-
- /**
- * Get BetterBug's profile ID. It is the work profile ID, if it exists. Otherwise the system
- * user ID.
- *
- * @return BetterBug's profile ID.
- */
- private int getBBProfileId() {
- UserManager userManager = UserManager.get(getContext());
- int[] profiles = userManager.getProfileIds(UserHandle.USER_SYSTEM, false);
- for (int p : profiles) {
- if (userManager.getUserInfo(p).isManagedProfile()) {
- return p;
- }
- }
- return UserHandle.USER_SYSTEM;
- }
-
- private boolean getUploaderEnabledConfig(Context context) {
- return context.getResources().getBoolean(
- R.bool.config_profcollectReportUploaderEnabled);
- }
-
- private String getUploaderPackageName(Context context) {
- return context.getResources().getString(
- R.string.config_defaultProfcollectReportUploaderApp);
- }
-
- private String getUploaderActionName(Context context) {
- return context.getResources().getString(
- R.string.config_defaultProfcollectReportUploaderAction);
- }
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index 26a7efb..d1d7cc6 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -21,22 +21,22 @@
import android.content.pm.PackageManager
import android.content.pm.parsing.component.ParsedActivityImpl
import android.content.pm.parsing.component.ParsedIntentInfoImpl
-import android.content.pm.pkg.PackageUserStateInternal
import android.content.pm.verify.domain.DomainVerificationManager
import android.content.pm.verify.domain.DomainVerificationState
import android.os.Build
import android.os.Process
import android.util.ArraySet
+import android.util.SparseArray
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.pm.PackageSetting
import com.android.server.pm.parsing.pkg.AndroidPackage
-import com.android.server.pm.test.verify.domain.DomainVerificationTestUtils.mockPackageSettings
+import com.android.server.pm.pkg.PackageStateInternal
+import com.android.server.pm.pkg.PackageUserStateInternal
+import com.android.server.pm.test.verify.domain.DomainVerificationTestUtils.mockPackageStates
import com.android.server.pm.verify.domain.DomainVerificationEnforcer
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
import com.android.server.pm.verify.domain.DomainVerificationService
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
import com.android.server.testutils.mockThrowOnUnmocked
-import com.android.server.testutils.spyThrowOnUnmocked
import com.android.server.testutils.whenever
import org.junit.Test
import org.junit.runner.RunWith
@@ -47,7 +47,6 @@
import org.mockito.Mockito.anyString
import org.mockito.Mockito.eq
import org.mockito.Mockito.verifyNoMoreInteractions
-import java.io.File
import java.util.UUID
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
@@ -73,9 +72,9 @@
@Parameterized.Parameters(name = "{0}")
fun parameters(): Array<Any> {
val visiblePkg = mockPkg(VISIBLE_PKG)
- val visiblePkgSetting = mockPkgSetting(VISIBLE_PKG, VISIBLE_UUID)
+ val visiblePkgState = mockPkgState(VISIBLE_PKG, VISIBLE_UUID)
val invisiblePkg = mockPkg(INVISIBLE_PKG)
- val invisiblePkgSetting = mockPkgSetting(INVISIBLE_PKG, INVISIBLE_UUID)
+ val invisiblePkgState = mockPkgState(INVISIBLE_PKG, INVISIBLE_UUID)
val makeEnforcer: (Context) -> DomainVerificationEnforcer = {
DomainVerificationEnforcer(it).apply {
@@ -98,10 +97,10 @@
mockThrowOnUnmocked {
whenever(callingUid) { callingUidInt.get() }
whenever(callingUserId) { callingUserIdInt.get() }
- mockPackageSettings {
+ mockPackageStates {
when (it) {
- VISIBLE_PKG -> visiblePkgSetting
- INVISIBLE_PKG -> invisiblePkgSetting
+ VISIBLE_PKG -> visiblePkgState
+ INVISIBLE_PKG -> invisiblePkgState
else -> null
}
}
@@ -142,8 +141,8 @@
callingUidInt.set(it.callingUid)
callingUserIdInt.set(it.callingUserId)
service.proxy = it.proxy
- service.addPackage(visiblePkgSetting)
- service.addPackage(invisiblePkgSetting)
+ service.addPackage(visiblePkgState)
+ service.addPackage(invisiblePkgState)
service.block(it)
}
@@ -314,33 +313,21 @@
}
}
- fun mockPkgSetting(packageName: String, domainSetId: UUID) = spyThrowOnUnmocked(
- PackageSetting(
- packageName,
- packageName,
- File("/test"),
- null,
- null,
- null,
- null,
- 1,
- 0,
- 0,
- 0,
- null,
- null,
- null,
- domainSetId
- )
- ) {
- whenever(this.packageName) { packageName }
- whenever(pkg) { mockPkg(packageName) }
- whenever(this.domainSetId) { domainSetId }
- whenever(readUserState(0)) { PackageUserStateInternal.DEFAULT }
- whenever(readUserState(1)) { PackageUserStateInternal.DEFAULT }
- whenever(getInstantApp(anyInt())) { false }
- whenever(isSystem) { false }
- }
+ fun mockPkgState(packageName: String, domainSetId: UUID) =
+ mockThrowOnUnmocked<PackageStateInternal> {
+ whenever(this.packageName) { packageName }
+ whenever(pkg) { mockPkg(packageName) }
+ whenever(this.domainSetId) { domainSetId }
+ whenever(getUserStateOrDefault(0)) { PackageUserStateInternal.DEFAULT }
+ whenever(getUserStateOrDefault(1)) { PackageUserStateInternal.DEFAULT }
+ whenever(userStates) {
+ SparseArray<PackageUserStateInternal>().apply {
+ this[0] = PackageUserStateInternal.DEFAULT
+ this[1] = PackageUserStateInternal.DEFAULT
+ }
+ }
+ whenever(isSystem) { false }
+ }
}
@Parameterized.Parameter(0)
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
index 34b248c..38e882e 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
@@ -21,7 +21,7 @@
import android.content.pm.PackageManager
import android.content.pm.parsing.component.ParsedActivityImpl
import android.content.pm.parsing.component.ParsedIntentInfoImpl
-import android.content.pm.pkg.PackageUserStateInternal
+import com.android.server.pm.pkg.PackageUserStateInternal
import android.content.pm.verify.domain.DomainOwner
import android.content.pm.verify.domain.DomainVerificationInfo
import android.content.pm.verify.domain.DomainVerificationManager
@@ -31,9 +31,10 @@
import android.os.PatternMatcher
import android.os.Process
import android.util.ArraySet
-import com.android.server.pm.PackageSetting
+import android.util.SparseArray
import com.android.server.pm.parsing.pkg.AndroidPackage
-import com.android.server.pm.test.verify.domain.DomainVerificationTestUtils.mockPackageSettings
+import com.android.server.pm.pkg.PackageStateInternal
+import com.android.server.pm.test.verify.domain.DomainVerificationTestUtils.mockPackageStates
import com.android.server.pm.verify.domain.DomainVerificationManagerStub
import com.android.server.pm.verify.domain.DomainVerificationService
import com.android.server.testutils.mockThrowOnUnmocked
@@ -69,8 +70,8 @@
@Test
fun queryValidVerificationPackageNames() {
- val pkgWithDomains = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
- val pkgWithoutDomains = mockPkgSetting(PKG_TWO, UUID_TWO, emptyList())
+ val pkgWithDomains = mockPkgState(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkgWithoutDomains = mockPkgState(PKG_TWO, UUID_TWO, emptyList())
val service = makeService(pkgWithDomains, pkgWithoutDomains).apply {
addPackages(pkgWithDomains, pkgWithoutDomains)
@@ -82,8 +83,8 @@
@Test
fun getDomainVerificationInfoId() {
- val pkgWithDomains = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
- val pkgWithoutDomains = mockPkgSetting(PKG_TWO, UUID_TWO, emptyList())
+ val pkgWithDomains = mockPkgState(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkgWithoutDomains = mockPkgState(PKG_TWO, UUID_TWO, emptyList())
val service = makeService(pkgWithDomains, pkgWithoutDomains).apply {
addPackages(pkgWithDomains, pkgWithoutDomains)
@@ -97,8 +98,8 @@
@Test
fun getDomainVerificationInfo() {
- val pkgWithDomains = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
- val pkgWithoutDomains = mockPkgSetting(PKG_TWO, UUID_TWO, emptyList())
+ val pkgWithDomains = mockPkgState(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkgWithoutDomains = mockPkgState(PKG_TWO, UUID_TWO, emptyList())
val service = makeService(pkgWithDomains, pkgWithoutDomains).apply {
addPackages(pkgWithDomains, pkgWithoutDomains)
@@ -124,8 +125,8 @@
@Test
fun setStatus() {
- val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
- val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, listOf(DOMAIN_3, DOMAIN_4))
+ val pkg1 = mockPkgState(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkg2 = mockPkgState(PKG_TWO, UUID_TWO, listOf(DOMAIN_3, DOMAIN_4))
val map = mutableMapOf(pkg1.packageName to pkg1, pkg2.packageName to pkg2)
val service = makeService(map::get).apply { addPackages(pkg1, pkg2) }
@@ -166,8 +167,8 @@
@Test
fun setDomainVerificationLinkHandlingAllowed() {
- val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
- val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, listOf(DOMAIN_3, DOMAIN_4))
+ val pkg1 = mockPkgState(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkg2 = mockPkgState(PKG_TWO, UUID_TWO, listOf(DOMAIN_3, DOMAIN_4))
val map = mutableMapOf(pkg1.packageName to pkg1, pkg2.packageName to pkg2)
val service = makeService(map::get).apply { addPackages(pkg1, pkg2) }
@@ -199,9 +200,9 @@
@Test
fun setUserSelection() {
- val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
- val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, listOf(DOMAIN_3, DOMAIN_4))
- val pkg3 = mockPkgSetting(PKG_THREE, UUID_THREE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkg1 = mockPkgState(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkg2 = mockPkgState(PKG_TWO, UUID_TWO, listOf(DOMAIN_3, DOMAIN_4))
+ val pkg3 = mockPkgState(PKG_THREE, UUID_THREE, listOf(DOMAIN_1, DOMAIN_2))
val map = mutableMapOf(
pkg1.packageName to pkg1,
@@ -253,8 +254,8 @@
@Test
fun getDomainVerificationUserState() {
- val pkgWithDomains = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
- val pkgWithoutDomains = mockPkgSetting(PKG_TWO, UUID_TWO, emptyList())
+ val pkgWithDomains = mockPkgState(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkgWithoutDomains = mockPkgState(PKG_TWO, UUID_TWO, emptyList())
val service = makeService(pkgWithDomains, pkgWithoutDomains).apply {
addPackages(pkgWithDomains, pkgWithoutDomains)
@@ -288,7 +289,7 @@
fun getOwnersForDomain() {
val pkg1User0Enabled = AtomicBoolean(true)
- val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2), pkgUserState0 = {
+ val pkg1 = mockPkgState(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2), pkgUserState0 = {
mockThrowOnUnmocked {
whenever(enabledState) {
if (pkg1User0Enabled.get()) {
@@ -299,9 +300,10 @@
}
whenever(isInstalled) { true }
whenever(isSuspended) { false }
+ whenever(isInstantApp) { false }
}
})
- val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, listOf(DOMAIN_1, DOMAIN_2))
+ val pkg2 = mockPkgState(PKG_TWO, UUID_TWO, listOf(DOMAIN_1, DOMAIN_2))
val service = makeService(pkg1, pkg2).apply {
addPackages(pkg1, pkg2)
@@ -474,10 +476,10 @@
}
}
- private fun makeService(vararg pkgSettings: PackageSetting) =
- makeService { pkgName -> pkgSettings.find { pkgName == it.packageName } }
+ private fun makeService(vararg pkgStates: PackageStateInternal) =
+ makeService { pkgName -> pkgStates.find { pkgName == it.packageName } }
- private fun makeService(pkgSettingFunction: (String) -> PackageSetting? = { null }) =
+ private fun makeService(pkgStateFunction: (String) -> PackageStateInternal? = { null }) =
DomainVerificationService(mockThrowOnUnmocked {
// Assume the test has every permission necessary
whenever(enforcePermission(anyString(), anyInt(), anyInt(), anyString()))
@@ -499,21 +501,21 @@
whenever(callingUid) { Process.ROOT_UID }
whenever(callingUserId) { 0 }
- mockPackageSettings {
- pkgSettingFunction(it)
+ mockPackageStates {
+ pkgStateFunction(it)
}
})
}
- private fun mockPkgSetting(
+ private fun mockPkgState(
pkgName: String,
domainSetId: UUID,
domains: List<String> = listOf(DOMAIN_1, DOMAIN_2),
- pkgUserState0: PackageSetting.() -> PackageUserStateInternal = {
+ pkgUserState0: PackageStateInternal.() -> PackageUserStateInternal = {
PackageUserStateInternal.DEFAULT },
- pkgUserState1: PackageSetting.() -> PackageUserStateInternal = {
+ pkgUserState1: PackageStateInternal.() -> PackageUserStateInternal = {
PackageUserStateInternal.DEFAULT }
- ) = mockThrowOnUnmocked<PackageSetting> {
+ ) = mockThrowOnUnmocked<PackageStateInternal> {
val pkg = mockThrowOnUnmocked<AndroidPackage> {
whenever(packageName) { pkgName }
whenever(targetSdkVersion) { Build.VERSION_CODES.S }
@@ -546,15 +548,20 @@
whenever(getPkg()) { pkg }
whenever(packageName) { pkgName }
whenever(this.domainSetId) { domainSetId }
- whenever(getInstantApp(anyInt())) { false }
whenever(firstInstallTime) { 0L }
- whenever(readUserState(0)) { pkgUserState0() }
- whenever(readUserState(1)) { pkgUserState1() }
+ whenever(getUserStateOrDefault(0)) { pkgUserState0() }
+ whenever(getUserStateOrDefault(1)) { pkgUserState1() }
+ whenever(userStates) {
+ SparseArray<PackageUserStateInternal>().apply {
+ this[0] = pkgUserState0()
+ this[1] = pkgUserState1()
+ }
+ }
whenever(isSystem) { false }
}
- private fun DomainVerificationService.addPackages(vararg pkgSettings: PackageSetting) =
- pkgSettings.forEach(::addPackage)
+ private fun DomainVerificationService.addPackages(vararg pkgStates: PackageStateInternal) =
+ pkgStates.forEach(::addPackage)
private fun makeManager(service: DomainVerificationService, userId: Int) =
DomainVerificationManager(
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
index a22563e..3ffce8c 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
@@ -22,7 +22,7 @@
import android.content.pm.SigningDetails
import android.content.pm.parsing.component.ParsedActivityImpl
import android.content.pm.parsing.component.ParsedIntentInfoImpl
-import android.content.pm.pkg.PackageUserStateInternal
+import com.android.server.pm.pkg.PackageUserStateInternal
import android.content.pm.verify.domain.DomainOwner
import android.content.pm.verify.domain.DomainVerificationInfo.STATE_MODIFIABLE_VERIFIED
import android.content.pm.verify.domain.DomainVerificationInfo.STATE_NO_RESPONSE
@@ -37,10 +37,11 @@
import android.os.PatternMatcher
import android.os.Process
import android.util.ArraySet
+import android.util.SparseArray
import android.util.Xml
-import com.android.server.pm.PackageSetting
import com.android.server.pm.parsing.pkg.AndroidPackage
-import com.android.server.pm.test.verify.domain.DomainVerificationTestUtils.mockPackageSettings
+import com.android.server.pm.pkg.PackageStateInternal
+import com.android.server.pm.test.verify.domain.DomainVerificationTestUtils.mockPackageStates
import com.android.server.pm.verify.domain.DomainVerificationService
import com.android.server.testutils.mock
import com.android.server.testutils.mockThrowOnUnmocked
@@ -52,7 +53,6 @@
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.anyString
-import org.mockito.Mockito
import org.mockito.Mockito.doReturn
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
@@ -80,8 +80,8 @@
private const val USER_ID = 0
}
- private val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, SIGNATURE_ONE)
- private val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, SIGNATURE_TWO)
+ private val pkg1 = mockPkgState(PKG_ONE, UUID_ONE, SIGNATURE_ONE)
+ private val pkg2 = mockPkgState(PKG_TWO, UUID_TWO, SIGNATURE_TWO)
@Test
fun addPackageFirstTime() {
@@ -111,8 +111,8 @@
@Test
fun addPackageSystemConfigured() {
- val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, SIGNATURE_ONE, isSystemApp = false)
- val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, SIGNATURE_TWO, isSystemApp = true)
+ val pkg1 = mockPkgState(PKG_ONE, UUID_ONE, SIGNATURE_ONE, isSystemApp = false)
+ val pkg2 = mockPkgState(PKG_TWO, UUID_TWO, SIGNATURE_TWO, isSystemApp = true)
val service = makeService(
systemConfiguredPackageNames = ArraySet(setOf(pkg1.packageName, pkg2.packageName)),
@@ -430,15 +430,15 @@
@Test
fun migratePackageDropDomain() {
val pkgName = PKG_ONE
- val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, SIGNATURE_ONE,
+ val pkgBefore = mockPkgState(pkgName, UUID_ONE, SIGNATURE_ONE,
listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3, DOMAIN_4))
- val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, SIGNATURE_TWO, listOf(DOMAIN_1, DOMAIN_2))
+ val pkgAfter = mockPkgState(pkgName, UUID_TWO, SIGNATURE_TWO, listOf(DOMAIN_1, DOMAIN_2))
// Test 4 domains:
// 1 will be approved and preserved, 2 will be selected and preserved,
// 3 will be denied and dropped, 4 will be selected and dropped
- val map = mutableMapOf<String, PackageSetting>()
+ val map = mutableMapOf<String, PackageStateInternal>()
val service = makeService { map[it] }
service.addPackage(pkgBefore)
@@ -491,10 +491,10 @@
@Test
fun migratePackageDropAll() {
val pkgName = PKG_ONE
- val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, SIGNATURE_ONE, listOf(DOMAIN_1, DOMAIN_2))
- val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, SIGNATURE_TWO, emptyList())
+ val pkgBefore = mockPkgState(pkgName, UUID_ONE, SIGNATURE_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkgAfter = mockPkgState(pkgName, UUID_TWO, SIGNATURE_TWO, emptyList())
- val map = mutableMapOf<String, PackageSetting>()
+ val map = mutableMapOf<String, PackageStateInternal>()
val service = makeService { map[it] }
service.addPackage(pkgBefore)
@@ -533,15 +533,15 @@
@Test
fun migratePackageAddDomain() {
val pkgName = PKG_ONE
- val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, SIGNATURE_ONE, listOf(DOMAIN_1, DOMAIN_2))
- val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, SIGNATURE_TWO,
+ val pkgBefore = mockPkgState(pkgName, UUID_ONE, SIGNATURE_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkgAfter = mockPkgState(pkgName, UUID_TWO, SIGNATURE_TWO,
listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3))
// Test 3 domains:
// 1 will be verified and preserved, 2 will be selected and preserved,
// 3 will be new and default
- val map = mutableMapOf<String, PackageSetting>()
+ val map = mutableMapOf<String, PackageStateInternal>()
val service = makeService { map[it] }
service.addPackage(pkgBefore)
@@ -584,10 +584,10 @@
@Test
fun migratePackageAddAll() {
val pkgName = PKG_ONE
- val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, SIGNATURE_ONE, emptyList())
- val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, SIGNATURE_TWO, listOf(DOMAIN_1, DOMAIN_2))
+ val pkgBefore = mockPkgState(pkgName, UUID_ONE, SIGNATURE_ONE, emptyList())
+ val pkgAfter = mockPkgState(pkgName, UUID_TWO, SIGNATURE_TWO, listOf(DOMAIN_1, DOMAIN_2))
- val map = mutableMapOf<String, PackageSetting>()
+ val map = mutableMapOf<String, PackageStateInternal>()
val service = makeService { map[it] }
service.addPackage(pkgBefore)
@@ -628,8 +628,8 @@
// This test acts as a proxy for true user restore through PackageManager,
// as that's much harder to test for real.
- val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, SIGNATURE_ONE, listOf(DOMAIN_1, DOMAIN_2))
- val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, SIGNATURE_TWO,
+ val pkg1 = mockPkgState(PKG_ONE, UUID_ONE, SIGNATURE_ONE, listOf(DOMAIN_1, DOMAIN_2))
+ val pkg2 = mockPkgState(PKG_TWO, UUID_TWO, SIGNATURE_TWO,
listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3))
val serviceBefore = makeService(pkg1, pkg2)
serviceBefore.addPackage(pkg1)
@@ -762,17 +762,17 @@
private fun makeService(
systemConfiguredPackageNames: ArraySet<String> = ArraySet(),
- vararg pkgSettings: PackageSetting
+ vararg pkgStates: PackageStateInternal
) = makeService(systemConfiguredPackageNames = systemConfiguredPackageNames) {
- pkgName -> pkgSettings.find { pkgName == it.packageName }
+ pkgName -> pkgStates.find { pkgName == it.packageName }
}
- private fun makeService(vararg pkgSettings: PackageSetting) =
- makeService { pkgName -> pkgSettings.find { pkgName == it.packageName } }
+ private fun makeService(vararg pkgStates: PackageStateInternal) =
+ makeService { pkgName -> pkgStates.find { pkgName == it.packageName } }
private fun makeService(
systemConfiguredPackageNames: ArraySet<String> = ArraySet(),
- pkgSettingFunction: (String) -> PackageSetting? = { null }
+ pkgStateFunction: (String) -> PackageStateInternal? = { null }
) = DomainVerificationService(mockThrowOnUnmocked {
// Assume the test has every permission necessary
whenever(enforcePermission(anyString(), anyInt(), anyInt(), anyString()))
@@ -794,19 +794,19 @@
whenever(callingUid) { Process.ROOT_UID }
whenever(callingUserId) { 0 }
- mockPackageSettings {
- pkgSettingFunction(it)
+ mockPackageStates {
+ pkgStateFunction(it)
}
})
}
- private fun mockPkgSetting(
+ private fun mockPkgState(
pkgName: String,
domainSetId: UUID,
signature: String,
domains: List<String> = listOf(DOMAIN_1, DOMAIN_2),
isSystemApp: Boolean = false
- ) = mockThrowOnUnmocked<PackageSetting> {
+ ) = mockThrowOnUnmocked<PackageStateInternal> {
val pkg = mockThrowOnUnmocked<AndroidPackage> {
whenever(packageName) { pkgName }
whenever(targetSdkVersion) { Build.VERSION_CODES.S }
@@ -839,10 +839,15 @@
whenever(this.pkg) { pkg }
whenever(packageName) { pkgName }
whenever(this.domainSetId) { domainSetId }
- whenever(getInstantApp(anyInt())) { false }
whenever(firstInstallTime) { 0L }
- whenever(readUserState(0)) { PackageUserStateInternal.DEFAULT }
- whenever(readUserState(10)) { PackageUserStateInternal.DEFAULT }
+ whenever(getUserStateOrDefault(0)) { PackageUserStateInternal.DEFAULT }
+ whenever(getUserStateOrDefault(10)) { PackageUserStateInternal.DEFAULT }
+ whenever(userStates) {
+ SparseArray<PackageUserStateInternal>().apply {
+ this[0] = PackageUserStateInternal.DEFAULT
+ this[1] = PackageUserStateInternal.DEFAULT
+ }
+ }
whenever(isSystem) { isSystemApp }
val mockSigningDetails = SigningDetails(arrayOf(spy(Signature(signature)) {
@@ -852,7 +857,7 @@
}
private fun DomainVerificationService.assertState(
- pkg: PackageSetting,
+ pkg: PackageStateInternal,
userId: Int,
linkHandingAllowed: Boolean = true,
hostToStateMap: Map<String, Int>
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
index cbb8e3a..a397d56 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -21,19 +21,19 @@
import android.content.pm.PackageManager
import android.content.pm.parsing.component.ParsedActivityImpl
import android.content.pm.parsing.component.ParsedIntentInfoImpl
-import android.content.pm.pkg.PackageUserStateInternal
+import com.android.server.pm.pkg.PackageUserStateInternal
import android.content.pm.verify.domain.DomainVerificationState
import android.os.Build
import android.os.Process
import android.util.ArraySet
-import com.android.server.pm.PackageSetting
+import android.util.SparseArray
import com.android.server.pm.parsing.pkg.AndroidPackage
-import com.android.server.pm.test.verify.domain.DomainVerificationTestUtils.mockPackageSettings
+import com.android.server.pm.pkg.PackageStateInternal
+import com.android.server.pm.test.verify.domain.DomainVerificationTestUtils.mockPackageStates
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
import com.android.server.pm.verify.domain.DomainVerificationService
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
import com.android.server.testutils.mockThrowOnUnmocked
-import com.android.server.testutils.spyThrowOnUnmocked
import com.android.server.testutils.whenever
import org.junit.Test
import org.junit.runner.RunWith
@@ -44,7 +44,6 @@
import org.mockito.Mockito.anyString
import org.mockito.Mockito.eq
import org.mockito.Mockito.verify
-import java.io.File
import java.util.UUID
@RunWith(Parameterized::class)
@@ -107,7 +106,7 @@
fun service(name: String, block: DomainVerificationService.() -> Unit) =
Params(makeService, name) { service ->
service.proxy = proxy
- service.addPackage(mockPkgSetting())
+ service.addPackage(mockPkgState())
service.block()
}
@@ -213,32 +212,18 @@
}
}
- // TODO: PackageSetting field encapsulation to move to whenever(name)
- fun mockPkgSetting() = spyThrowOnUnmocked(
- PackageSetting(
- TEST_PKG,
- TEST_PKG,
- File("/test"),
- null,
- null,
- null,
- null,
- 1,
- 0,
- 0,
- 0,
- null,
- null,
- null,
- TEST_UUID
- )
- ) {
+ fun mockPkgState() = mockThrowOnUnmocked<PackageStateInternal> {
whenever(packageName) { TEST_PKG }
whenever(pkg) { mockPkg() }
whenever(domainSetId) { TEST_UUID }
- whenever(readUserState(0)) { PackageUserStateInternal.DEFAULT }
- whenever(readUserState(10)) { PackageUserStateInternal.DEFAULT }
- whenever(getInstantApp(anyInt())) { false }
+ whenever(getUserStateOrDefault(0)) { PackageUserStateInternal.DEFAULT }
+ whenever(getUserStateOrDefault(10)) { PackageUserStateInternal.DEFAULT }
+ whenever(userStates) {
+ SparseArray<PackageUserStateInternal>().apply {
+ this[0] = PackageUserStateInternal.DEFAULT
+ this[1] = PackageUserStateInternal.DEFAULT
+ }
+ }
whenever(isSystem) { false }
}
}
@@ -259,9 +244,9 @@
mockThrowOnUnmocked {
whenever(callingUid) { TEST_UID }
whenever(callingUserId) { TEST_USER_ID }
- mockPackageSettings {
+ mockPackageStates {
when (it) {
- TEST_PKG -> mockPkgSetting()
+ TEST_PKG -> mockPkgState()
else -> null
}
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationTestUtils.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationTestUtils.kt
index 48845be..c5f0eb1 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationTestUtils.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationTestUtils.kt
@@ -17,7 +17,7 @@
package com.android.server.pm.test.verify.domain
import com.android.internal.util.FunctionalUtils
-import com.android.server.pm.PackageSetting
+import com.android.server.pm.pkg.PackageStateInternal
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
import com.android.server.testutils.whenever
import org.mockito.ArgumentMatchers.any
@@ -27,29 +27,30 @@
internal object DomainVerificationTestUtils {
@Suppress("UNCHECKED_CAST")
- fun DomainVerificationManagerInternal.Connection.mockPackageSettings(
- block: (String) -> PackageSetting?
+ fun DomainVerificationManagerInternal.Connection.mockPackageStates(
+ block: (String) -> PackageStateInternal?
) {
whenever(withPackageSettingsSnapshot(any())) {
- (arguments[0] as Consumer<Function<String, PackageSetting?>>).accept { block(it) }
+ (arguments[0] as Consumer<Function<String, PackageStateInternal?>>).accept { block(it) }
}
- whenever(withPackageSettingsSnapshotReturning<Any>(any())) {
- (arguments[0] as FunctionalUtils.ThrowingFunction<Function<String, PackageSetting?>, *>)
+ whenever(withPackageSettingsSnapshotReturning(any())) {
+ (arguments[0] as FunctionalUtils.ThrowingFunction<
+ Function<String, PackageStateInternal?>, *>)
.apply { block(it) }
}
whenever(withPackageSettingsSnapshotThrowing<Exception>(any())) {
(arguments[0] as FunctionalUtils.ThrowingCheckedConsumer<
- Function<String, PackageSetting?>, *>)
+ Function<String, PackageStateInternal?>, *>)
.accept { block(it) }
}
whenever(withPackageSettingsSnapshotThrowing2<Exception, Exception>(any())) {
(arguments[0] as FunctionalUtils.ThrowingChecked2Consumer<
- Function<String, PackageSetting?>, *, *>)
+ Function<String, PackageStateInternal?>, *, *>)
.accept { block(it) }
}
whenever(withPackageSettingsSnapshotReturningThrowing<Any, Exception>(any())) {
(arguments[0] as FunctionalUtils.ThrowingCheckedFunction<
- Function<String, PackageSetting?>, *, *>)
+ Function<String, PackageStateInternal?>, *, *>)
.apply { block(it) }
}
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
index 473d70c..9fa1a235 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
@@ -18,12 +18,8 @@
import android.content.Intent
import android.content.pm.PackageManager
-import android.content.pm.parsing.component.ParsedActivity
import android.content.pm.parsing.component.ParsedActivityImpl
-import android.content.pm.parsing.component.ParsedIntentInfo
import android.content.pm.parsing.component.ParsedIntentInfoImpl
-import android.content.pm.pkg.PackageUserState
-import android.content.pm.pkg.PackageUserStateInternal
import android.content.pm.verify.domain.DomainVerificationManager
import android.content.pm.verify.domain.DomainVerificationState
import android.content.pm.verify.domain.DomainVerificationUserState
@@ -31,9 +27,11 @@
import android.os.PatternMatcher
import android.os.Process
import android.util.ArraySet
-import com.android.server.pm.PackageSetting
+import android.util.SparseArray
import com.android.server.pm.parsing.pkg.AndroidPackage
-import com.android.server.pm.test.verify.domain.DomainVerificationTestUtils.mockPackageSettings
+import com.android.server.pm.pkg.PackageStateInternal
+import com.android.server.pm.pkg.PackageUserStateInternal
+import com.android.server.pm.test.verify.domain.DomainVerificationTestUtils.mockPackageStates
import com.android.server.pm.verify.domain.DomainVerificationService
import com.android.server.testutils.mockThrowOnUnmocked
import com.android.server.testutils.whenever
@@ -63,8 +61,8 @@
private const val USER_ID = 0
}
- private val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE)
- private val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO)
+ private val pkg1 = mockPkgState(PKG_ONE, UUID_ONE)
+ private val pkg2 = mockPkgState(PKG_TWO, UUID_TWO)
fun makeService() =
DomainVerificationService(mockThrowOnUnmocked {
@@ -87,7 +85,7 @@
// Need to provide an internal UID so some permission checks are ignored
whenever(callingUid) { Process.ROOT_UID }
whenever(callingUserId) { 0 }
- mockPackageSettings {
+ mockPackageStates {
when (it) {
PKG_ONE -> pkg1
PKG_TWO -> pkg2
@@ -104,57 +102,63 @@
assertThat(stateFor(PKG_ONE, DOMAIN_ONE)).isEqualTo(STATE_SELECTED)
}
- fun mockPkgSetting(pkgName: String, domainSetId: UUID) = mockThrowOnUnmocked<PackageSetting> {
- val pkg = mockThrowOnUnmocked<AndroidPackage> {
+ fun mockPkgState(pkgName: String, domainSetId: UUID) =
+ mockThrowOnUnmocked<PackageStateInternal> {
+ val pkg = mockThrowOnUnmocked<AndroidPackage> {
+ whenever(packageName) { pkgName }
+ whenever(targetSdkVersion) { Build.VERSION_CODES.S }
+ whenever(isEnabled) { true }
+
+ val activityList = listOf(
+ ParsedActivityImpl().apply {
+ addIntent(
+ ParsedIntentInfoImpl().apply {
+ intentFilter.apply {
+ autoVerify = true
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme("http")
+ addDataScheme("https")
+ addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
+ addDataAuthority(DOMAIN_ONE, null)
+ }
+ }
+ )
+ addIntent(
+ ParsedIntentInfoImpl().apply {
+ intentFilter.apply {
+ autoVerify = true
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme("http")
+ addDataPath("/sub2", PatternMatcher.PATTERN_LITERAL)
+ addDataAuthority("example2.com", null)
+ }
+ }
+ )
+ },
+ )
+
+ whenever(activities) { activityList }
+ }
+
+ whenever(this.pkg) { pkg }
whenever(packageName) { pkgName }
- whenever(targetSdkVersion) { Build.VERSION_CODES.S }
- whenever(isEnabled) { true }
-
- val activityList = listOf(
- ParsedActivityImpl().apply {
- addIntent(
- ParsedIntentInfoImpl().apply {
- intentFilter.apply {
- autoVerify = true
- addAction(Intent.ACTION_VIEW)
- addCategory(Intent.CATEGORY_BROWSABLE)
- addCategory(Intent.CATEGORY_DEFAULT)
- addDataScheme("http")
- addDataScheme("https")
- addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
- addDataAuthority(DOMAIN_ONE, null)
- }
- }
- )
- addIntent(
- ParsedIntentInfoImpl().apply {
- intentFilter.apply {
- autoVerify = true
- addAction(Intent.ACTION_VIEW)
- addCategory(Intent.CATEGORY_BROWSABLE)
- addCategory(Intent.CATEGORY_DEFAULT)
- addDataScheme("http")
- addDataPath("/sub2", PatternMatcher.PATTERN_LITERAL)
- addDataAuthority("example2.com", null)
- }
- }
- )
- },
- )
-
- whenever(activities) { activityList }
+ whenever(this.domainSetId) { domainSetId }
+ whenever(firstInstallTime) { 0L }
+ whenever(getUserStateOrDefault(0)) { PackageUserStateInternal.DEFAULT }
+ whenever(getUserStateOrDefault(1)) { PackageUserStateInternal.DEFAULT }
+ whenever(userStates) {
+ SparseArray<PackageUserStateInternal>().apply {
+ this[0] = PackageUserStateInternal.DEFAULT
+ this[1] = PackageUserStateInternal.DEFAULT
+ }
+ }
+ whenever(isSystem) { false }
}
- whenever(this.pkg) { pkg }
- whenever(packageName) { pkgName }
- whenever(this.domainSetId) { domainSetId }
- whenever(getInstantApp(anyInt())) { false }
- whenever(firstInstallTime) { 0L }
- whenever(readUserState(0)) { PackageUserStateInternal.DEFAULT }
- whenever(readUserState(1)) { PackageUserStateInternal.DEFAULT }
- whenever(isSystem) { false }
- }
-
@Test
fun anotherPackageTakeoverSuccess() {
val service = makeService()
diff --git a/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
index 06e691f..3aeaa42 100644
--- a/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
@@ -16,37 +16,48 @@
package com.android.server.communal;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.communal.CommunalManagerService.ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT;
import static com.android.server.wm.ActivityInterceptorCallback.COMMUNAL_MODE_ORDERED_ID;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import android.Manifest;
+import android.annotation.Nullable;
import android.app.KeyguardManager;
import android.app.communal.ICommunalManager;
-import android.content.ContentResolver;
-import android.content.Context;
+import android.app.compat.CompatChanges;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContextWrapper;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
-import android.os.IBinder;
+import android.net.Uri;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
+import android.service.dreams.DreamManagerInternal;
+import android.test.mock.MockContentResolver;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.LocalServices;
+import com.android.server.SystemService;
import com.android.server.wm.ActivityInterceptorCallback;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -84,32 +95,37 @@
@Mock
private KeyguardManager mKeyguardManager;
@Mock
- private Context mMockContext;
- @Mock
- private ContentResolver mContentResolver;
+ private DreamManagerInternal mDreamManagerInternal;
private ActivityInterceptorCallback mActivityInterceptorCallback;
+ private BroadcastReceiver mPackageReceiver;
private ActivityInfo mAInfo;
private ICommunalManager mBinder;
+ private ContextWrapper mContextSpy;
@Before
public final void setUp() {
mMockingSession = mockitoSession()
.initMocks(this)
- .mockStatic(LocalServices.class)
- .mockStatic(ServiceManager.class)
- .mockStatic(Settings.Secure.class)
- .mockStatic(KeyguardManager.class)
+ .spyStatic(CommunalManagerService.class)
+ .mockStatic(CompatChanges.class)
.strictness(Strictness.WARN)
.startMocking();
- when(mMockContext.getContentResolver()).thenReturn(mContentResolver);
- when(mMockContext.getSystemService(KeyguardManager.class)).thenReturn(mKeyguardManager);
- doReturn(mAtmInternal).when(() -> LocalServices.getService(
- ActivityTaskManagerInternal.class));
+ mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
+ MockContentResolver cr = new MockContentResolver(mContextSpy);
+ cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ when(mContextSpy.getContentResolver()).thenReturn(cr);
- mService = new CommunalManagerService(mMockContext);
- mService.onStart();
+ when(mContextSpy.getSystemService(KeyguardManager.class)).thenReturn(mKeyguardManager);
+ addLocalServiceMock(ActivityTaskManagerInternal.class, mAtmInternal);
+ addLocalServiceMock(DreamManagerInternal.class, mDreamManagerInternal);
+
+ doNothing().when(mContextSpy).enforceCallingPermission(
+ eq(Manifest.permission.WRITE_COMMUNAL_STATE), anyString());
+
+ mService = new CommunalManagerService(mContextSpy);
+ mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
ArgumentCaptor<ActivityInterceptorCallback> activityInterceptorCaptor =
ArgumentCaptor.forClass(ActivityInterceptorCallback.class);
@@ -117,11 +133,13 @@
activityInterceptorCaptor.capture());
mActivityInterceptorCallback = activityInterceptorCaptor.getValue();
- ArgumentCaptor<IBinder> binderCaptor = ArgumentCaptor.forClass(IBinder.class);
- verify(() -> ServiceManager.addService(eq(Context.COMMUNAL_MANAGER_SERVICE),
- binderCaptor.capture(),
- anyBoolean(), anyInt()));
- mBinder = ICommunalManager.Stub.asInterface(binderCaptor.getValue());
+ ArgumentCaptor<BroadcastReceiver> packageReceiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(mContextSpy).registerReceiverAsUser(packageReceiverCaptor.capture(),
+ eq(UserHandle.SYSTEM), any(), any(), any());
+ mPackageReceiver = packageReceiverCaptor.getValue();
+
+ mBinder = mService.getBinderServiceInstance();
mAInfo = new ActivityInfo();
mAInfo.applicationInfo = new ApplicationInfo();
@@ -130,11 +148,20 @@
@After
public void tearDown() {
+ FakeSettingsProvider.clearSettingsProvider();
if (mMockingSession != null) {
mMockingSession.finishMocking();
}
}
+ /**
+ * Creates a mock and registers it to {@link LocalServices}.
+ */
+ private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+ LocalServices.removeServiceForTest(clazz);
+ LocalServices.addService(clazz, mock);
+ }
+
private ActivityInterceptorCallback.ActivityInterceptorInfo buildActivityInfo(Intent intent) {
return new ActivityInterceptorCallback.ActivityInterceptorInfo(
TEST_REAL_CALLING_UID,
@@ -152,84 +179,172 @@
}
private void allowPackages(String packages) {
- doReturn(packages).when(
- () -> Settings.Secure.getStringForUser(mContentResolver,
- Settings.Secure.COMMUNAL_MODE_PACKAGES, UserHandle.USER_SYSTEM));
- mService.updateSelectedApps();
+ Settings.Secure.putStringForUser(mContextSpy.getContentResolver(),
+ Settings.Secure.COMMUNAL_MODE_PACKAGES, packages, UserHandle.USER_SYSTEM);
+ }
+
+ private String getAllowedPackages() {
+ return Settings.Secure.getStringForUser(mContextSpy.getContentResolver(),
+ Settings.Secure.COMMUNAL_MODE_PACKAGES, UserHandle.USER_SYSTEM);
+ }
+
+ private void assertDoesIntercept() {
+ final Intent intent = new Intent(Intent.ACTION_MAIN);
+ assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNotNull();
+ }
+
+ private void assertDoesNotIntercept() {
+ final Intent intent = new Intent(Intent.ACTION_MAIN);
+ assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNull();
+ }
+
+ private Intent createPackageIntent(String packageName, @Nullable String action) {
+ return new Intent(action, Uri.parse("package:" + packageName));
}
@Test
public void testIntercept_unlocked_communalOff_appNotEnabled_showWhenLockedOff() {
- final Intent intent = new Intent(Intent.ACTION_MAIN);
when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
mAInfo.flags = 0;
- assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNull();
+ assertDoesNotIntercept();
}
@Test
public void testIntercept_unlocked_communalOn_appNotEnabled_showWhenLockedOff()
throws RemoteException {
- final Intent intent = new Intent(Intent.ACTION_MAIN);
mBinder.setCommunalViewShowing(true);
when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
mAInfo.flags = 0;
- assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNull();
+ assertDoesNotIntercept();
}
@Test
public void testIntercept_locked_communalOff_appNotEnabled_showWhenLockedOff() {
- final Intent intent = new Intent(Intent.ACTION_MAIN);
when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
mAInfo.flags = 0;
- assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNull();
+ assertDoesNotIntercept();
}
@Test
- public void testIntercept_locked_communalOn_appNotEnabled_showWhenLockedOff()
+ public void testIntercept_locked_communalOn_appNotEnabled_showWhenLockedOff_allowlistEnabled()
throws RemoteException {
- final Intent intent = new Intent(Intent.ACTION_MAIN);
mBinder.setCommunalViewShowing(true);
when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
+ when(CompatChanges.isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, TEST_PACKAGE_NAME,
+ UserHandle.SYSTEM)).thenReturn(true);
mAInfo.flags = 0;
- // TODO(b/191994709): Fix this assertion once we properly intercept activities.
- assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNull();
+ assertDoesIntercept();
}
@Test
- public void testIntercept_locked_communalOn_appNotEnabled_showWhenLockedOn()
+ public void testIntercept_locked_communalOn_appNotEnabled_showWhenLockedOn_allowlistEnabled()
throws RemoteException {
- final Intent intent = new Intent(Intent.ACTION_MAIN);
mBinder.setCommunalViewShowing(true);
when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
+ when(CompatChanges.isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, TEST_PACKAGE_NAME,
+ UserHandle.SYSTEM)).thenReturn(true);
mAInfo.flags = FLAG_SHOW_WHEN_LOCKED;
allowPackages("package1,package2");
- // TODO(b/191994709): Fix this assertion once we properly intercept activities.
- assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNull();
+ assertDoesIntercept();
}
@Test
- public void testIntercept_locked_communalOn_appEnabled_showWhenLockedOff()
+ public void testIntercept_locked_communalOn_appEnabled_showWhenLockedOff_allowlistEnabled()
throws RemoteException {
- final Intent intent = new Intent(Intent.ACTION_MAIN);
mBinder.setCommunalViewShowing(true);
when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
+ when(CompatChanges.isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, TEST_PACKAGE_NAME,
+ UserHandle.SYSTEM)).thenReturn(true);
mAInfo.flags = 0;
allowPackages(TEST_PACKAGE_NAME);
- // TODO(b/191994709): Fix this assertion once we properly intercept activities.
- assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNull();
+ // TODO(b/191994709): Fix this assertion once we start checking showWhenLocked
+ assertDoesNotIntercept();
}
@Test
- public void testIntercept_locked_communalOn_appEnabled_showWhenLockedOn()
+ public void testIntercept_locked_communalOn_appEnabled_showWhenLockedOn_allowlistEnabled()
throws RemoteException {
- final Intent intent = new Intent(Intent.ACTION_MAIN);
mBinder.setCommunalViewShowing(true);
when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
+ when(CompatChanges.isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, TEST_PACKAGE_NAME,
+ UserHandle.SYSTEM)).thenReturn(true);
+
mAInfo.flags = FLAG_SHOW_WHEN_LOCKED;
allowPackages(TEST_PACKAGE_NAME);
- assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNull();
+ assertDoesNotIntercept();
+ }
+
+ @Test
+ public void testIntercept_locked_communalOn_appEnabled_showWhenLockedOn_allowlistDisabled()
+ throws RemoteException {
+ mBinder.setCommunalViewShowing(true);
+ when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
+ when(CompatChanges.isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, TEST_PACKAGE_NAME,
+ UserHandle.SYSTEM)).thenReturn(false);
+
+ mAInfo.flags = FLAG_SHOW_WHEN_LOCKED;
+
+ allowPackages(TEST_PACKAGE_NAME);
+ assertDoesIntercept();
+ }
+
+ @Test
+ public void testIntercept_locked_communalOn_dream() throws RemoteException {
+ mBinder.setCommunalViewShowing(true);
+ when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
+ when(mDreamManagerInternal.getActiveDreamComponent(false)).thenReturn(
+ new ComponentName(TEST_PACKAGE_NAME, "SomeClass"));
+
+ allowPackages(TEST_PACKAGE_NAME);
+ assertDoesNotIntercept();
+ }
+
+ @Test
+ public void testUpdateSettings_packageUninstalled() {
+ allowPackages("package1,package2");
+ assertThat(getAllowedPackages()).isEqualTo("package1,package2");
+
+ mPackageReceiver.onReceive(mContextSpy,
+ createPackageIntent("package1", ACTION_PACKAGE_REMOVED));
+
+ assertThat(getAllowedPackages()).isEqualTo("package2");
+ }
+
+ @Test
+ public void testUpdateSettings_nullAction_doesNothing() {
+ allowPackages("package1,package2");
+ assertThat(getAllowedPackages()).isEqualTo("package1,package2");
+
+ mPackageReceiver.onReceive(mContextSpy,
+ createPackageIntent("package1", null));
+
+ assertThat(getAllowedPackages()).isEqualTo("package1,package2");
+ }
+
+ @Test
+ public void testUpdateSettings_invalidPackage_doesNothing() {
+ allowPackages("package1,package2");
+ assertThat(getAllowedPackages()).isEqualTo("package1,package2");
+
+ mPackageReceiver.onReceive(mContextSpy,
+ createPackageIntent("package3", ACTION_PACKAGE_REMOVED));
+
+ assertThat(getAllowedPackages()).isEqualTo("package1,package2");
+ }
+
+ @Test
+ public void testUpdateSettings_onBoot() {
+ allowPackages("package1,package2");
+ assertThat(getAllowedPackages()).isEqualTo("package1,package2");
+
+ when(CommunalManagerService.isPackageInstalled(eq("package1"), any())).thenReturn(true);
+ when(CommunalManagerService.isPackageInstalled(eq("package2"), any())).thenReturn(false);
+
+ mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
+
+ assertThat(getAllowedPackages()).isEqualTo("package1");
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/communal/OWNERS b/services/tests/mockingservicestests/src/com/android/server/communal/OWNERS
new file mode 100644
index 0000000..9ec6665
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/communal/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/communal/OWNERS
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index a94f0ee..0c3e472 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -56,7 +56,9 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.platform.test.annotations.LargeTest;
import android.util.Log;
+import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseLongArray;
@@ -85,6 +87,11 @@
public class JobSchedulerServiceTest {
private static final String TAG = JobSchedulerServiceTest.class.getSimpleName();
+ private static final int[] sRegJobPriorities = {
+ JobInfo.PRIORITY_HIGH, JobInfo.PRIORITY_DEFAULT,
+ JobInfo.PRIORITY_LOW, JobInfo.PRIORITY_MIN
+ };
+
private JobSchedulerService mService;
private MockitoSession mMockingSession;
@@ -893,7 +900,7 @@
createJobInfo(6).setExpedited(true), 2);
JobStatus eA7 = createJobStatus("testPendingJobSorting",
createJobInfo(7).setExpedited(true), 1);
- JobStatus rH8 = createJobStatus("testPendingJobSorting", createJobInfo(8), 14);
+ JobStatus rH8 = createJobStatus("testPendingJobSorting", createJobInfo(8), 8);
JobStatus rF8 = createJobStatus("testPendingJobSorting", createJobInfo(8), 6);
JobStatus eF9 = createJobStatus("testPendingJobSorting",
createJobInfo(9).setExpedited(true), 6);
@@ -905,21 +912,21 @@
JobStatus eE14 = createJobStatus("testPendingJobSorting",
createJobInfo(14).setExpedited(true), 5);
- rA1.enqueueTime = 1;
- rB2.enqueueTime = 2;
- eC3.enqueueTime = 3;
- rD4.enqueueTime = 4;
- eE5.enqueueTime = 5;
- eB6.enqueueTime = 6;
- eA7.enqueueTime = 7;
- rF8.enqueueTime = 8;
- rH8.enqueueTime = 8;
- eF9.enqueueTime = 9;
- rC10.enqueueTime = 10;
- eC11.enqueueTime = 11;
- rG12.enqueueTime = 12;
- rG13.enqueueTime = 13;
- eE14.enqueueTime = 14;
+ rA1.enqueueTime = 10;
+ rB2.enqueueTime = 20;
+ eC3.enqueueTime = 30;
+ rD4.enqueueTime = 40;
+ eE5.enqueueTime = 50;
+ eB6.enqueueTime = 60;
+ eA7.enqueueTime = 70;
+ rF8.enqueueTime = 80;
+ rH8.enqueueTime = 80;
+ eF9.enqueueTime = 90;
+ rC10.enqueueTime = 100;
+ eC11.enqueueTime = 110;
+ rG12.enqueueTime = 120;
+ rG13.enqueueTime = 130;
+ eE14.enqueueTime = 140;
mService.mPendingJobs.clear();
// Add in random order so sorting is apparent.
@@ -951,39 +958,62 @@
}
private void checkPendingJobInvariants() {
- long regJobEnqueueTime = 0;
final SparseBooleanArray regJobSeen = new SparseBooleanArray();
- final SparseLongArray ejEnqueueTimes = new SparseLongArray();
+ // Latest priority enqueue times seen for each priority for each app.
+ final SparseArray<SparseLongArray> latestPriorityRegEnqueueTimesPerUid =
+ new SparseArray<>();
+ final SparseArray<SparseLongArray> latestPriorityEjEnqueueTimesPerUid = new SparseArray<>();
+ final long noEntry = -1;
for (int i = 0; i < mService.mPendingJobs.size(); ++i) {
final JobStatus job = mService.mPendingJobs.get(i);
final int uid = job.getSourceUid();
- if (!job.isRequestedExpeditedJob()) {
- // Invariant #1: Regular jobs are sorted by enqueue time.
- assertTrue("Regular job with earlier enqueue time sorted after a later time: "
- + regJobEnqueueTime + " vs " + job.enqueueTime,
- regJobEnqueueTime <= job.enqueueTime);
- regJobEnqueueTime = job.enqueueTime;
- regJobSeen.put(uid, true);
- } else {
- // Invariant #2: EJs should be before regular jobs for an individual app
- if (regJobSeen.get(uid)) {
- fail("UID " + uid + " had an EJ ordered after a regular job");
+ // Invariant #1: All jobs (for a UID) are sorted by priority order
+ // Invariant #2: Jobs (for a UID) with the same priority are sorted by enqueue time.
+ // Invariant #3: EJs (for a UID) should be before regular jobs
+
+ final int priority = job.getEffectivePriority();
+ final SparseArray<SparseLongArray> latestPriorityEnqueueTimesPerUid =
+ job.isRequestedExpeditedJob()
+ ? latestPriorityEjEnqueueTimesPerUid
+ : latestPriorityRegEnqueueTimesPerUid;
+ SparseLongArray latestPriorityEnqueueTimes = latestPriorityEnqueueTimesPerUid.get(uid);
+ if (latestPriorityEnqueueTimes != null) {
+ // Invariant 1
+ for (int p = priority - 1; p >= JobInfo.PRIORITY_MIN; --p) {
+ // If we haven't seen the priority, there shouldn't be an entry in the array.
+ assertEquals("Jobs not properly sorted by priority for uid " + uid,
+ noEntry, latestPriorityEnqueueTimes.get(p, noEntry));
}
- final long ejEnqueueTime = ejEnqueueTimes.get(uid, 0);
- // Invariant #3: EJs for an individual app should be sorted by enqueue time.
- assertTrue("EJ with earlier enqueue time sorted after a later time: "
- + ejEnqueueTime + " vs " + job.enqueueTime,
- ejEnqueueTime <= job.enqueueTime);
- ejEnqueueTimes.put(uid, job.enqueueTime);
+
+ // Invariant 2
+ final long lastSeenPriorityEnqueueTime =
+ latestPriorityEnqueueTimes.get(priority, noEntry);
+ if (lastSeenPriorityEnqueueTime != noEntry) {
+ assertTrue("Jobs with same priority not sorted by enqueue time: "
+ + lastSeenPriorityEnqueueTime + " vs " + job.enqueueTime,
+ lastSeenPriorityEnqueueTime <= job.enqueueTime);
+ }
+ } else {
+ latestPriorityEnqueueTimes = new SparseLongArray();
+ latestPriorityEnqueueTimesPerUid.put(uid, latestPriorityEnqueueTimes);
+ }
+ latestPriorityEnqueueTimes.put(priority, job.enqueueTime);
+
+ // Invariant 3
+ if (!job.isRequestedExpeditedJob()) {
+ regJobSeen.put(uid, true);
+ } else if (regJobSeen.get(uid)) {
+ fail("UID " + uid + " had an EJ ordered after a regular job");
}
}
}
private static String sortedJobToString(JobStatus job) {
- return "testJob " + job.getSourceUid() + "/" + job.getJobId() + "/"
- + job.isRequestedExpeditedJob() + "@" + job.enqueueTime;
+ return "testJob " + job.getSourceUid() + "/" + job.getJobId()
+ + "/p" + job.getEffectivePriority()
+ + "/" + job.isRequestedExpeditedJob() + "@" + job.enqueueTime;
}
@Test
@@ -992,23 +1022,23 @@
mService.mPendingJobs.clear();
- for (int i = 0; i < 2500; ++i) {
+ for (int i = 0; i < 5000; ++i) {
JobStatus job = createJobStatus("testPendingJobSorting_Random",
createJobInfo(i).setExpedited(random.nextBoolean()), random.nextInt(250));
job.enqueueTime = random.nextInt(1_000_000);
mService.mPendingJobs.add(job);
-
- mService.mPendingJobComparator.refreshLocked();
- try {
- mService.mPendingJobs.sort(mService.mPendingJobComparator);
- } catch (Exception e) {
- for (JobStatus toDump : mService.mPendingJobs) {
- Log.i(TAG, sortedJobToString(toDump));
- }
- throw e;
- }
- checkPendingJobInvariants();
}
+
+ mService.mPendingJobComparator.refreshLocked();
+ try {
+ mService.mPendingJobs.sort(mService.mPendingJobComparator);
+ } catch (Exception e) {
+ for (JobStatus toDump : mService.mPendingJobs) {
+ Log.i(TAG, sortedJobToString(toDump));
+ }
+ throw e;
+ }
+ checkPendingJobInvariants();
}
private int sign(int i) {
@@ -1042,6 +1072,7 @@
}
@Test
+ @LargeTest
public void testPendingJobSortingTransitivity_Concentrated() {
// Always use the same series of pseudo random values.
for (int seed : new int[]{1337, 6000, 637739, 6357, 1, 7, 13}) {
@@ -1064,6 +1095,99 @@
}
}
+ @Test
+ public void testPendingJobSorting_Random_WithPriority() {
+ Random random = new Random(1); // Always use the same series of pseudo random values.
+
+ mService.mPendingJobs.clear();
+
+ for (int i = 0; i < 5000; ++i) {
+ final boolean isEj = random.nextBoolean();
+ final int priority;
+ if (isEj) {
+ priority = random.nextBoolean() ? JobInfo.PRIORITY_MAX : JobInfo.PRIORITY_HIGH;
+ } else {
+ priority = sRegJobPriorities[random.nextInt(sRegJobPriorities.length)];
+ }
+ JobStatus job = createJobStatus("testPendingJobSorting_Random_WithPriority",
+ createJobInfo(i).setExpedited(isEj).setPriority(priority),
+ random.nextInt(250));
+ job.enqueueTime = random.nextInt(1_000_000);
+ mService.mPendingJobs.add(job);
+ }
+
+ mService.mPendingJobComparator.refreshLocked();
+ try {
+ mService.mPendingJobs.sort(mService.mPendingJobComparator);
+ } catch (Exception e) {
+ for (JobStatus toDump : mService.mPendingJobs) {
+ Log.i(TAG, sortedJobToString(toDump));
+ }
+ throw e;
+ }
+ checkPendingJobInvariants();
+ }
+
+ @Test
+ public void testPendingJobSortingTransitivity_WithPriority() {
+ // Always use the same series of pseudo random values.
+ for (int seed : new int[]{1337, 7357, 606, 6357, 41106010, 3, 2, 1}) {
+ Random random = new Random(seed);
+
+ mService.mPendingJobs.clear();
+
+ for (int i = 0; i < 300; ++i) {
+ final boolean isEj = random.nextBoolean();
+ final int priority;
+ if (isEj) {
+ priority = random.nextBoolean() ? JobInfo.PRIORITY_MAX : JobInfo.PRIORITY_HIGH;
+ } else {
+ priority = sRegJobPriorities[random.nextInt(sRegJobPriorities.length)];
+ }
+ JobStatus job = createJobStatus("testPendingJobSortingTransitivity_WithPriority",
+ createJobInfo(i).setExpedited(isEj).setPriority(priority),
+ random.nextInt(50));
+ job.enqueueTime = random.nextInt(1_000_000);
+ job.overrideState = random.nextInt(4);
+ mService.mPendingJobs.add(job);
+ }
+
+ verifyPendingJobComparatorTransitivity();
+ }
+ }
+
+ @Test
+ @LargeTest
+ public void testPendingJobSortingTransitivity_Concentrated_WithPriority() {
+ // Always use the same series of pseudo random values.
+ for (int seed : new int[]{1337, 6000, 637739, 6357, 1, 7, 13}) {
+ Random random = new Random(seed);
+
+ mService.mPendingJobs.clear();
+
+ for (int i = 0; i < 300; ++i) {
+ final boolean isEj = random.nextFloat() < .03;
+ final int priority;
+ if (isEj) {
+ priority = random.nextBoolean() ? JobInfo.PRIORITY_MAX : JobInfo.PRIORITY_HIGH;
+ } else {
+ priority = sRegJobPriorities[random.nextInt(sRegJobPriorities.length)];
+ }
+ JobStatus job = createJobStatus(
+ "testPendingJobSortingTransitivity_Concentrated_WithPriority",
+ createJobInfo(i).setExpedited(isEj).setPriority(priority),
+ random.nextInt(20));
+ job.enqueueTime = random.nextInt(250);
+ job.overrideState = random.nextFloat() < .01
+ ? JobStatus.OVERRIDE_SORTING : JobStatus.OVERRIDE_NONE;
+ mService.mPendingJobs.add(job);
+ Log.d(TAG, sortedJobToString(job));
+ }
+
+ verifyPendingJobComparatorTransitivity();
+ }
+ }
+
private void verifyPendingJobComparatorTransitivity() {
mService.mPendingJobComparator.refreshLocked();
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 6a25560..7d42a52 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -37,6 +37,8 @@
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_STORAGE_NOT_LOW;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_TIMING_DELAY;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_WITHIN_QUOTA;
+import static com.android.server.job.controllers.JobStatus.NO_EARLIEST_RUNTIME;
+import static com.android.server.job.controllers.JobStatus.NO_LATEST_RUNTIME;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -222,6 +224,104 @@
assertEquals(1, createJobStatus(now - 2000, now).getFractionRunTime(), DELTA);
}
+ @Test
+ public void testGetEffectivePriority_Expedited() {
+ final JobInfo jobInfo =
+ new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+ .setExpedited(true)
+ .build();
+ JobStatus job = createJobStatus(jobInfo);
+
+ // Less than 2 failures, priority shouldn't be affected.
+ assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority());
+ int backoffAttempt = 1;
+ job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+ assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority());
+
+ // 2+ failures, priority should be lowered as much as possible.
+ backoffAttempt = 2;
+ job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+ assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
+ backoffAttempt = 5;
+ job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+ assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
+ backoffAttempt = 8;
+ job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+ assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
+ }
+
+ @Test
+ public void testGetEffectivePriority_Regular_High() {
+ final JobInfo jobInfo =
+ new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+ .setPriority(JobInfo.PRIORITY_HIGH)
+ .build();
+ JobStatus job = createJobStatus(jobInfo);
+
+ // Less than 2 failures, priority shouldn't be affected.
+ assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
+ int backoffAttempt = 1;
+ job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+ assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority());
+
+ // Failures in [2,4), priority should be lowered slightly.
+ backoffAttempt = 2;
+ job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+ assertEquals(JobInfo.PRIORITY_DEFAULT, job.getEffectivePriority());
+ backoffAttempt = 3;
+ job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+ assertEquals(JobInfo.PRIORITY_DEFAULT, job.getEffectivePriority());
+
+ // Failures in [4,6), priority should be lowered more.
+ backoffAttempt = 4;
+ job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+ assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
+ backoffAttempt = 5;
+ job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+ assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
+
+ // 6+ failures, priority should be lowered as much as possible.
+ backoffAttempt = 6;
+ job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+ assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority());
+ backoffAttempt = 12;
+ job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+ assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority());
+ }
+
+ /**
+ * Test that LOW priority jobs don't have their priority lowered as quickly as higher priority
+ * jobs.
+ */
+ @Test
+ public void testGetEffectivePriority_Regular_Low() {
+ final JobInfo jobInfo =
+ new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+ .setPriority(JobInfo.PRIORITY_LOW)
+ .build();
+ JobStatus job = createJobStatus(jobInfo);
+
+ // Less than 6 failures, priority shouldn't be affected.
+ assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
+ int backoffAttempt = 1;
+ job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+ assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
+ backoffAttempt = 4;
+ job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+ assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
+ backoffAttempt = 5;
+ job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+ assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority());
+
+ // 6+ failures, priority should be lowered as much as possible.
+ backoffAttempt = 6;
+ job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+ assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority());
+ backoffAttempt = 12;
+ job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, backoffAttempt, 0, 0);
+ assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority());
+ }
+
/**
* Test {@link JobStatus#wouldBeReadyWithConstraint} on explicit constraints that weren't
* requested.
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
index 98e089e..e5b2d14 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
@@ -20,15 +20,33 @@
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+import static com.android.server.job.JobSchedulerService.sSystemClock;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import android.app.AlarmManager;
+import android.app.job.JobInfo;
+import android.app.usage.UsageStatsManagerInternal;
+import android.app.usage.UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.ServiceInfo;
+import android.os.Looper;
import android.os.SystemClock;
import android.provider.DeviceConfig;
@@ -42,7 +60,9 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
@@ -55,15 +75,26 @@
@RunWith(AndroidJUnit4.class)
public class PrefetchControllerTest {
+ private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
+ private static final int SOURCE_USER_ID = 0;
+ private static final int CALLING_UID = 1000;
+ private static final long DEFAULT_WAIT_MS = 3000;
+ private static final String TAG_PREFETCH = "*job.prefetch*";
+
private PrefetchController mPrefetchController;
private PcConstants mPcConstants;
private DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder;
+ private EstimatedLaunchTimeChangedListener mEstimatedLaunchTimeChangedListener;
private MockitoSession mMockingSession;
@Mock
+ private AlarmManager mAlarmManager;
+ @Mock
private Context mContext;
@Mock
private JobSchedulerService mJobSchedulerService;
+ @Mock
+ private UsageStatsManagerInternal mUsageStatsManagerInternal;
@Before
public void setUp() {
@@ -77,6 +108,11 @@
// Called in StateController constructor.
when(mJobSchedulerService.getTestableContext()).thenReturn(mContext);
when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService);
+ // Called in PrefetchController constructor.
+ doReturn(mUsageStatsManagerInternal)
+ .when(() -> LocalServices.getService(UsageStatsManagerInternal.class));
+ when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+ when(mContext.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager);
// Used in PrefetchController.PcConstants
doAnswer((Answer<Void>) invocationOnMock -> null)
.when(() -> DeviceConfig.addOnPropertiesChangedListener(
@@ -93,7 +129,7 @@
// Freeze the clocks at 24 hours after this moment in time. Several tests create sessions
// in the past, and PrefetchController sometimes floors values at 0, so if the test time
// causes sessions with negative timestamps, they will fail.
- JobSchedulerService.sSystemClock =
+ sSystemClock =
getShiftedClock(Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC),
24 * HOUR_IN_MILLIS);
JobSchedulerService.sUptimeMillisClock = getShiftedClock(
@@ -105,8 +141,14 @@
// Initialize real objects.
// Capture the listeners.
+ ArgumentCaptor<EstimatedLaunchTimeChangedListener> eltListenerCaptor =
+ ArgumentCaptor.forClass(EstimatedLaunchTimeChangedListener.class);
mPrefetchController = new PrefetchController(mJobSchedulerService);
mPcConstants = mPrefetchController.getPcConstants();
+
+ verify(mUsageStatsManagerInternal)
+ .registerLaunchTimeChangedListener(eltListenerCaptor.capture());
+ mEstimatedLaunchTimeChangedListener = eltListenerCaptor.getValue();
}
@After
@@ -116,6 +158,29 @@
}
}
+ private JobStatus createJobStatus(String testTag, int jobId) {
+ JobInfo jobInfo = new JobInfo.Builder(jobId,
+ new ComponentName(mContext, "TestPrefetchJobService"))
+ .setPrefetch(true)
+ .build();
+ return createJobStatus(testTag, SOURCE_PACKAGE, CALLING_UID, jobInfo);
+ }
+
+ private static JobStatus createJobStatus(String testTag, String packageName, int callingUid,
+ JobInfo jobInfo) {
+ JobStatus js = JobStatus.createFromJobInfo(
+ jobInfo, callingUid, packageName, SOURCE_USER_ID, testTag);
+ js.serviceInfo = mock(ServiceInfo.class);
+ // Make sure Doze and background-not-restricted don't affect tests.
+ js.setDeviceNotDozingConstraintSatisfied(/* nowElapsed */ sElapsedRealtimeClock.millis(),
+ /* state */ true, /* allowlisted */false);
+ js.setBackgroundNotRestrictedConstraintSatisfied(
+ sElapsedRealtimeClock.millis(), true, false);
+ js.setTareWealthConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ js.setExpeditedJobTareApproved(sElapsedRealtimeClock.millis(), true);
+ return js;
+ }
+
private Clock getShiftedClock(Clock clock, long incrementMs) {
return Clock.offset(clock, Duration.ofMillis(incrementMs));
}
@@ -125,6 +190,15 @@
synchronized (mPrefetchController.mLock) {
mPrefetchController.prepareForUpdatedConstantsLocked();
mPcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
+ mPrefetchController.onConstantsUpdatedLocked();
+ }
+ }
+
+ private void trackJobs(JobStatus... jobs) {
+ for (JobStatus job : jobs) {
+ synchronized (mPrefetchController.mLock) {
+ mPrefetchController.maybeStartTrackingJobLocked(job, null);
+ }
}
}
@@ -147,4 +221,100 @@
assertEquals(24 * HOUR_IN_MILLIS, mPrefetchController.getLaunchTimeThresholdMs());
}
+
+ @Test
+ public void testConstantsUpdating_ThresholdChangesAlarms() {
+ final long launchDelayMs = 11 * HOUR_IN_MILLIS;
+ setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS);
+ when(mUsageStatsManagerInternal
+ .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID))
+ .thenReturn(sSystemClock.millis() + launchDelayMs);
+ JobStatus jobStatus = createJobStatus("testConstantsUpdating_ThresholdChangesAlarms", 1);
+ trackJobs(jobStatus);
+
+ InOrder inOrder = inOrder(mAlarmManager);
+
+ inOrder.verify(mAlarmManager, timeout(DEFAULT_WAIT_MS).times(1))
+ .setWindow(
+ anyInt(), eq(sElapsedRealtimeClock.millis() + 4 * HOUR_IN_MILLIS),
+ anyLong(), eq(TAG_PREFETCH), any(), any());
+
+ setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 3 * HOUR_IN_MILLIS);
+ inOrder.verify(mAlarmManager, timeout(DEFAULT_WAIT_MS).times(1))
+ .setWindow(
+ anyInt(), eq(sElapsedRealtimeClock.millis() + 8 * HOUR_IN_MILLIS),
+ anyLong(), eq(TAG_PREFETCH), any(), any());
+ }
+
+ @Test
+ public void testConstraintNotSatisfiedWhenLaunchLate() {
+ setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS);
+
+ final JobStatus job = createJobStatus("testConstraintNotSatisfiedWhenLaunchLate", 1);
+ when(mUsageStatsManagerInternal
+ .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID))
+ .thenReturn(sSystemClock.millis() + 10 * HOUR_IN_MILLIS);
+ trackJobs(job);
+ verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS))
+ .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
+ assertFalse(job.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ }
+
+ @Test
+ public void testConstraintSatisfiedWhenLaunchSoon() {
+ final JobStatus job = createJobStatus("testConstraintSatisfiedWhenLaunchSoon", 2);
+ when(mUsageStatsManagerInternal
+ .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID))
+ .thenReturn(sSystemClock.millis() + MINUTE_IN_MILLIS);
+ trackJobs(job);
+ verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS))
+ .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
+ assertTrue(job.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ }
+
+ @Test
+ public void testEstimatedLaunchTimeChangedToLate() {
+ setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS);
+ when(mUsageStatsManagerInternal
+ .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID))
+ .thenReturn(sSystemClock.millis() + HOUR_IN_MILLIS);
+
+ InOrder inOrder = inOrder(mUsageStatsManagerInternal);
+
+ JobStatus jobStatus = createJobStatus("testEstimatedLaunchTimeChangedToLate", 1);
+ trackJobs(jobStatus);
+ inOrder.verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS))
+ .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
+ assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+
+ mEstimatedLaunchTimeChangedListener.onEstimatedLaunchTimeChanged(SOURCE_USER_ID,
+ SOURCE_PACKAGE, sSystemClock.millis() + 10 * HOUR_IN_MILLIS);
+
+ inOrder.verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS).times(0))
+ .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
+ assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ }
+
+ @Test
+ public void testEstimatedLaunchTimeChangedToSoon() {
+ setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS);
+ when(mUsageStatsManagerInternal
+ .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID))
+ .thenReturn(sSystemClock.millis() + 10 * HOUR_IN_MILLIS);
+
+ InOrder inOrder = inOrder(mUsageStatsManagerInternal);
+
+ JobStatus jobStatus = createJobStatus("testEstimatedLaunchTimeChangedToSoon", 1);
+ trackJobs(jobStatus);
+ inOrder.verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS))
+ .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
+ assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+
+ mEstimatedLaunchTimeChangedListener.onEstimatedLaunchTimeChanged(SOURCE_USER_ID,
+ SOURCE_PACKAGE, sSystemClock.millis() + MINUTE_IN_MILLIS);
+
+ inOrder.verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS).times(0))
+ .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
+ assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java
new file mode 100644
index 0000000..ab08573
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java
@@ -0,0 +1,241 @@
+/*
+ * 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.job.restrictions;
+
+import static android.os.PowerManager.THERMAL_STATUS_CRITICAL;
+import static android.os.PowerManager.THERMAL_STATUS_EMERGENCY;
+import static android.os.PowerManager.THERMAL_STATUS_LIGHT;
+import static android.os.PowerManager.THERMAL_STATUS_MODERATE;
+import static android.os.PowerManager.THERMAL_STATUS_NONE;
+import static android.os.PowerManager.THERMAL_STATUS_SEVERE;
+import static android.os.PowerManager.THERMAL_STATUS_SHUTDOWN;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.app.job.JobInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.PowerManager;
+import android.provider.DeviceConfig;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.job.JobSchedulerService;
+import com.android.server.job.controllers.JobStatus;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+@RunWith(AndroidJUnit4.class)
+public class ThermalStatusRestrictionTest {
+ private static final int CALLING_UID = 1000;
+ private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
+ private static final int SOURCE_USER_ID = 0;
+
+ private ThermalStatusRestriction mThermalStatusRestriction;
+ private PowerManager.OnThermalStatusChangedListener mStatusChangedListener;
+
+ private MockitoSession mMockingSession;
+ @Mock
+ private Context mContext;
+ @Mock
+ private JobSchedulerService mJobSchedulerService;
+
+ @Before
+ public void setUp() {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .spyStatic(DeviceConfig.class)
+ .mockStatic(LocalServices.class)
+ .startMocking();
+
+ when(mJobSchedulerService.getTestableContext()).thenReturn(mContext);
+ when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService);
+
+ PowerManager powerManager = mock(PowerManager.class);
+ when(mContext.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ // Initialize real objects.
+ // Capture the listeners.
+ ArgumentCaptor<PowerManager.OnThermalStatusChangedListener> listenerCaptor =
+ ArgumentCaptor.forClass(PowerManager.OnThermalStatusChangedListener.class);
+ mThermalStatusRestriction = new ThermalStatusRestriction(mJobSchedulerService);
+ mThermalStatusRestriction.onSystemServicesReady();
+
+ verify(powerManager).addThermalStatusListener(listenerCaptor.capture());
+ mStatusChangedListener = listenerCaptor.getValue();
+ }
+
+ @After
+ public void tearDown() {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+ }
+
+ @Test
+ public void testStatusChanges() {
+ InOrder inOrder = inOrder(mJobSchedulerService);
+
+ mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_NONE);
+ inOrder.verify(mJobSchedulerService, never()).onControllerStateChanged(any());
+ assertEquals(THERMAL_STATUS_NONE, mThermalStatusRestriction.getThermalStatus());
+
+ // Moving within LOW and UPPER thresholds
+ mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_LIGHT);
+ inOrder.verify(mJobSchedulerService).onControllerStateChanged(any());
+ assertEquals(THERMAL_STATUS_LIGHT, mThermalStatusRestriction.getThermalStatus());
+
+ mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_MODERATE);
+ inOrder.verify(mJobSchedulerService).onControllerStateChanged(any());
+ assertEquals(THERMAL_STATUS_MODERATE, mThermalStatusRestriction.getThermalStatus());
+
+ mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_SEVERE);
+ inOrder.verify(mJobSchedulerService).onControllerStateChanged(any());
+ assertEquals(THERMAL_STATUS_SEVERE, mThermalStatusRestriction.getThermalStatus());
+
+ // Changing outside of range
+ mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_CRITICAL);
+ inOrder.verify(mJobSchedulerService, never()).onControllerStateChanged(any());
+ assertEquals(THERMAL_STATUS_CRITICAL, mThermalStatusRestriction.getThermalStatus());
+
+ mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_EMERGENCY);
+ inOrder.verify(mJobSchedulerService, never()).onControllerStateChanged(any());
+ assertEquals(THERMAL_STATUS_EMERGENCY, mThermalStatusRestriction.getThermalStatus());
+
+ mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_SHUTDOWN);
+ inOrder.verify(mJobSchedulerService, never()).onControllerStateChanged(any());
+ assertEquals(THERMAL_STATUS_SHUTDOWN, mThermalStatusRestriction.getThermalStatus());
+
+ // Cross values we care about
+ mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_NONE);
+ inOrder.verify(mJobSchedulerService).onControllerStateChanged(any());
+ assertEquals(THERMAL_STATUS_NONE, mThermalStatusRestriction.getThermalStatus());
+
+ mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_EMERGENCY);
+ inOrder.verify(mJobSchedulerService).onControllerStateChanged(any());
+ assertEquals(THERMAL_STATUS_EMERGENCY, mThermalStatusRestriction.getThermalStatus());
+ }
+
+ @Test
+ public void testIsJobRestricted() {
+ mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_NONE);
+
+ final JobStatus jobMinPriority = createJobStatus("testIsJobRestricted",
+ createJobBuilder(1).setPriority(JobInfo.PRIORITY_MIN).build());
+ final JobStatus jobLowPriority = createJobStatus("testIsJobRestricted",
+ createJobBuilder(2).setPriority(JobInfo.PRIORITY_LOW).build());
+ final JobStatus jobLowPriorityRunning = createJobStatus("testIsJobRestricted",
+ createJobBuilder(3).setPriority(JobInfo.PRIORITY_LOW).build());
+ final JobStatus jobDefaultPriority = createJobStatus("testIsJobRestricted",
+ createJobBuilder(4).setPriority(JobInfo.PRIORITY_DEFAULT).build());
+ final JobStatus jobHighPriority = createJobStatus("testIsJobRestricted",
+ createJobBuilder(5).setPriority(JobInfo.PRIORITY_HIGH).build());
+ final JobStatus jobHighPriorityRunning = createJobStatus("testIsJobRestricted",
+ createJobBuilder(6).setPriority(JobInfo.PRIORITY_HIGH).build());
+ final JobStatus ejDowngraded = createJobStatus("testIsJobRestricted",
+ createJobBuilder(7).setExpedited(true).build());
+ final JobStatus ej = spy(createJobStatus("testIsJobRestricted",
+ createJobBuilder(8).setExpedited(true).build()));
+ when(ej.shouldTreatAsExpeditedJob()).thenReturn(true);
+ when(mJobSchedulerService.isCurrentlyRunningLocked(jobLowPriorityRunning)).thenReturn(true);
+ when(mJobSchedulerService.isCurrentlyRunningLocked(jobHighPriorityRunning))
+ .thenReturn(true);
+
+ assertFalse(mThermalStatusRestriction.isJobRestricted(jobMinPriority));
+ assertFalse(mThermalStatusRestriction.isJobRestricted(jobLowPriority));
+ assertFalse(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunning));
+ assertFalse(mThermalStatusRestriction.isJobRestricted(jobDefaultPriority));
+ assertFalse(mThermalStatusRestriction.isJobRestricted(jobHighPriority));
+ assertFalse(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunning));
+ assertFalse(mThermalStatusRestriction.isJobRestricted(ej));
+ assertFalse(mThermalStatusRestriction.isJobRestricted(ejDowngraded));
+
+ mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_LIGHT);
+
+ assertTrue(mThermalStatusRestriction.isJobRestricted(jobMinPriority));
+ assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriority));
+ assertFalse(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunning));
+ assertFalse(mThermalStatusRestriction.isJobRestricted(jobDefaultPriority));
+ assertFalse(mThermalStatusRestriction.isJobRestricted(jobHighPriority));
+ assertFalse(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunning));
+ assertFalse(mThermalStatusRestriction.isJobRestricted(ejDowngraded));
+ assertFalse(mThermalStatusRestriction.isJobRestricted(ej));
+
+ mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_MODERATE);
+
+ assertTrue(mThermalStatusRestriction.isJobRestricted(jobMinPriority));
+ assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriority));
+ assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunning));
+ assertTrue(mThermalStatusRestriction.isJobRestricted(jobDefaultPriority));
+ assertTrue(mThermalStatusRestriction.isJobRestricted(jobHighPriority));
+ assertFalse(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunning));
+ assertTrue(mThermalStatusRestriction.isJobRestricted(ejDowngraded));
+ assertFalse(mThermalStatusRestriction.isJobRestricted(ej));
+
+ mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_SEVERE);
+
+ assertTrue(mThermalStatusRestriction.isJobRestricted(jobMinPriority));
+ assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriority));
+ assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunning));
+ assertTrue(mThermalStatusRestriction.isJobRestricted(jobDefaultPriority));
+ assertTrue(mThermalStatusRestriction.isJobRestricted(jobHighPriority));
+ assertTrue(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunning));
+ assertTrue(mThermalStatusRestriction.isJobRestricted(ejDowngraded));
+ assertTrue(mThermalStatusRestriction.isJobRestricted(ej));
+
+ mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_CRITICAL);
+
+ assertTrue(mThermalStatusRestriction.isJobRestricted(jobMinPriority));
+ assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriority));
+ assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunning));
+ assertTrue(mThermalStatusRestriction.isJobRestricted(jobDefaultPriority));
+ assertTrue(mThermalStatusRestriction.isJobRestricted(jobHighPriority));
+ assertTrue(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunning));
+ assertTrue(mThermalStatusRestriction.isJobRestricted(ejDowngraded));
+ assertTrue(mThermalStatusRestriction.isJobRestricted(ej));
+ }
+
+ private JobInfo.Builder createJobBuilder(int jobId) {
+ return new JobInfo.Builder(jobId,
+ new ComponentName(mContext, "ThermalStatusRestrictionTest"));
+ }
+
+ private JobStatus createJobStatus(String testTag, JobInfo jobInfo) {
+ return JobStatus.createFromJobInfo(
+ jobInfo, CALLING_UID, SOURCE_PACKAGE, SOURCE_USER_ID, testTag);
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 5b70f77..63416c9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -15,6 +15,7 @@
*/
package com.android.server.pm
+import android.app.PropertyInvalidatedCache
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
@@ -120,6 +121,7 @@
private val mSettingsMap = WatchedArrayMap<String, PackageSetting>()
init {
+ PropertyInvalidatedCache.disableForTestMode()
val apply = ExtendedMockito.mockitoSession()
.strictness(Strictness.LENIENT)
.mockStatic(SystemProperties::class.java)
@@ -161,6 +163,24 @@
mSettingsMap.putAll(mPreExistingSettings)
!mPreExistingSettings.isEmpty()
}
+ whenever(mocks.settings.setPackageStoppedStateLPw(any(), any(), anyBoolean(), anyInt())) {
+ val pm: PackageManagerService = getArgument(0)
+ val pkgSetting = mSettingsMap[getArgument(1)]!!
+ val stopped: Boolean = getArgument(2)
+ val userId: Int = getArgument(3)
+ return@whenever if (pkgSetting.getStopped(userId) != stopped) {
+ pkgSetting.setStopped(stopped, userId)
+ if (pkgSetting.getNotLaunched(userId)) {
+ pkgSetting.installSource.installerPackageName?.let {
+ pm.notifyFirstLaunch(pkgSetting.packageName, it, userId)
+ }
+ pkgSetting.setNotLaunched(false, userId)
+ }
+ true
+ } else {
+ false
+ }
+ }
}
/** Collection of mocks used for PackageManagerService tests. */
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
index 15acdac..dcdbb09 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
@@ -36,6 +36,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -100,8 +101,10 @@
rule.system().dataAppDirectory)
val pm = createPackageManagerService()
rule.system().validateFinalState()
- val ps = pm.getPackageSetting(TEST_PACKAGE_NAME)
- ps!!.setStopped(true, TEST_USER_ID)
+
+ pm.setPackageStoppedState(TEST_PACKAGE_NAME, true, TEST_USER_ID)
+ TestableLooper.get(this).processAllMessages()
+ Mockito.clearInvocations(appHibernationManager)
pm.setPackageStoppedState(TEST_PACKAGE_NAME, false, TEST_USER_ID)
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageSessionVerifierTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageSessionVerifierTest.java
new file mode 100644
index 0000000..1e399ff
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageSessionVerifierTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.pm;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.function.Predicate;
+
+@Presubmit
+@RunWith(JUnit4.class)
+public class PackageSessionVerifierTest {
+ private PackageSessionVerifier mSessionVerifier = new PackageSessionVerifier();
+
+ @Test
+ public void checkRebootlessApex() throws Exception {
+ StagingManager.StagedSession session1 = createStagedSession(111, "com.foo", 1);
+ mSessionVerifier.storeSession(session1);
+
+ // Should throw for package name conflicts
+ PackageInstallerSession session2 = createSession(false, true, "com.foo");
+ assertThrows(PackageManagerException.class,
+ () -> mSessionVerifier.checkRebootlessApex(session2));
+
+ // Shouldn't throw if no package name conflicts
+ PackageInstallerSession session3 = createSession(false, true, "com.bar");
+ mSessionVerifier.checkRebootlessApex(session3);
+ }
+
+ @Test
+ public void checkActiveSessions() throws Exception {
+ StagingManager.StagedSession session1 = createStagedSession(111, "com.foo", 1);
+ mSessionVerifier.storeSession(session1);
+ // Shouldn't throw for a single session no matter if supporting checkpoint or not
+ mSessionVerifier.checkActiveSessions(true);
+ mSessionVerifier.checkActiveSessions(false);
+
+ // Now we have multiple active sessions
+ StagingManager.StagedSession session2 = createStagedSession(222, "com.bar", 2);
+ mSessionVerifier.storeSession(session2);
+ // Shouldn't throw if supporting checkpoint
+ mSessionVerifier.checkActiveSessions(true);
+ // Should throw if not supporting checkpoint
+ assertThrows(PackageManagerException.class,
+ () -> mSessionVerifier.checkActiveSessions(false));
+ }
+
+ @Test
+ public void checkRollbacks() throws Exception {
+ StagingManager.StagedSession session1 = createStagedSession(111, "com.foo", 1);
+ StagingManager.StagedSession session2 = createStagedSession(222, "com.bar", 2);
+ session2.sessionParams().setInstallReason(PackageManager.INSTALL_REASON_ROLLBACK);
+ mSessionVerifier.storeSession(session1);
+ mSessionVerifier.storeSession(session2);
+
+ // Non-rollback session should fail
+ mSessionVerifier.checkRollbacks(session2);
+ verify(session1, times(1)).setSessionFailed(anyInt(), anyString());
+
+ // Yet another non-rollback session should fail
+ StagingManager.StagedSession session3 = createStagedSession(333, "com.baz", 3);
+ assertThrows(PackageManagerException.class,
+ () -> mSessionVerifier.checkRollbacks(session3));
+ }
+
+ @Test
+ public void checkOverlaps() throws Exception {
+ StagingManager.StagedSession session1 = createStagedSession(111, "com.foo", 1);
+ StagingManager.StagedSession session2 = createStagedSession(222, "com.foo", 2);
+ mSessionVerifier.storeSession(session1);
+ mSessionVerifier.storeSession(session2);
+ // No exception should be thrown for the earlier session should not fail
+ mSessionVerifier.checkOverlaps(session1, session1);
+ // Later session should fail
+ verify(session2, times(1)).setSessionFailed(anyInt(), anyString());
+ // Yet another later session should fail
+ StagingManager.StagedSession session3 = createStagedSession(333, "com.foo", 3);
+ assertThrows(PackageManagerException.class,
+ () -> mSessionVerifier.checkOverlaps(session3, session3));
+ }
+
+ private PackageInstallerSession createSession(boolean isStaged, boolean isApex,
+ String packageName) {
+ PackageInstallerSession session = mock(PackageInstallerSession.class);
+ when(session.isStaged()).thenReturn(isStaged);
+ when(session.isApexSession()).thenReturn(isApex);
+ when(session.getPackageName()).thenReturn(packageName);
+ return session;
+ }
+
+ private StagingManager.StagedSession createStagedSession(int sessionId, String packageName,
+ long committedMillis) {
+ StagingManager.StagedSession session = mock(StagingManager.StagedSession.class);
+ when(session.sessionId()).thenReturn(sessionId);
+ when(session.getPackageName()).thenReturn(packageName);
+ when(session.getCommittedMillis()).thenReturn(committedMillis);
+ when(session.sessionContains(any())).then(invocation -> {
+ Predicate<StagingManager.StagedSession> filter = invocation.getArgument(0);
+ return filter.test(session);
+ });
+ PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ when(session.sessionParams()).thenReturn(params);
+ return session;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index b72f05f..663bb2b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -454,66 +454,6 @@
}
@Test
- public void getSessionIdByPackageName() throws Exception {
- FakeStagedSession session = new FakeStagedSession(239);
- session.setCommitted(true);
- session.setSessionReady();
- session.setPackageName("com.foo");
-
- mStagingManager.createSession(session);
- assertThat(mStagingManager.getSessionIdByPackageName("com.foo")).isEqualTo(239);
- }
-
- @Test
- public void getSessionIdByPackageName_appliedSession_ignores() throws Exception {
- FakeStagedSession session = new FakeStagedSession(37);
- session.setCommitted(true);
- session.setSessionApplied();
- session.setPackageName("com.foo");
-
- mStagingManager.createSession(session);
- assertThat(mStagingManager.getSessionIdByPackageName("com.foo")).isEqualTo(-1);
- }
-
- @Test
- public void getSessionIdByPackageName_failedSession_ignores() throws Exception {
- FakeStagedSession session = new FakeStagedSession(73);
- session.setCommitted(true);
- session.setSessionFailed(1, "whatevs");
- session.setPackageName("com.foo");
-
- mStagingManager.createSession(session);
- assertThat(mStagingManager.getSessionIdByPackageName("com.foo")).isEqualTo(-1);
- }
-
- @Test
- public void getSessionIdByPackageName_destroyedSession_ignores() throws Exception {
- FakeStagedSession session = new FakeStagedSession(23);
- session.setCommitted(true);
- session.setDestroyed(true);
- session.setPackageName("com.foo");
-
- mStagingManager.createSession(session);
- assertThat(mStagingManager.getSessionIdByPackageName("com.foo")).isEqualTo(-1);
- }
-
- @Test
- public void getSessionIdByPackageName_noSessions() throws Exception {
- assertThat(mStagingManager.getSessionIdByPackageName("com.foo")).isEqualTo(-1);
- }
-
- @Test
- public void getSessionIdByPackageName_noSessionHasThisPackage() throws Exception {
- FakeStagedSession session = new FakeStagedSession(37);
- session.setCommitted(true);
- session.setSessionApplied();
- session.setPackageName("com.foo");
-
- mStagingManager.createSession(session);
- assertThat(mStagingManager.getSessionIdByPackageName("com.bar")).isEqualTo(-1);
- }
-
- @Test
public void getStagedApexInfos_validatePreConditions() throws Exception {
// Invalid session: null session
{
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt
index f17fa62..87bfd56 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt
@@ -20,6 +20,7 @@
import android.os.Build
import android.os.Bundle
import android.util.SparseArray
+import com.android.server.pm.pkg.PackageStateInternal
import com.android.server.testutils.any
import com.android.server.testutils.eq
import com.android.server.testutils.nullable
@@ -49,8 +50,8 @@
}
lateinit var pms: PackageManagerService
- lateinit var packageSetting1: PackageSetting
- lateinit var packageSetting2: PackageSetting
+ lateinit var packageSetting1: PackageStateInternal
+ lateinit var packageSetting2: PackageStateInternal
lateinit var packagesToSuspend: Array<String>
lateinit var uidsToSuspend: IntArray
@@ -67,8 +68,8 @@
MockitoAnnotations.initMocks(this)
rule.system().stageNominalSystemState()
pms = spy(createPackageManagerService(TEST_PACKAGE_1, TEST_PACKAGE_2))
- packageSetting1 = pms.getPackageSetting(TEST_PACKAGE_1)!!
- packageSetting2 = pms.getPackageSetting(TEST_PACKAGE_2)!!
+ packageSetting1 = pms.getPackageStateInternal(TEST_PACKAGE_1)!!
+ packageSetting2 = pms.getPackageStateInternal(TEST_PACKAGE_2)!!
packagesToSuspend = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
uidsToSuspend = intArrayOf(packageSetting1.appId, packageSetting2.appId)
}
@@ -153,10 +154,10 @@
this.put(TEST_USER_ID, uids)
}
- private fun mockAllowList(pkgSetting: PackageSetting, list: SparseArray<IntArray>?) {
+ private fun mockAllowList(pkgSetting: PackageStateInternal, list: SparseArray<IntArray>?) {
whenever(rule.mocks().injector.appsFilter.getVisibilityAllowList(eq(pkgSetting),
- any(IntArray::class.java), any() as WatchedArrayMap<String, PackageSetting>))
- .thenReturn(list)
+ any(IntArray::class.java), any() as WatchedArrayMap<String, out PackageStateInternal>))
+ .thenReturn(list)
}
private fun createPackageManagerService(vararg stageExistingPackages: String):
diff --git a/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java
index 24c58f4..1542b01 100644
--- a/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java
@@ -18,8 +18,11 @@
import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
import static android.app.usage.UsageEvents.Event.APP_COMPONENT_USED;
+import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mockitoSession;
@@ -28,6 +31,7 @@
import android.content.Context;
import android.os.SystemClock;
import android.text.format.DateUtils;
+import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -47,6 +51,8 @@
@RunWith(AndroidJUnit4.class)
public class UserUsageStatsServiceTest {
+ private static final String TAG = UserUsageStatsServiceTest.class.getSimpleName();
+
private static final int TEST_USER_ID = 0;
private static final String TEST_PACKAGE_NAME = "test.package";
private static final long TIME_INTERVAL_MILLIS = DateUtils.DAY_IN_MILLIS;
@@ -54,6 +60,8 @@
private UserUsageStatsService mService;
private MockitoSession mMockitoSession;
+ private File mDir;
+
@Mock
private Context mContext;
@Mock
@@ -66,8 +74,11 @@
.strictness(Strictness.LENIENT)
.startMocking();
- File dir = new File(InstrumentationRegistry.getContext().getCacheDir(), "test");
- mService = new UserUsageStatsService(mContext, TEST_USER_ID, dir, mStatsUpdatedListener);
+ // Deleting in tearDown() doesn't always work, so adding a unique suffix to each test
+ // directory to ensure sequential test runs don't interfere with each other.
+ mDir = new File(InstrumentationRegistry.getContext().getCacheDir(),
+ "test_" + System.currentTimeMillis());
+ mService = new UserUsageStatsService(mContext, TEST_USER_ID, mDir, mStatsUpdatedListener);
HashMap<String, Long> installedPkgs = new HashMap<>();
installedPkgs.put(TEST_PACKAGE_NAME, System.currentTimeMillis());
@@ -77,6 +88,9 @@
@After
public void tearDown() {
+ if (mDir != null && mDir.exists() && !mDir.delete()) {
+ Log.d(TAG, "Failed to delete test directory");
+ }
if (mMockitoSession != null) {
mMockitoSession.finishMocking();
}
@@ -88,6 +102,9 @@
event.mPackage = TEST_PACKAGE_NAME;
mService.reportEvent(event);
+ // Force persist the event instead of waiting for it to be processed on the handler.
+ mService.persistActiveStats();
+
long now = System.currentTimeMillis();
long startTime = now - TIME_INTERVAL_MILLIS;
UsageEvents events = mService.queryEventsForPackage(
@@ -112,6 +129,9 @@
event.mPackage = TEST_PACKAGE_NAME;
mService.reportEvent(event);
+ // Force persist the event instead of waiting for it to be processed on the handler.
+ mService.persistActiveStats();
+
long now = System.currentTimeMillis();
long startTime = now - TIME_INTERVAL_MILLIS;
UsageEvents events = mService.queryEventsForPackage(
@@ -127,4 +147,36 @@
}
assertFalse(hasTestEvent);
}
+
+ @Test
+ public void testQueryEarliestEventsForPackage() {
+ Event event1 = new Event(NOTIFICATION_SEEN, SystemClock.elapsedRealtime());
+ event1.mPackage = TEST_PACKAGE_NAME;
+ mService.reportEvent(event1);
+ Event event2 = new Event(ACTIVITY_RESUMED, SystemClock.elapsedRealtime());
+ event2.mPackage = TEST_PACKAGE_NAME;
+ mService.reportEvent(event2);
+
+ // Force persist the events instead of waiting for them to be processed on the handler.
+ mService.persistActiveStats();
+
+ long now = System.currentTimeMillis();
+ long startTime = now - TIME_INTERVAL_MILLIS;
+ UsageEvents events = mService.queryEarliestEventsForPackage(
+ startTime, now, TEST_PACKAGE_NAME, ACTIVITY_RESUMED);
+
+ assertNotNull(events);
+ boolean hasTestEvent = false;
+ int count = 0;
+ while (events.hasNextEvent()) {
+ count++;
+ Event outEvent = new Event();
+ events.getNextEvent(outEvent);
+ if (outEvent.mEventType == ACTIVITY_RESUMED) {
+ hasTestEvent = true;
+ }
+ }
+ assertTrue(hasTestEvent);
+ assertEquals(2, count);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/GestureManifoldTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/GestureManifoldTest.java
index 0e78785..45f0e67 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/GestureManifoldTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/GestureManifoldTest.java
@@ -27,18 +27,20 @@
import android.graphics.Point;
import android.graphics.PointF;
import android.os.Handler;
+import android.view.Display;
import android.view.MotionEvent;
import androidx.test.InstrumentationRegistry;
+import com.android.server.accessibility.AccessibilityManagerService;
+
import org.junit.Before;
import org.junit.Test;
+import org.mockito.Mock;
import java.util.ArrayList;
-/**
- * Tests for GestureManifold
- */
+/** Tests for GestureManifold */
public class GestureManifoldTest {
// Constants for testRecognizeGesturePath()
@@ -50,25 +52,23 @@
private GestureManifold mManifold;
private TouchState mState;
private GestureManifold.Listener mResultListener;
+ @Mock private AccessibilityManagerService mMockAms;
@Before
public void setUp() {
Context context = InstrumentationRegistry.getContext();
// Construct a testable GestureManifold.
mResultListener = mock(GestureManifold.Listener.class);
- mState = new TouchState();
+ mState = new TouchState(Display.DEFAULT_DISPLAY, mMockAms);
Handler handler = new Handler(context.getMainLooper());
mManifold = new GestureManifold(context, mResultListener, mState, handler);
// Play the role of touch explorer in updating the shared state.
when(mResultListener.onGestureStarted()).thenReturn(onGestureStarted());
-
-
}
-
@Test
public void testRecognizeGesturePath() {
- final int d = 1000; // Length of each segment in the test gesture, in pixels.
+ final int d = 1000; // Length of each segment in the test gesture, in pixels.
testPath(p(-d, +0), AccessibilityService.GESTURE_SWIPE_LEFT);
testPath(p(+d, +0), AccessibilityService.GESTURE_SWIPE_RIGHT);
@@ -138,9 +138,10 @@
// For each path step from start (non-inclusive) to end ... add a motion point.
for (int step = 1; step < numSteps; ++step) {
- path.add(new PointF(
- (start.x + (stepX * (float) step)),
- (start.y + (stepY * (float) step))));
+ path.add(
+ new PointF(
+ (start.x + (stepX * (float) step)),
+ (start.y + (stepY * (float) step))));
}
}
@@ -164,11 +165,12 @@
} else if (pointIndex == path.size() - 1) {
action = MotionEvent.ACTION_UP;
}
- MotionEvent event = MotionEvent.obtain(eventDownTimeMs, eventTimeMs, action,
- point.x, point.y, 0);
+ MotionEvent event =
+ MotionEvent.obtain(eventDownTimeMs, eventTimeMs, action, point.x, point.y, 0);
// Send event.
- mState.onReceivedMotionEvent(event);
+ // In this case the event and raw event values are the same.
+ mState.onReceivedMotionEvent(event, event, policyFlags);
mManifold.onMotionEvent(event, event, policyFlags);
eventTimeMs += PATH_STEP_MILLISEC;
if (mState.isClear()) {
@@ -178,8 +180,9 @@
mState.clear();
// Check that correct gesture was recognized.
- verify(mResultListener).onGestureCompleted(
- argThat(gestureEvent -> gestureEvent.getGestureId() == gestureId));
+ verify(mResultListener)
+ .onGestureCompleted(
+ argThat(gestureEvent -> gestureEvent.getGestureId() == gestureId));
}
private boolean onGestureStarted() {
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 12e0d8b..f4a83c3 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -98,6 +98,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
@@ -164,6 +165,8 @@
mAms.mWaitForNetworkTimeoutMs = 2000;
mAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
mAms.mActivityTaskManager.initialize(null, null, mHandler.getLooper());
+ mHandler.setRunnablesToIgnore(
+ List.of(mAms.mUidObserverController.getDispatchRunnableForTest()));
}
private void mockNoteOperation() {
@@ -983,13 +986,22 @@
private static final long WAIT_FOR_MSG_TIMEOUT_MS = 4000; // 4 sec
private static final long WAIT_FOR_MSG_INTERVAL_MS = 400; // 0.4 sec
- private Set<Integer> mMsgsHandled = new HashSet<>();
+ private final Set<Integer> mMsgsHandled = new HashSet<>();
+ private final List<Runnable> mRunnablesToIgnore = new ArrayList<>();
TestHandler(Looper looper) {
super(looper);
}
@Override
+ public void dispatchMessage(Message msg) {
+ if (msg.getCallback() != null && mRunnablesToIgnore.contains(msg.getCallback())) {
+ return;
+ }
+ super.dispatchMessage(msg);
+ }
+
+ @Override
public void handleMessage(Message msg) {
mMsgsHandled.add(msg.what);
}
@@ -1004,6 +1016,11 @@
}
}
+ public void setRunnablesToIgnore(List<Runnable> runnables) {
+ mRunnablesToIgnore.clear();
+ mRunnablesToIgnore.addAll(runnables);
+ }
+
public void reset() {
mMsgsHandled.clear();
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
index 0cd6d86..0ac00aa 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
@@ -19,13 +19,17 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.hardware.biometrics.common.CommonProps;
import android.hardware.biometrics.face.IFace;
+import android.hardware.biometrics.face.ISession;
import android.hardware.biometrics.face.SensorProps;
+import android.os.RemoteException;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
@@ -33,7 +37,6 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.HalClientMonitor;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
@@ -55,6 +58,8 @@
private Context mContext;
@Mock
private UserManager mUserManager;
+ @Mock
+ private IFace mDaemon;
private SensorProps[] mSensorProps;
private LockoutResetDispatcher mLockoutResetDispatcher;
@@ -65,11 +70,12 @@
}
@Before
- public void setUp() {
+ public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>());
+ when(mDaemon.createSession(anyInt(), anyInt(), any())).thenReturn(mock(ISession.class));
final SensorProps sensor1 = new SensorProps();
sensor1.commonProps = new CommonProps();
@@ -78,11 +84,11 @@
sensor2.commonProps = new CommonProps();
sensor2.commonProps.sensorId = 1;
- mSensorProps = new SensorProps[] {sensor1, sensor2};
+ mSensorProps = new SensorProps[]{sensor1, sensor2};
mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
- mFaceProvider = new TestableFaceProvider(mContext, mSensorProps, TAG,
+ mFaceProvider = new TestableFaceProvider(mDaemon, mContext, mSensorProps, TAG,
mLockoutResetDispatcher);
}
@@ -127,17 +133,20 @@
}
private static class TestableFaceProvider extends FaceProvider {
- public TestableFaceProvider(@NonNull Context context,
+ private final IFace mDaemon;
+
+ TestableFaceProvider(@NonNull IFace daemon,
+ @NonNull Context context,
@NonNull SensorProps[] props,
@NonNull String halInstanceName,
@NonNull LockoutResetDispatcher lockoutResetDispatcher) {
super(context, props, halInstanceName, lockoutResetDispatcher);
+ mDaemon = daemon;
}
@Override
synchronized IFace getHalInstance() {
- return mock(IFace.class);
+ return mDaemon;
}
}
-
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
index 8b7c90d..73f1516 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -28,8 +29,10 @@
import android.content.res.TypedArray;
import android.hardware.biometrics.common.CommonProps;
import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.biometrics.fingerprint.SensorLocation;
import android.hardware.biometrics.fingerprint.SensorProps;
+import android.os.RemoteException;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
@@ -63,6 +66,8 @@
@Mock
private UserManager mUserManager;
@Mock
+ private IFingerprint mDaemon;
+ @Mock
private GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
@Mock
private FingerprintStateCallback mFingerprintStateCallback;
@@ -76,13 +81,14 @@
}
@Before
- public void setUp() {
+ public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
when(mContext.getResources()).thenReturn(mResources);
when(mResources.obtainTypedArray(anyInt())).thenReturn(mock(TypedArray.class));
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>());
+ when(mDaemon.createSession(anyInt(), anyInt(), any())).thenReturn(mock(ISession.class));
final SensorProps sensor1 = new SensorProps();
sensor1.commonProps = new CommonProps();
@@ -97,8 +103,9 @@
mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
- mFingerprintProvider = new TestableFingerprintProvider(mContext, mFingerprintStateCallback,
- mSensorProps, TAG, mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
+ mFingerprintProvider = new TestableFingerprintProvider(mDaemon, mContext,
+ mFingerprintStateCallback, mSensorProps, TAG, mLockoutResetDispatcher,
+ mGestureAvailabilityDispatcher);
}
@SuppressWarnings("rawtypes")
@@ -142,7 +149,10 @@
}
private static class TestableFingerprintProvider extends FingerprintProvider {
- public TestableFingerprintProvider(@NonNull Context context,
+ private final IFingerprint mDaemon;
+
+ TestableFingerprintProvider(@NonNull IFingerprint daemon,
+ @NonNull Context context,
@NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull SensorProps[] props,
@NonNull String halInstanceName,
@@ -150,11 +160,12 @@
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
super(context, fingerprintStateCallback, props, halInstanceName, lockoutResetDispatcher,
gestureAvailabilityDispatcher);
+ mDaemon = daemon;
}
@Override
synchronized IFingerprint getHalInstance() {
- return mock(IFingerprint.class);
+ return mDaemon;
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/criticalevents/CriticalEventLogTest.java b/services/tests/servicestests/src/com/android/server/criticalevents/CriticalEventLogTest.java
index dca666c..54e75aa 100644
--- a/services/tests/servicestests/src/com/android/server/criticalevents/CriticalEventLogTest.java
+++ b/services/tests/servicestests/src/com/android/server/criticalevents/CriticalEventLogTest.java
@@ -19,12 +19,15 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import android.server.ServerProtoEnums;
+
import com.android.framework.protobuf.nano.MessageNano;
import com.android.server.criticalevents.CriticalEventLog.ILogLoader;
import com.android.server.criticalevents.CriticalEventLog.LogLoader;
import com.android.server.criticalevents.nano.CriticalEventLogProto;
import com.android.server.criticalevents.nano.CriticalEventLogStorageProto;
import com.android.server.criticalevents.nano.CriticalEventProto;
+import com.android.server.criticalevents.nano.CriticalEventProto.AppNotResponding;
import com.android.server.criticalevents.nano.CriticalEventProto.HalfWatchdog;
import com.android.server.criticalevents.nano.CriticalEventProto.Watchdog;
@@ -63,6 +66,13 @@
private static final String UUID_STRING = "123e4567-e89b-12d3-a456-556642440000";
+ private static final int SYSTEM_SERVER_UID = 1000;
+ private static final int SYSTEM_APP_UID = 1001;
+
+ private static final int DATA_APP_UID = 10_001;
+ private static final int DATA_APP_UID_2 = 10_002;
+ private static final int DATA_APP_UID_3 = 10_003;
+
@Rule
public TemporaryFolder mFolder = new TemporaryFolder();
@@ -80,7 +90,7 @@
createTestFileWithEvents(2);
setLogInstance(); // Log instance reads the proto file at initialization.
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
assertProtoArrayEquals(
@@ -96,7 +106,7 @@
mTestFile.delete();
setLogInstance();
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
assertThat(logProto.events).isEmpty();
@@ -107,7 +117,7 @@
mFolder.delete();
setLogInstance();
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
assertThat(logProto.events).isEmpty();
@@ -119,7 +129,7 @@
mTestFile.setReadable(false);
setLogInstance();
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
assertThat(logProto.events).isEmpty();
@@ -132,7 +142,7 @@
}
setLogInstance();
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
assertThat(logProto.events).isEmpty();
@@ -143,7 +153,7 @@
createTestFileWithEvents(0);
setLogInstance();
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
assertThat(logProto.events).isEmpty();
@@ -154,7 +164,7 @@
createTestFileWithEvents(10); // Ring buffer capacity is 5
setLogInstance();
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
// Log contains the last 5 events only.
@@ -170,7 +180,7 @@
}
@Test
- public void logLinesForAnrFile() {
+ public void logLinesForTraceFile() {
mCriticalEventLog.incTimeSeconds(1);
mCriticalEventLog.logWatchdog("Watchdog subject",
UUID.fromString("123e4567-e89b-12d3-a456-556642440000"));
@@ -178,7 +188,7 @@
mCriticalEventLog.logHalfWatchdog("Half watchdog subject");
mCriticalEventLog.incTimeSeconds(1);
- assertThat(mCriticalEventLog.logLinesForAnrFile()).isEqualTo(
+ assertThat(mCriticalEventLog.logLinesForSystemServerTraceFile()).isEqualTo(
"--- CriticalEventLog ---\n"
+ "capacity: 5\n"
+ "events <\n"
@@ -205,7 +215,7 @@
UUID.fromString("123e4567-e89b-12d3-a456-556642440000"));
mCriticalEventLog.incTimeSeconds(1);
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS + 2000);
assertProtoArrayEquals(logProto.events, new CriticalEventProto[]{
@@ -220,7 +230,7 @@
mCriticalEventLog.logHalfWatchdog("Subject 1");
mCriticalEventLog.incTimeSeconds(1);
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS + 2000);
assertProtoArrayEquals(logProto.events, new CriticalEventProto[]{
@@ -229,6 +239,108 @@
}
@Test
+ public void logAnr() {
+ mCriticalEventLog.incTimeSeconds(1);
+ mCriticalEventLog.logAnr("Subject 1", ServerProtoEnums.SYSTEM_SERVER, "AID_SYSTEM",
+ SYSTEM_SERVER_UID, 0);
+ mCriticalEventLog.incTimeSeconds(1);
+
+ CriticalEventLogProto logProto = getLogOutput();
+
+ assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS + 2000);
+ assertProtoArrayEquals(logProto.events, new CriticalEventProto[]{
+ anr(START_TIME_MS + 1000, "Subject 1", ServerProtoEnums.SYSTEM_SERVER,
+ "AID_SYSTEM", SYSTEM_SERVER_UID, 0)
+ });
+ }
+
+ @Test
+ public void privacyRedaction_anr() {
+ mCriticalEventLog.incTimeSeconds(1);
+ mCriticalEventLog.logAnr("Subject 1", ServerProtoEnums.SYSTEM_SERVER, "AID_SYSTEM",
+ SYSTEM_SERVER_UID, 0);
+ mCriticalEventLog.incTimeSeconds(1);
+ mCriticalEventLog.logAnr("Subject 2", ServerProtoEnums.SYSTEM_APP, "AID_RADIO",
+ SYSTEM_APP_UID, 1);
+ mCriticalEventLog.incTimeSeconds(1);
+ mCriticalEventLog.logAnr("Subject 3", ServerProtoEnums.DATA_APP, "com.foo",
+ DATA_APP_UID, 2);
+ mCriticalEventLog.incTimeSeconds(1);
+ mCriticalEventLog.logAnr("Subject 4", ServerProtoEnums.DATA_APP, "com.foo",
+ DATA_APP_UID_2, 3);
+ mCriticalEventLog.incTimeSeconds(1);
+ mCriticalEventLog.logAnr("Subject 5", ServerProtoEnums.DATA_APP, "com.bar",
+ DATA_APP_UID_3, 4);
+ mCriticalEventLog.incTimeSeconds(1);
+
+ CriticalEventProto systemServerAnr = anr(START_TIME_MS + 1000, "Subject 1",
+ CriticalEventProto.SYSTEM_SERVER, "AID_SYSTEM", SYSTEM_SERVER_UID, 0);
+ CriticalEventProto systemAppAnr = anr(START_TIME_MS + 2000, "Subject 2",
+ CriticalEventProto.SYSTEM_APP,
+ "AID_RADIO", SYSTEM_APP_UID, 1);
+ CriticalEventProto fooAppAnr = anr(START_TIME_MS + 3000, "Subject 3",
+ CriticalEventProto.DATA_APP, "com.foo", DATA_APP_UID, 2);
+ CriticalEventProto fooAppAnrUid2 = anr(START_TIME_MS + 4000, "Subject 4",
+ CriticalEventProto.DATA_APP, "com.foo", DATA_APP_UID_2, 3);
+ CriticalEventProto fooAppAnrUid2Redacted = anr(START_TIME_MS + 4000, "",
+ CriticalEventProto.DATA_APP, "", DATA_APP_UID_2, 3);
+ CriticalEventProto barAppAnr = anr(START_TIME_MS + 5000, "Subject 5",
+ CriticalEventProto.DATA_APP, "com.bar", DATA_APP_UID_3, 4);
+ CriticalEventProto barAppAnrRedacted = anr(START_TIME_MS + 5000, "",
+ CriticalEventProto.DATA_APP, "", DATA_APP_UID_3, 4);
+
+ assertProtoArrayEquals(
+ getLogOutput(ServerProtoEnums.DATA_APP, "com.foo", DATA_APP_UID).events,
+ new CriticalEventProto[]{
+ systemServerAnr,
+ systemAppAnr,
+ fooAppAnr,
+ // Redacted since the trace file and ANR are for different uids.
+ fooAppAnrUid2Redacted,
+ // Redacted since the trace file and ANR are for different data apps.
+ barAppAnrRedacted
+ });
+
+ assertProtoArrayEquals(
+ getLogOutput(ServerProtoEnums.SYSTEM_SERVER, "AID_SYSTEM",
+ SYSTEM_SERVER_UID).events,
+ new CriticalEventProto[]{
+ systemServerAnr,
+ systemAppAnr,
+ fooAppAnr,
+ fooAppAnrUid2,
+ barAppAnr
+ });
+
+ assertProtoArrayEquals(
+ getLogOutput(ServerProtoEnums.SYSTEM_APP, "AID_RADIO",
+ SYSTEM_APP_UID).events,
+ new CriticalEventProto[]{
+ systemServerAnr,
+ systemAppAnr,
+ fooAppAnr,
+ fooAppAnrUid2,
+ barAppAnr
+ });
+ }
+
+ @Test
+ public void privacyRedaction_anr_doesNotMutateLogState() {
+ mCriticalEventLog.logAnr("Subject", ServerProtoEnums.DATA_APP, "com.foo",
+ 10_001, DATA_APP_UID);
+
+ CriticalEventLogProto unredactedLogBefore = getLogOutput(ServerProtoEnums.SYSTEM_SERVER,
+ "AID_SYSTEM", SYSTEM_SERVER_UID);
+ CriticalEventLogProto redactedLog = getLogOutput(ServerProtoEnums.DATA_APP, "com.bar",
+ DATA_APP_UID);
+ CriticalEventLogProto unredactedLogAfter = getLogOutput(ServerProtoEnums.SYSTEM_SERVER,
+ "AID_SYSTEM", SYSTEM_SERVER_UID);
+
+ assertProtoNotEqual(unredactedLogBefore, redactedLog); // verify some redaction took place.
+ assertProtoEquals(unredactedLogBefore, unredactedLogAfter);
+ }
+
+ @Test
public void getOutputLogProto_numberOfEventsExceedsCapacity() {
// Log 10 events in 10 sec (capacity = 5)
for (int i = 0; i < 10; i++) {
@@ -237,7 +349,7 @@
mCriticalEventLog.incTimeSeconds(1);
}
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS + 10000);
assertThat(logProto.windowMs).isEqualTo(300_000); // 5 minutes
@@ -270,7 +382,7 @@
mCriticalEventLog.logHalfWatchdog("New event 2"); // 5m59s old
mCriticalEventLog.setCurrentTimeMillis(logTimestamp);
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
assertThat(logProto.timestampMs).isEqualTo(logTimestamp);
assertThat(logProto.windowMs).isEqualTo(300_000); // 5 minutes
@@ -288,7 +400,7 @@
createTestFileWithEvents(5);
setLogInstance(new NoOpLogLoader());
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
// Output log is empty.
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
@@ -350,7 +462,7 @@
log2.logHalfWatchdog("New subject");
log2.incTimeSeconds(1);
- CriticalEventLogProto logProto = log2.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput(log2);
// Log contains 4 + 1 events.
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS + 21_000);
@@ -363,6 +475,38 @@
});
}
+ @Test
+ public void processClassEnumParity() {
+ String message = "CriticalEventProto.ProcessClass and ServerProtoEnum are out of sync.";
+ assertWithMessage(message).that(CriticalEventProto.PROCESS_CLASS_UNKNOWN).isEqualTo(
+ ServerProtoEnums.ERROR_SOURCE_UNKNOWN);
+ assertWithMessage(message).that(CriticalEventProto.DATA_APP).isEqualTo(
+ ServerProtoEnums.DATA_APP);
+ assertWithMessage(message).that(CriticalEventProto.SYSTEM_APP).isEqualTo(
+ ServerProtoEnums.SYSTEM_APP);
+ assertWithMessage(message).that(CriticalEventProto.SYSTEM_SERVER).isEqualTo(
+ ServerProtoEnums.SYSTEM_SERVER);
+ }
+
+ private CriticalEventLogProto getLogOutput() {
+ return getLogOutput(mCriticalEventLog);
+ }
+
+ private CriticalEventLogProto getLogOutput(CriticalEventLog log) {
+ return getLogOutput(log, ServerProtoEnums.SYSTEM_SERVER, "AID_SYSTEM", SYSTEM_SERVER_UID);
+ }
+
+ private CriticalEventLogProto getLogOutput(int traceProcessClassEnum,
+ String traceProcessName, int traceProcessUid) {
+ return getLogOutput(mCriticalEventLog, traceProcessClassEnum, traceProcessName,
+ traceProcessUid);
+ }
+
+ private CriticalEventLogProto getLogOutput(CriticalEventLog log, int traceProcessClassEnum,
+ String traceProcessName, int traceProcessUid) {
+ return log.getOutputLogProto(traceProcessClassEnum, traceProcessName, traceProcessUid);
+ }
+
private CriticalEventLogStorageProto getEventsWritten() throws IOException {
return CriticalEventLogStorageProto.parseFrom(
Files.readAllBytes(mTestFile.toPath()));
@@ -389,6 +533,20 @@
}
}
+ private static CriticalEventProto anr(long timestampMs, String subject, int processClass,
+ String processName,
+ int uid, int pid) {
+ CriticalEventProto event = new CriticalEventProto();
+ event.timestampMs = timestampMs;
+ event.setAnr(new AppNotResponding());
+ event.getAnr().subject = subject;
+ event.getAnr().processClass = processClass;
+ event.getAnr().process = processName;
+ event.getAnr().uid = uid;
+ event.getAnr().pid = pid;
+ return event;
+ }
+
private CriticalEventProto watchdog(long timestampMs, String subject) {
return watchdog(timestampMs, subject, "A UUID");
}
@@ -414,15 +572,32 @@
assertThat(expected).isNotNull();
assertThat(actual).isNotNull();
- String message =
- "Expected:\n" + Arrays.toString(expected) + "\nGot:\n" + Arrays.toString(actual);
- assertWithMessage(message).that(expected.length).isEqualTo(actual.length);
+ String baseMsg = String.format("Expected:\n%s\nGot:\n%s", Arrays.toString(expected),
+ Arrays.toString(actual));
+ String lengthMsg = String.format("%s\nGot different length arrays.\bExpected %d, got %d",
+ baseMsg, expected.length, actual.length);
+ assertWithMessage(lengthMsg).that(expected.length).isEqualTo(actual.length);
for (int i = 0; i < expected.length; i++) {
- assertWithMessage(message).that(
+ String pairMsg = String.format("%s\nMismatched pair.\nExpected:\n%s\nGot:\n%s",
+ baseMsg, expected[i], actual[i]);
+ assertWithMessage(pairMsg).that(
MessageNano.messageNanoEquals(expected[i], actual[i])).isTrue();
}
}
+ private static void assertProtoEquals(MessageNano actual, MessageNano expected) {
+ String message = String.format("Expected:\n%s\nGot:\n%s", expected, actual);
+ assertWithMessage(message).that(
+ MessageNano.messageNanoEquals(expected, actual)).isTrue();
+ }
+
+ private static void assertProtoNotEqual(MessageNano first, MessageNano second) {
+ String message = String.format("Expected protos to be different, but were equal:\n%s",
+ first);
+ assertWithMessage(message).that(
+ MessageNano.messageNanoEquals(first, second)).isFalse();
+ }
+
private TestableCriticalEventLog setLogInstance() {
return setLogInstance(new LogLoader());
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index d10419d..d4b1165 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -439,6 +439,12 @@
}
@Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) {
+ mMockSystemServices.registerReceiver(receiver, filter, null);
+ return spiedContext.registerReceiver(receiver, filter, flags);
+ }
+
+ @Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
mMockSystemServices.registerReceiver(receiver, filter, scheduler);
diff --git a/services/tests/servicestests/src/com/android/server/health/OWNERS b/services/tests/servicestests/src/com/android/server/health/OWNERS
new file mode 100644
index 0000000..81522fc
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/health/OWNERS
@@ -0,0 +1 @@
+file:platform/hardware/interfaces:/health/aidl/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index 243f7b4..4de15c8 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -328,6 +328,23 @@
assertEquals("Bias not correctly persisted.", 42, loaded.getBias());
}
+ @Test
+ public void testPriorityPersisted() throws Exception {
+ final JobInfo.Builder b = new Builder(92, mComponent)
+ .setOverrideDeadline(5000)
+ .setPriority(JobInfo.PRIORITY_MIN)
+ .setPersisted(true);
+ final JobStatus js = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null);
+ mTaskStoreUnderTest.add(js);
+ waitForPendingIo();
+
+ final JobSet jobStatusSet = new JobSet();
+ mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
+ final JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
+ assertEquals("Priority not correctly persisted.",
+ JobInfo.PRIORITY_MIN, loaded.getEffectivePriority());
+ }
+
/**
* Test that non persisted job is not written to disk.
*/
diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
index 1141d9b..b3a513f 100644
--- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
@@ -34,6 +34,8 @@
import android.Manifest;
import android.app.ActivityManagerInternal;
import android.content.Context;
+import android.content.pm.InstallSourceInfo;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
import android.os.LocaleList;
@@ -60,13 +62,16 @@
private static final String DEFAULT_LOCALE_TAGS = "en-XC, ar-XB";
private static final LocaleList DEFAULT_LOCALES =
LocaleList.forLanguageTags(DEFAULT_LOCALE_TAGS);
+ private static final InstallSourceInfo DEFAULT_INSTALL_SOURCE_INFO = new InstallSourceInfo(
+ /* initiatingPackageName = */ null, /* initiatingPackageSigningInfo = */ null,
+ /* originatingPackageName = */ null, /* installingPackageName = */ null);
private LocaleManagerService mLocaleManagerService;
@Mock
private Context mMockContext;
@Mock
- private PackageManagerInternal mMockPackageManager;
+ private PackageManagerInternal mMockPackageManagerInternal;
@Mock
private FakePackageConfigurationUpdater mFakePackageConfigurationUpdater;
@Mock
@@ -75,11 +80,17 @@
private ActivityManagerInternal mMockActivityManager;
@Before
- public void setUp() {
+ public void setUp() throws Exception {
mMockContext = mock(Context.class);
mMockActivityTaskManager = mock(ActivityTaskManagerInternal.class);
mMockActivityManager = mock(ActivityManagerInternal.class);
- mMockPackageManager = mock(PackageManagerInternal.class);
+ mMockPackageManagerInternal = mock(PackageManagerInternal.class);
+
+ // For unit tests, set the default (null) installer info
+ PackageManager mockPackageManager = mock(PackageManager.class);
+ doReturn(DEFAULT_INSTALL_SOURCE_INFO).when(mockPackageManager)
+ .getInstallSourceInfo(anyString());
+ doReturn(mockPackageManager).when(mMockContext).getPackageManager();
mFakePackageConfigurationUpdater = new FakePackageConfigurationUpdater();
doReturn(mFakePackageConfigurationUpdater)
@@ -93,13 +104,13 @@
anyString(), anyString());
mLocaleManagerService = new LocaleManagerService(mMockContext, mMockActivityTaskManager,
- mMockActivityManager, mMockPackageManager);
+ mMockActivityManager, mMockPackageManagerInternal);
}
@Test(expected = SecurityException.class)
public void testSetApplicationLocales_arbitraryAppWithoutPermissions_fails() throws Exception {
doReturn(DEFAULT_UID)
- .when(mMockPackageManager).getPackageUid(anyString(), anyInt(), anyInt());
+ .when(mMockPackageManagerInternal).getPackageUid(anyString(), anyInt(), anyInt());
setUpFailingPermissionCheckFor(Manifest.permission.CHANGE_CONFIGURATION);
try {
@@ -107,7 +118,7 @@
LocaleList.getEmptyLocaleList());
fail("Expected SecurityException");
} finally {
- verify(mMockContext).enforceCallingPermission(
+ verify(mMockContext).enforceCallingOrSelfPermission(
eq(android.Manifest.permission.CHANGE_CONFIGURATION),
anyString());
assertNoLocalesStored(mFakePackageConfigurationUpdater.getStoredLocales());
@@ -142,7 +153,7 @@
@Test
public void testSetApplicationLocales_arbitraryAppWithPermission_succeeds() throws Exception {
doReturn(DEFAULT_UID)
- .when(mMockPackageManager).getPackageUid(anyString(), anyInt(), anyInt());
+ .when(mMockPackageManagerInternal).getPackageUid(anyString(), anyInt(), anyInt());
// if package is not owned by the caller, the calling app should have the following
// permission. We will mock this to succeed to imitate that.
setUpPassingPermissionCheckFor(Manifest.permission.CHANGE_CONFIGURATION);
@@ -157,7 +168,7 @@
@Test
public void testSetApplicationLocales_callerOwnsPackage_succeeds() throws Exception {
doReturn(Binder.getCallingUid())
- .when(mMockPackageManager).getPackageUid(anyString(), anyInt(), anyInt());
+ .when(mMockPackageManagerInternal).getPackageUid(anyString(), anyInt(), anyInt());
mLocaleManagerService.setApplicationLocales(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID,
DEFAULT_LOCALES);
@@ -168,7 +179,7 @@
@Test(expected = IllegalArgumentException.class)
public void testSetApplicationLocales_invalidPackageOrUserId_fails() throws Exception {
doReturn(INVALID_UID)
- .when(mMockPackageManager).getPackageUid(anyString(), anyInt(), anyInt());
+ .when(mMockPackageManagerInternal).getPackageUid(anyString(), anyInt(), anyInt());
try {
mLocaleManagerService.setApplicationLocales(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID,
LocaleList.getEmptyLocaleList());
@@ -180,7 +191,7 @@
@Test(expected = SecurityException.class)
public void testGetApplicationLocales_arbitraryAppWithoutPermission_fails() throws Exception {
- doReturn(DEFAULT_UID).when(mMockPackageManager)
+ doReturn(DEFAULT_UID).when(mMockPackageManagerInternal)
.getPackageUid(anyString(), anyInt(), anyInt());
setUpFailingPermissionCheckFor(Manifest.permission.READ_APP_SPECIFIC_LOCALES);
@@ -188,7 +199,7 @@
mLocaleManagerService.getApplicationLocales(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
fail("Expected SecurityException");
} finally {
- verify(mMockContext).enforceCallingPermission(
+ verify(mMockContext).enforceCallingOrSelfPermission(
eq(android.Manifest.permission.READ_APP_SPECIFIC_LOCALES),
anyString());
}
@@ -198,7 +209,7 @@
public void testGetApplicationLocales_appSpecificConfigAbsent_returnsEmptyList()
throws Exception {
// any valid app calling for its own package or having appropriate permission
- doReturn(DEFAULT_UID).when(mMockPackageManager)
+ doReturn(DEFAULT_UID).when(mMockPackageManagerInternal)
.getPackageUid(anyString(), anyInt(), anyInt());
setUpPassingPermissionCheckFor(Manifest.permission.READ_APP_SPECIFIC_LOCALES);
doReturn(null)
@@ -213,7 +224,7 @@
@Test
public void testGetApplicationLocales_appSpecificLocalesAbsent_returnsEmptyList()
throws Exception {
- doReturn(DEFAULT_UID).when(mMockPackageManager)
+ doReturn(DEFAULT_UID).when(mMockPackageManagerInternal)
.getPackageUid(anyString(), anyInt(), anyInt());
setUpPassingPermissionCheckFor(Manifest.permission.READ_APP_SPECIFIC_LOCALES);
doReturn(new PackageConfig(/* nightMode = */ 0, /* locales = */ null))
@@ -228,7 +239,7 @@
@Test
public void testGetApplicationLocales_callerOwnsAppAndConfigPresent_returnsLocales()
throws Exception {
- doReturn(Binder.getCallingUid()).when(mMockPackageManager)
+ doReturn(Binder.getCallingUid()).when(mMockPackageManagerInternal)
.getPackageUid(anyString(), anyInt(), anyInt());
doReturn(new PackageConfig(/* nightMode = */ 0, DEFAULT_LOCALES))
.when(mMockActivityTaskManager).getApplicationConfig(anyString(), anyInt());
@@ -242,7 +253,7 @@
@Test
public void testGetApplicationLocales_arbitraryCallerWithPermissions_returnsLocales()
throws Exception {
- doReturn(DEFAULT_UID).when(mMockPackageManager)
+ doReturn(DEFAULT_UID).when(mMockPackageManagerInternal)
.getPackageUid(anyString(), anyInt(), anyInt());
setUpPassingPermissionCheckFor(Manifest.permission.READ_APP_SPECIFIC_LOCALES);
doReturn(new PackageConfig(/* nightMode = */ 0, DEFAULT_LOCALES))
@@ -260,10 +271,10 @@
private void setUpFailingPermissionCheckFor(String permission) {
doThrow(new SecurityException("Mock"))
- .when(mMockContext).enforceCallingPermission(eq(permission), any());
+ .when(mMockContext).enforceCallingOrSelfPermission(eq(permission), any());
}
private void setUpPassingPermissionCheckFor(String permission) {
- doNothing().when(mMockContext).enforceCallingPermission(eq(permission), any());
+ doNothing().when(mMockContext).enforceCallingOrSelfPermission(eq(permission), any());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java b/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java
index f8c3bf3..b228c83 100644
--- a/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java
@@ -34,7 +34,7 @@
import android.os.Build;
import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.pkg.PackageUserStateInternalImpl;
+import com.android.server.pm.pkg.PackageUserStateImpl;
import org.junit.After;
import org.junit.Before;
@@ -44,13 +44,13 @@
private boolean mCompatibilityModeEnabled;;
private PackageImpl mMockAndroidPackage;
- private PackageUserStateInternalImpl mMockUserState;
+ private PackageUserStateImpl mMockUserState;
@Before
public void setUp() {
mCompatibilityModeEnabled = ParsingPackageUtils.sCompatibilityModeEnabled;
mMockAndroidPackage = mock(PackageImpl.class);
- mMockUserState = new PackageUserStateInternalImpl();
+ mMockUserState = new PackageUserStateImpl();
mMockUserState.setInstalled(true);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 24377a9..b0f8d40 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -42,8 +42,6 @@
import android.content.pm.PackageManager;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
-import android.content.pm.pkg.PackageUserState;
-import android.content.pm.pkg.PackageUserStateInternal;
import android.os.BaseBundle;
import android.os.PersistableBundle;
import android.os.Process;
@@ -62,6 +60,9 @@
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.permission.LegacyPermissionDataProvider;
+import com.android.server.pm.pkg.PackageUserState;
+import com.android.server.pm.pkg.PackageUserStateInternal;
+import com.android.server.pm.pkg.SuspendParams;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.utils.WatchableTester;
import com.android.server.utils.WatchedArrayMap;
@@ -252,8 +253,7 @@
assertThat(packageUserState1.isSuspended(), is(true));
assertThat(packageUserState1.getSuspendParams().size(), is(1));
assertThat(packageUserState1.getSuspendParams().keyAt(0), is(PACKAGE_NAME_3));
- final PackageUserState.SuspendParams params =
- packageUserState1.getSuspendParams().valueAt(0);
+ final SuspendParams params = packageUserState1.getSuspendParams().valueAt(0);
watcher.verifyNoChangeReported("fetch user state");
assertThat(params, is(notNullValue()));
assertThat(params.appExtras.size(), is(1));
@@ -340,7 +340,7 @@
watcher.verifyNoChangeReported("read package param");
assertThat(readPus1.getSuspendParams().keyAt(0), is("suspendingPackage1"));
- final PackageUserState.SuspendParams params11 = readPus1.getSuspendParams().valueAt(0);
+ final SuspendParams params11 = readPus1.getSuspendParams().valueAt(0);
watcher.verifyNoChangeReported("read package param");
assertThat(params11, is(notNullValue()));
assertThat(params11.dialogInfo, is(dialogInfo1));
@@ -350,7 +350,7 @@
watcher.verifyNoChangeReported("read package param");
assertThat(readPus1.getSuspendParams().keyAt(1), is("suspendingPackage2"));
- final PackageUserState.SuspendParams params12 = readPus1.getSuspendParams().valueAt(1);
+ final SuspendParams params12 = readPus1.getSuspendParams().valueAt(1);
assertThat(params12, is(notNullValue()));
assertThat(params12.dialogInfo, is(dialogInfo2));
assertThat(BaseBundle.kindofEquals(params12.appExtras, appExtras2), is(true));
@@ -363,7 +363,7 @@
assertThat(readPus2.isSuspended(), is(true));
assertThat(readPus2.getSuspendParams().size(), is(1));
assertThat(readPus2.getSuspendParams().keyAt(0), is("suspendingPackage3"));
- final PackageUserState.SuspendParams params21 = readPus2.getSuspendParams().valueAt(0);
+ final SuspendParams params21 = readPus2.getSuspendParams().valueAt(0);
assertThat(params21, is(notNullValue()));
assertThat(params21.dialogInfo, is(nullValue()));
assertThat(BaseBundle.kindofEquals(params21.appExtras, appExtras1), is(true));
@@ -745,7 +745,7 @@
assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("armeabi"));
// signatures object must be different
assertNotSame(testPkgSetting01.getSignatures(), originalSignatures);
- assertThat(testPkgSetting01.getLongVersionCode(), is(UPDATED_VERSION_CODE));
+ assertThat(testPkgSetting01.getVersionCode(), is(UPDATED_VERSION_CODE));
final PackageUserState userState = testPkgSetting01.readUserState(0);
verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/,
false /*notLaunched*/, false /*stopped*/, true /*installed*/);
@@ -783,7 +783,7 @@
assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("x86_64"));
assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("x86"));
- assertThat(testPkgSetting01.getLongVersionCode(), is(INITIAL_VERSION_CODE));
+ assertThat(testPkgSetting01.getVersionCode(), is(INITIAL_VERSION_CODE));
// by default, the package is considered stopped
final PackageUserState userState = testPkgSetting01.readUserState(0);
verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/,
@@ -825,7 +825,7 @@
assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("x86_64"));
assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("x86"));
- assertThat(testPkgSetting01.getLongVersionCode(), is(INITIAL_VERSION_CODE));
+ assertThat(testPkgSetting01.getVersionCode(), is(INITIAL_VERSION_CODE));
final PackageUserState userState = testPkgSetting01.readUserState(0);
verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/,
false /*notLaunched*/, false /*stopped*/, true /*installed*/);
@@ -868,7 +868,7 @@
assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a"));
assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("armeabi"));
assertNotSame(testPkgSetting01.getSignatures(), disabledSignatures);
- assertThat(testPkgSetting01.getLongVersionCode(), is(UPDATED_VERSION_CODE));
+ assertThat(testPkgSetting01.getVersionCode(), is(UPDATED_VERSION_CODE));
final PackageUserState userState = testPkgSetting01.readUserState(0);
verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/,
false /*notLaunched*/, false /*stopped*/, true /*installed*/);
@@ -957,10 +957,10 @@
assertSame(origPkgSetting.getSignatures(), testPkgSetting.getSignatures());
assertThat(origPkgSetting.getSignatures(), is(testPkgSetting.getSignatures()));
assertThat(origPkgSetting.getLastModifiedTime(), is(testPkgSetting.getLastModifiedTime()));
- assertNotSame(origPkgSetting.getUserState(), is(testPkgSetting.getUserState()));
+ assertNotSame(origPkgSetting.getUserStates(), is(testPkgSetting.getUserStates()));
// No equals() method for SparseArray object
// assertThat(origPkgSetting.getUserState(), is(testPkgSetting.getUserState()));
- assertThat(origPkgSetting.getLongVersionCode(), is(testPkgSetting.getLongVersionCode()));
+ assertThat(origPkgSetting.getVersionCode(), is(testPkgSetting.getVersionCode()));
assertSame(origPkgSetting.getVolumeUuid(), testPkgSetting.getVolumeUuid());
assertThat(origPkgSetting.getVolumeUuid(), is(testPkgSetting.getVolumeUuid()));
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index cbd1aa3..fb092d2 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -63,7 +63,6 @@
import android.content.pm.parsing.component.ParsedUsesPermission;
import android.content.pm.parsing.component.ParsedUsesPermissionImpl;
import android.content.pm.permission.CompatibilityPermissionInfo;
-import android.content.pm.pkg.PackageUserState;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -85,6 +84,7 @@
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.pkg.PackageUserState;
import org.junit.Before;
import org.junit.Rule;
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index f1c8dd6..5593763 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -21,7 +21,7 @@
import android.util.SparseArray;
import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.pkg.PackageUserStateInternalImpl;
+import com.android.server.pm.pkg.PackageUserStateImpl;
import java.io.File;
import java.util.Map;
@@ -41,7 +41,7 @@
private int mSharedUserId;
private String mVolumeUuid;
private int mAppId;
- private SparseArray<PackageUserStateInternalImpl> mUserStates = new SparseArray<>();
+ private SparseArray<PackageUserStateImpl> mUserStates = new SparseArray<>();
private AndroidPackage mPkg;
private InstallSource mInstallSource;
private String[] mUsesStaticLibraries;
@@ -139,7 +139,7 @@
public PackageSettingBuilder setInstantAppUserState(int userId, boolean isInstant) {
if (mUserStates.indexOfKey(userId) < 0) {
- mUserStates.put(userId, new PackageUserStateInternalImpl());
+ mUserStates.put(userId, new PackageUserStateImpl());
}
mUserStates.get(userId).setInstantApp(isInstant);
return this;
@@ -147,7 +147,7 @@
public PackageSettingBuilder setInstallState(int userId, boolean installed) {
if (mUserStates.indexOfKey(userId) < 0) {
- mUserStates.put(userId, new PackageUserStateInternalImpl());
+ mUserStates.put(userId, new PackageUserStateImpl());
}
mUserStates.get(userId).setInstalled(installed);
return this;
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index 5376298..c9f3cb2 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -27,7 +27,6 @@
import android.content.pm.PackageManager;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.overlay.OverlayPaths;
-import android.content.pm.pkg.PackageUserState;
import android.os.PersistableBundle;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -36,7 +35,8 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.server.pm.pkg.PackageStateUnserialized;
-import com.android.server.pm.pkg.PackageUserStateInternalImpl;
+import com.android.server.pm.pkg.PackageUserStateImpl;
+import com.android.server.pm.pkg.SuspendParams;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -47,81 +47,81 @@
@Test
public void testPackageUserState01() {
- final PackageUserStateInternalImpl testUserState = new PackageUserStateInternalImpl();
- PackageUserStateInternalImpl oldUserState;
+ final PackageUserStateImpl testUserState = new PackageUserStateImpl();
+ PackageUserStateImpl oldUserState;
- oldUserState = new PackageUserStateInternalImpl();
+ oldUserState = new PackageUserStateImpl();
assertThat(testUserState.equals(null), is(false));
assertThat(testUserState.equals(testUserState), is(true));
assertThat(testUserState.equals(oldUserState), is(true));
- oldUserState = new PackageUserStateInternalImpl();
+ oldUserState = new PackageUserStateImpl();
oldUserState.setCeDataInode(4000L);
assertThat(testUserState.equals(oldUserState), is(false));
- oldUserState = new PackageUserStateInternalImpl();
+ oldUserState = new PackageUserStateImpl();
oldUserState.setEnabledState(COMPONENT_ENABLED_STATE_ENABLED);
assertThat(testUserState.equals(oldUserState), is(false));
- oldUserState = new PackageUserStateInternalImpl();
+ oldUserState = new PackageUserStateImpl();
oldUserState.setHidden(true);
assertThat(testUserState.equals(oldUserState), is(false));
- oldUserState = new PackageUserStateInternalImpl();
+ oldUserState = new PackageUserStateImpl();
oldUserState.setInstalled(false);
assertThat(testUserState.equals(oldUserState), is(false));
- oldUserState = new PackageUserStateInternalImpl();
+ oldUserState = new PackageUserStateImpl();
oldUserState.setNotLaunched(true);
assertThat(testUserState.equals(oldUserState), is(false));
- oldUserState = new PackageUserStateInternalImpl();
+ oldUserState = new PackageUserStateImpl();
oldUserState.setStopped(true);
assertThat(testUserState.equals(oldUserState), is(false));
- oldUserState = new PackageUserStateInternalImpl();
+ oldUserState = new PackageUserStateImpl();
oldUserState.setSuspended(true);
assertThat(testUserState.equals(oldUserState), is(false));
- oldUserState = new PackageUserStateInternalImpl();
+ oldUserState = new PackageUserStateImpl();
oldUserState.setUninstallReason(PackageManager.UNINSTALL_REASON_USER_TYPE);
assertThat(testUserState.equals(oldUserState), is(false));
}
@Test
public void testPackageUserState02() {
- final PackageUserStateInternalImpl testUserState01 = new PackageUserStateInternalImpl();
- PackageUserStateInternalImpl oldUserState;
+ final PackageUserStateImpl testUserState01 = new PackageUserStateImpl();
+ PackageUserStateImpl oldUserState;
- oldUserState = new PackageUserStateInternalImpl();
+ oldUserState = new PackageUserStateImpl();
oldUserState.setLastDisableAppCaller("unit_test");
assertThat(testUserState01.equals(oldUserState), is(false));
- final PackageUserStateInternalImpl testUserState02 = new PackageUserStateInternalImpl();
+ final PackageUserStateImpl testUserState02 = new PackageUserStateImpl();
testUserState02.setLastDisableAppCaller("unit_test");
assertThat(testUserState02.equals(oldUserState), is(true));
- final PackageUserStateInternalImpl testUserState03 = new PackageUserStateInternalImpl();
+ final PackageUserStateImpl testUserState03 = new PackageUserStateImpl();
testUserState03.setLastDisableAppCaller("unit_test_00");
assertThat(testUserState03.equals(oldUserState), is(false));
}
@Test
public void testPackageUserState03() {
- final PackageUserStateInternalImpl oldUserState = new PackageUserStateInternalImpl();
+ final PackageUserStateImpl oldUserState = new PackageUserStateImpl();
// only new user state has array defined; different
- final PackageUserStateInternalImpl testUserState01 = new PackageUserStateInternalImpl();
+ final PackageUserStateImpl testUserState01 = new PackageUserStateImpl();
testUserState01.setDisabledComponents(new ArraySet<>());
assertThat(testUserState01.equals(oldUserState), is(false));
// only old user state has array defined; different
- final PackageUserStateInternalImpl testUserState02 = new PackageUserStateInternalImpl();
+ final PackageUserStateImpl testUserState02 = new PackageUserStateImpl();
oldUserState.setDisabledComponents(new ArraySet<>());
assertThat(testUserState02.equals(oldUserState), is(false));
// both states have array defined; not different
- final PackageUserStateInternalImpl testUserState03 = new PackageUserStateInternalImpl();
+ final PackageUserStateImpl testUserState03 = new PackageUserStateImpl();
testUserState03.setDisabledComponents(new ArraySet<>());
assertThat(testUserState03.equals(oldUserState), is(true));
// fewer elements in old user state; different
@@ -144,20 +144,20 @@
@Test
public void testPackageUserState04() {
- final PackageUserStateInternalImpl oldUserState = new PackageUserStateInternalImpl();
+ final PackageUserStateImpl oldUserState = new PackageUserStateImpl();
// only new user state has array defined; different
- final PackageUserStateInternalImpl testUserState01 = new PackageUserStateInternalImpl();
+ final PackageUserStateImpl testUserState01 = new PackageUserStateImpl();
testUserState01.setEnabledComponents(new ArraySet<>());
assertThat(testUserState01.equals(oldUserState), is(false));
// only old user state has array defined; different
- final PackageUserStateInternalImpl testUserState02 = new PackageUserStateInternalImpl();
+ final PackageUserStateImpl testUserState02 = new PackageUserStateImpl();
oldUserState.setEnabledComponents(new ArraySet<>());
assertThat(testUserState02.equals(oldUserState), is(false));
// both states have array defined; not different
- final PackageUserStateInternalImpl testUserState03 = new PackageUserStateInternalImpl();
+ final PackageUserStateImpl testUserState03 = new PackageUserStateImpl();
testUserState03.setEnabledComponents(new ArraySet<>());
assertThat(testUserState03.equals(oldUserState), is(true));
// fewer elements in old user state; different
@@ -178,10 +178,9 @@
assertThat(testUserState03.equals(oldUserState), is(false));
}
- private static PackageUserState.SuspendParams createSuspendParams(SuspendDialogInfo dialogInfo,
+ private static SuspendParams createSuspendParams(SuspendDialogInfo dialogInfo,
PersistableBundle appExtras, PersistableBundle launcherExtras) {
- return PackageUserState.SuspendParams.getInstanceOrNull(
- dialogInfo, appExtras, launcherExtras);
+ return SuspendParams.getInstanceOrNull(dialogInfo, appExtras, launcherExtras);
}
private static PersistableBundle createPersistableBundle(String lKey, long lValue, String sKey,
@@ -221,20 +220,20 @@
.setMessage("dialogMessage2")
.build();
- final ArrayMap<String, PackageUserState.SuspendParams> paramsMap1 = new ArrayMap<>();
+ final ArrayMap<String, SuspendParams> paramsMap1 = new ArrayMap<>();
paramsMap1.put(suspendingPackage1, createSuspendParams(dialogInfo1, appExtras1,
launcherExtras1));
- final ArrayMap<String, PackageUserState.SuspendParams> paramsMap2 = new ArrayMap<>();
+ final ArrayMap<String, SuspendParams> paramsMap2 = new ArrayMap<>();
paramsMap2.put(suspendingPackage2, createSuspendParams(dialogInfo2,
appExtras2, launcherExtras2));
- final PackageUserStateInternalImpl testUserState1 = new PackageUserStateInternalImpl();
+ final PackageUserStateImpl testUserState1 = new PackageUserStateImpl();
testUserState1.setSuspended(true);
testUserState1.setSuspendParams(paramsMap1);
- PackageUserStateInternalImpl testUserState2 =
- new PackageUserStateInternalImpl(testUserState1);
+ PackageUserStateImpl testUserState2 =
+ new PackageUserStateImpl(testUserState1);
assertThat(testUserState1.equals(testUserState2), is(true));
testUserState2.setSuspendParams(paramsMap2);
// Should not be equal since suspendParams maps are different
@@ -243,17 +242,17 @@
@Test
public void testPackageUserState06() {
- final PackageUserStateInternalImpl userState1 = new PackageUserStateInternalImpl();
+ final PackageUserStateImpl userState1 = new PackageUserStateImpl();
assertThat(userState1.getDistractionFlags(), is(PackageManager.RESTRICTION_NONE));
userState1.setDistractionFlags(PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS);
- final PackageUserStateInternalImpl copyOfUserState1 =
- new PackageUserStateInternalImpl(userState1);
+ final PackageUserStateImpl copyOfUserState1 =
+ new PackageUserStateImpl(userState1);
assertThat(userState1.getDistractionFlags(), is(copyOfUserState1.getDistractionFlags()));
assertThat(userState1.equals(copyOfUserState1), is(true));
- final PackageUserStateInternalImpl userState2 =
- new PackageUserStateInternalImpl(userState1);
+ final PackageUserStateImpl userState2 =
+ new PackageUserStateImpl(userState1);
userState2.setDistractionFlags(PackageManager.RESTRICTION_HIDE_NOTIFICATIONS);
assertThat(userState1.equals(userState2), is(false));
}
@@ -277,8 +276,8 @@
.setMessage("dialogMessage2")
.build();
- final PackageUserState.SuspendParams params1;
- PackageUserState.SuspendParams params2;
+ final SuspendParams params1;
+ SuspendParams params2;
params1 = createSuspendParams(dialogInfo1, appExtras1, launcherExtras1);
params2 = createSuspendParams(dialogInfo1, appExtras1, launcherExtras1);
// Everything is same
@@ -354,7 +353,7 @@
@Test
public void testOverlayPaths() {
- final PackageUserStateInternalImpl testState = new PackageUserStateInternalImpl();
+ final PackageUserStateImpl testState = new PackageUserStateImpl();
assertFalse(testState.setOverlayPaths(null));
assertFalse(testState.setOverlayPaths(new OverlayPaths.Builder().build()));
@@ -368,7 +367,7 @@
}
@Test
public void testSharedLibOverlayPaths() {
- final PackageUserStateInternalImpl testState = new PackageUserStateInternalImpl();
+ final PackageUserStateImpl testState = new PackageUserStateImpl();
final String LIB_ONE = "lib1";
final String LIB_TW0 = "lib2";
assertFalse(testState.setSharedLibraryOverlayPaths(LIB_ONE, null));
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 6188bc4..82ee330 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -559,7 +559,7 @@
assertThat(pkgSetting.usesStaticLibrariesVersions, is(new long[]{234L, 456L}));
assertThat(pkgSetting.getPkg(), is(scanResult.mRequest.mParsedPackage));
assertThat(pkgSetting.getPath(), is(new File(createCodePath(packageName))));
- assertThat(pkgSetting.getLongVersionCode(),
+ assertThat(pkgSetting.getVersionCode(),
is(PackageInfo.composeLongVersionCode(1, 2345)));
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
index f009488..df8786f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
@@ -35,10 +35,10 @@
import android.util.SparseArray
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.pm.PackageManagerService
-import com.android.server.pm.PackageSetting
import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.pm.pkg.PackageStateInternal
import com.android.server.pm.pkg.PackageStateUnserialized
-import com.android.server.pm.pkg.PackageUserStateInternalImpl
+import com.android.server.pm.pkg.PackageUserStateImpl
import com.android.server.testutils.mockThrowOnUnmocked
import com.android.server.testutils.whenever
import org.junit.BeforeClass
@@ -85,7 +85,8 @@
}
.distinct()
- private val dummyUserState = PackageUserStateInternalImpl()
+ private val dummyUserState =
+ PackageUserStateImpl()
val oldPackages = mutableListOf<PackageParser.Package>()
@@ -167,15 +168,16 @@
dummyUserState, 0, mockPkgSetting(pkg))
}
- private fun mockPkgSetting(aPkg: AndroidPackage) = mockThrowOnUnmocked<PackageSetting> {
- this.pkg = aPkg
- this.appId = aPkg.uid
- whenever(pkgState) { PackageStateUnserialized() }
- whenever(readUserState(anyInt())) { dummyUserState }
- whenever(categoryOverride) { ApplicationInfo.CATEGORY_UNDEFINED }
- whenever(primaryCpuAbi) { null }
- whenever(secondaryCpuAbi) { null }
- }
+ private fun mockPkgSetting(aPkg: AndroidPackage) =
+ mockThrowOnUnmocked<PackageStateInternal> {
+ whenever(pkg) { aPkg }
+ whenever(appId) { aPkg.uid }
+ whenever(transientState) { PackageStateUnserialized() }
+ whenever(getUserStateOrDefault(anyInt())) { dummyUserState }
+ whenever(categoryOverride) { ApplicationInfo.CATEGORY_UNDEFINED }
+ whenever(primaryCpuAbi) { null }
+ whenever(secondaryCpuAbi) { null }
+ }
}
// The following methods dump an exact set of fields from the object to compare, because
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index 8e2c1f0..761cea7 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -160,6 +160,64 @@
}
@Test
+ public void create_stateWithCancelStickyRequestFlag() {
+ String configString = "<device-state-config>\n"
+ + " <device-state>\n"
+ + " <identifier>1</identifier>\n"
+ + " <flags>\n"
+ + " <flag>FLAG_CANCEL_STICKY_REQUESTS</flag>\n"
+ + " </flags>\n"
+ + " <conditions/>\n"
+ + " </device-state>\n"
+ + " <device-state>\n"
+ + " <identifier>2</identifier>\n"
+ + " <conditions/>\n"
+ + " </device-state>\n"
+ + "</device-state-config>\n";
+ DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
+ DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(mContext,
+ config);
+
+ DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
+ provider.setListener(listener);
+
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
+ final DeviceState[] expectedStates = new DeviceState[]{
+ new DeviceState(1, "", DeviceState.FLAG_CANCEL_STICKY_REQUESTS),
+ new DeviceState(2, "", 0 /* flags */) };
+ assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
+ }
+
+ @Test
+ public void create_stateWithInvalidFlag() {
+ String configString = "<device-state-config>\n"
+ + " <device-state>\n"
+ + " <identifier>1</identifier>\n"
+ + " <flags>\n"
+ + " <flag>INVALID_FLAG</flag>\n"
+ + " </flags>\n"
+ + " <conditions/>\n"
+ + " </device-state>\n"
+ + " <device-state>\n"
+ + " <identifier>2</identifier>\n"
+ + " <conditions/>\n"
+ + " </device-state>\n"
+ + "</device-state-config>\n";
+ DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
+ DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(mContext,
+ config);
+
+ DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
+ provider.setListener(listener);
+
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
+ final DeviceState[] expectedStates = new DeviceState[]{
+ new DeviceState(1, "", 0 /* flags */),
+ new DeviceState(2, "", 0 /* flags */) };
+ assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
+ }
+
+ @Test
public void create_lidSwitch() {
String configString = "<device-state-config>\n"
+ " <device-state>\n"
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index b2dacab..75bd2cc 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -67,9 +67,10 @@
private static final UsageStatsDatabase.StatCombiner<IntervalStats> mIntervalStatsVerifier =
new UsageStatsDatabase.StatCombiner<IntervalStats>() {
@Override
- public void combine(IntervalStats stats, boolean mutable,
+ public boolean combine(IntervalStats stats, boolean mutable,
List<IntervalStats> accResult) {
accResult.add(stats);
+ return true;
}
};
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 987236c..c337ccd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -379,6 +379,7 @@
/** Test that restore correctly parses the user_set attribute. */
@Test
public void testReadXml_restoresUserSet() throws Exception {
+ mVersionString = "4";
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
ManagedServices service =
new TestManagedServices(
@@ -1513,7 +1514,8 @@
for (int userId : mExpectedPrimary.get(service.mApprovalLevel).keySet()) {
String pkgOrCmp = mExpectedPrimary.get(service.mApprovalLevel).get(userId);
xml.append(getXmlEntry(
- pkgOrCmp, userId, true, !(pkgOrCmp.startsWith("non.user.set.package"))));
+ pkgOrCmp, userId, true,
+ !(pkgOrCmp.startsWith("non.user.set.package"))));
}
for (int userId : mExpectedSecondary.get(service.mApprovalLevel).keySet()) {
xml.append(getXmlEntry(
@@ -1541,7 +1543,9 @@
private TypedXmlPullParser getParserWithEntries(ManagedServices service, String... xmlEntries)
throws Exception {
final StringBuffer xml = new StringBuffer();
- xml.append("<" + service.getConfig().xmlTag + ">\n");
+ xml.append("<" + service.getConfig().xmlTag
+ + (mVersionString != null ? " version=\"" + mVersionString + "\" " : "")
+ + ">\n");
for (String xmlEntry : xmlEntries) {
xml.append(xmlEntry);
}
@@ -1726,12 +1730,19 @@
}
private String getXmlEntry(String approved, int userId, boolean isPrimary, boolean userSet) {
+ String userSetString = "";
+ if (mVersionString.equals("4")) {
+ userSetString =
+ ManagedServices.ATT_USER_CHANGED + "=\"" + String.valueOf(userSet) + "\" ";
+ } else if (mVersionString.equals("3")) {
+ userSetString =
+ ManagedServices.ATT_USER_SET + "=\"" + (userSet ? approved : "") + "\" ";
+ }
return "<" + ManagedServices.TAG_MANAGED_SERVICES + " "
+ ManagedServices.ATT_USER_ID + "=\"" + userId +"\" "
+ ManagedServices.ATT_IS_PRIMARY + "=\"" + isPrimary +"\" "
+ ManagedServices.ATT_APPROVED_LIST + "=\"" + approved +"\" "
- + ManagedServices.ATT_USER_SET + "=\"" + (userSet ? approved : "") + "\" "
- + "/>\n";
+ + userSetString + "/>\n";
}
class TestManagedServices extends ManagedServices {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 054a401..4b93e35 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
@@ -124,6 +125,7 @@
profileIds.add(12);
when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
when(mNm.isNASMigrationDone(anyInt())).thenReturn(true);
+ when(mNm.canUseManagedServices(any(), anyInt(), any())).thenReturn(true);
}
@Test
@@ -178,6 +180,92 @@
}
@Test
+ public void testReadXml_upgradeUserSet_preS_VersionThree() throws Exception {
+ String xml = "<enabled_assistants version=\"3\" defaults=\"b/b\">"
+ + "<service_listing approved=\"\" user=\"0\" primary=\"true\""
+ + "user_set=\"true\"/>"
+ + "</enabled_assistants>";
+
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+ TriPredicate<String, Integer, String> allowedManagedServicePackages =
+ mNm::canUseManagedServices;
+
+ parser.nextTag();
+ mAssistants.readXml(parser, allowedManagedServicePackages, false, UserHandle.USER_ALL);
+
+ verify(mAssistants, times(0)).upgradeUserSet();
+ assertTrue(isUserSetServicesEmpty(mAssistants, 0));
+ assertTrue(mAssistants.mIsUserChanged.get(0));
+ }
+
+ @Test
+ public void testReadXml_upgradeUserSet_preS_VersionOne() throws Exception {
+ String xml = "<enabled_assistants version=\"1\" defaults=\"b/b\">"
+ + "<service_listing approved=\"\" user=\"0\" primary=\"true\""
+ + "user_set=\"true\"/>"
+ + "</enabled_assistants>";
+
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+ TriPredicate<String, Integer, String> allowedManagedServicePackages =
+ mNm::canUseManagedServices;
+
+ parser.nextTag();
+ mAssistants.readXml(parser, allowedManagedServicePackages, false, UserHandle.USER_ALL);
+
+ verify(mAssistants, times(0)).upgradeUserSet();
+ assertTrue(isUserSetServicesEmpty(mAssistants, 0));
+ assertTrue(mAssistants.mIsUserChanged.get(0));
+ }
+
+ @Test
+ public void testReadXml_upgradeUserSet_preS_noUserSet() throws Exception {
+ String xml = "<enabled_assistants version=\"3\" defaults=\"b/b\">"
+ + "<service_listing approved=\"b/b\" user=\"0\" primary=\"true\"/>"
+ + "</enabled_assistants>";
+
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+ TriPredicate<String, Integer, String> allowedManagedServicePackages =
+ mNm::canUseManagedServices;
+
+ parser.nextTag();
+ mAssistants.readXml(parser, allowedManagedServicePackages, false, UserHandle.USER_ALL);
+
+ verify(mAssistants, times(1)).upgradeUserSet();
+ assertTrue(isUserSetServicesEmpty(mAssistants, 0));
+ assertFalse(mAssistants.mIsUserChanged.get(0));
+ }
+
+ @Test
+ public void testReadXml_upgradeUserSet_preS_noUserSet_diffDefault() throws Exception {
+ String xml = "<enabled_assistants version=\"3\" defaults=\"a/a\">"
+ + "<service_listing approved=\"b/b\" user=\"0\" primary=\"true\"/>"
+ + "</enabled_assistants>";
+
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml.toString().getBytes())), null);
+ TriPredicate<String, Integer, String> allowedManagedServicePackages =
+ mNm::canUseManagedServices;
+
+ parser.nextTag();
+ mAssistants.readXml(parser, allowedManagedServicePackages, false, UserHandle.USER_ALL);
+
+ verify(mAssistants, times(1)).upgradeUserSet();
+ assertTrue(isUserSetServicesEmpty(mAssistants, 0));
+ assertFalse(mAssistants.mIsUserChanged.get(0));
+ assertEquals(new ArraySet<>(Arrays.asList(new ComponentName("a", "a"))),
+ mAssistants.getDefaultComponents());
+ assertEquals(Arrays.asList(new ComponentName("b", "b")),
+ mAssistants.getAllowedComponents(0));
+ }
+
+ @Test
public void testReadXml_multiApproved() throws Exception {
String xml = "<enabled_assistants version=\"4\" defaults=\"b/b\">"
+ "<service_listing approved=\"a/a:b/b\" user=\"0\" primary=\"true\""
@@ -210,7 +298,7 @@
verify(mNm, never()).setDefaultAssistantForUser(anyInt());
verify(mAssistants, times(1)).addApprovedList(
- new ComponentName("b", "b").flattenToString(), 10, true, null);
+ new ComponentName("b", "b").flattenToString(), 10, true, "");
}
@Test
@@ -380,4 +468,11 @@
verify(mNm, times(1)).setDefaultAssistantForUser(eq(mZero.id));
assertEquals(new ArraySet<>(), mAssistants.getDefaultComponents());
}
+
+ // Helper function to hold mApproved lock, avoid GuardedBy lint errors
+ private boolean isUserSetServicesEmpty(NotificationAssistants assistant, int userId) {
+ synchronized (assistant.mApproved) {
+ return assistant.mUserSetServices.get(userId).isEmpty();
+ }
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
index 50ebffc31..d4420bd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
@@ -24,8 +24,9 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -41,7 +42,6 @@
import android.service.notification.NotificationListenerService;
import android.util.ArraySet;
import android.util.Pair;
-import android.util.Slog;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
@@ -355,4 +355,19 @@
.getDisallowedPackages()).isEmpty();
}
+ @Test
+ public void testHasAllowedListener() {
+ final int uid1 = 1, uid2 = 2;
+ // enable mCn1 but not mCn2 for uid1
+ mListeners.addApprovedList(mCn1.flattenToString(), uid1, true);
+
+ // verify that:
+ // the package for mCn1 has an allowed listener for uid1 and not uid2
+ assertTrue(mListeners.hasAllowedListener(mCn1.getPackageName(), uid1));
+ assertFalse(mListeners.hasAllowedListener(mCn1.getPackageName(), uid2));
+
+ // and that mCn2 has no allowed listeners for either user id
+ assertFalse(mListeners.hasAllowedListener(mCn2.getPackageName(), uid1));
+ assertFalse(mListeners.hasAllowedListener(mCn2.getPackageName(), uid2));
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 5694e59..def38a6 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -8347,6 +8347,36 @@
}
@Test
+ public void testPostNotification_channelLockedFixed() throws Exception {
+ mTestNotificationChannel.setImportanceLockedByOEM(true);
+
+ NotificationRecord temp = generateNotificationRecord(mTestNotificationChannel);
+ mBinderService.enqueueNotificationWithTag(PKG, PKG,
+ "testPostNotification_appPermissionFixed", 0,
+ temp.getNotification(), 0);
+ waitForIdle();
+ assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(PKG);
+ assertThat(mService.getNotificationRecord(notifs[0].getKey()).isImportanceFixed()).isTrue();
+
+ mBinderService.cancelAllNotifications(PKG, 0);
+ waitForIdle();
+
+ mTestNotificationChannel.setImportanceLockedByOEM(false);
+ mTestNotificationChannel.setImportanceLockedByCriticalDeviceFunction(true);
+
+ temp = generateNotificationRecord(mTestNotificationChannel);
+ mBinderService.enqueueNotificationWithTag(PKG, PKG,
+ "testPostNotification_appPermissionFixed", 0,
+ temp.getNotification(), 0);
+ waitForIdle();
+ assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
+ notifs = mBinderService.getActiveNotifications(PKG);
+ assertThat(mService.getNotificationRecord(notifs[0].getKey()).isImportanceFixed()).isTrue();
+ }
+
+ @Test
public void testGetNotificationChannelsBypassingDnd_blocked() throws RemoteException {
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.getImportance(PKG, mUid)).thenReturn(IMPORTANCE_NONE);
@@ -8356,4 +8386,79 @@
verify(mPermissionHelper, never()).hasPermission(anyInt());
verify(mPreferencesHelper, never()).getNotificationChannelsBypassingDnd(PKG, mUid);
}
+
+ @Test
+ public void testMatchesCallFilter_noPermissionShouldThrow() throws Exception {
+ // set the testable NMS to not system uid
+ mService.isSystemUid = false;
+
+ // make sure a caller without listener access or read_contacts permission can't call
+ // matchesCallFilter.
+ when(mListeners.hasAllowedListener(anyString(), anyInt())).thenReturn(false);
+ doThrow(new SecurityException()).when(mContext).enforceCallingPermission(
+ eq("android.permission.READ_CONTACTS"), anyString());
+
+ try {
+ // shouldn't matter what we're passing in, if we get past this line fail immediately
+ ((INotificationManager) mService.mService).matchesCallFilter(null);
+ fail("call to matchesCallFilter with no permissions should fail");
+ } catch (SecurityException e) {
+ // pass
+ }
+ }
+
+ @Test
+ public void testMatchesCallFilter_hasSystemPermission() throws Exception {
+ // set the testable NMS to system uid
+ mService.isSystemUid = true;
+
+ // make sure caller doesn't have listener access or read_contacts permission
+ when(mListeners.hasAllowedListener(anyString(), anyInt())).thenReturn(false);
+ doThrow(new SecurityException()).when(mContext).enforceCallingPermission(
+ eq("android.permission.READ_CONTACTS"), anyString());
+
+ try {
+ ((INotificationManager) mService.mService).matchesCallFilter(null);
+ // pass, but check that we actually checked for system permissions
+ assertTrue(mService.countSystemChecks > 0);
+ } catch (SecurityException e) {
+ fail("call to matchesCallFilter with just system permissions should work");
+ }
+ }
+
+ @Test
+ public void testMatchesCallFilter_hasListenerPermission() throws Exception {
+ mService.isSystemUid = false;
+
+ // make sure a caller with only listener access and not read_contacts permission can call
+ // matchesCallFilter.
+ when(mListeners.hasAllowedListener(anyString(), anyInt())).thenReturn(true);
+ doThrow(new SecurityException()).when(mContext).enforceCallingPermission(
+ eq("android.permission.READ_CONTACTS"), anyString());
+
+ try {
+ ((INotificationManager) mService.mService).matchesCallFilter(null);
+ // pass, this is not a functionality test
+ } catch (SecurityException e) {
+ fail("call to matchesCallFilter with listener permissions should work");
+ }
+ }
+
+ @Test
+ public void testMatchesCallFilter_hasContactsPermission() throws Exception {
+ mService.isSystemUid = false;
+
+ // make sure a caller with only read_contacts permission and not listener access can call
+ // matchesCallFilter.
+ when(mListeners.hasAllowedListener(anyString(), anyInt())).thenReturn(false);
+ doNothing().when(mContext).enforceCallingPermission(
+ eq("android.permission.READ_CONTACTS"), anyString());
+
+ try {
+ ((INotificationManager) mService.mService).matchesCallFilter(null);
+ // pass, this is not a functionality test
+ } catch (SecurityException e) {
+ fail("call to matchesCallFilter with listener permissions should work");
+ }
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
index 016b579..423ba94 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
@@ -20,6 +20,7 @@
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.content.pm.PackageManager.FEATURE_WATCH;
@@ -272,6 +273,8 @@
MockitoAnnotations.initMocks(this);
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+
DeviceIdleInternal deviceIdleInternal = mock(DeviceIdleInternal.class);
when(deviceIdleInternal.getNotificationAllowlistDuration()).thenReturn(3000L);
@@ -678,4 +681,34 @@
verify(mPermissionHelper).setNotificationPermission(
PKG_N_MR1, ActivityManager.getCurrentUser(), false, true);
}
+
+ @Test
+ public void testPostNotification_appPermissionFixed() throws Exception {
+ when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
+ when(mPermissionHelper.isPermissionFixed(PKG, 0)).thenReturn(true);
+
+ NotificationRecord temp = generateNotificationRecord(mTestNotificationChannel);
+ mBinderService.enqueueNotificationWithTag(PKG, PKG,
+ "testPostNotification_appPermissionFixed", 0,
+ temp.getNotification(), 0);
+ waitForIdle();
+ assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(PKG);
+ assertThat(mService.getNotificationRecord(notifs[0].getKey()).isImportanceFixed()).isTrue();
+ }
+
+ @Test
+ public void testSummaryNotification_appPermissionFixed() {
+ NotificationRecord temp = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(temp);
+
+ when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
+ when(mPermissionHelper.isPermissionFixed(PKG, temp.getUserId())).thenReturn(true);
+
+ NotificationRecord r = mService.createAutoGroupSummary(
+ temp.getUserId(), temp.getSbn().getPackageName(), temp.getKey());
+
+ assertThat(r.isImportanceFixed()).isTrue();
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 49b6386..7542033 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -1017,38 +1017,14 @@
}
@Test
- public void testIgnoreImportanceAdjustmentsForOemLockedChannels() {
+ public void testIgnoreImportanceAdjustmentsForFixedRecords() {
NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
- channel.setImportanceLockedByOEM(true);
StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /* defaultLights */, groupId /* group */);
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
-
- assertEquals(IMPORTANCE_DEFAULT, record.getImportance());
-
- Bundle bundle = new Bundle();
- bundle.putInt(KEY_IMPORTANCE, IMPORTANCE_LOW);
- Adjustment adjustment = new Adjustment(
- PKG_O, record.getKey(), bundle, "", record.getUserId());
-
- record.addAdjustment(adjustment);
- record.applyAdjustments();
- record.calculateImportance();
-
- assertEquals(IMPORTANCE_DEFAULT, record.getImportance());
- }
-
- @Test
- public void testIgnoreImportanceAdjustmentsForDefaultAppLockedChannels() {
- NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
- channel.setImportanceLockedByCriticalDeviceFunction(true);
-
- StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
- true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
- false /* lights */, false /* defaultLights */, groupId /* group */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ record.setImportanceFixed(true);
assertEquals(IMPORTANCE_DEFAULT, record.getImportance());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
index 55b12dd..4cdae88 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
@@ -15,6 +15,8 @@
*/
package com.android.server.notification;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.permission.PermissionManager.PERMISSION_GRANTED;
@@ -39,6 +41,8 @@
import android.content.pm.ParceledListSlice;
import android.permission.IPermissionManager;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Pair;
+import android.util.Slog;
import androidx.test.runner.AndroidJUnit4;
@@ -47,6 +51,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import org.junit.Before;
@@ -63,6 +68,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -104,6 +110,9 @@
args.add(false);
} else if (type.getTypeName().equals("int")) {
args.add(1);
+ } else if (type.getTypeName().equals(
+ "com.android.server.notification.PermissionHelper$PackagePermission")) {
+ args.add(null);
}
}
try {
@@ -155,15 +164,16 @@
aiSecond.uid = 2;
second.applicationInfo = aiSecond;
- Map<Integer, String> expected = ImmutableMap.of(1, "first", 2, "second");
+ Set<Pair<Integer, String>> expected =
+ ImmutableSet.of(new Pair(1, "first"), new Pair(2, "second"));
ParceledListSlice<PackageInfo> infos = new ParceledListSlice<>(
ImmutableList.of(notThis, none, first, second));
when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS), anyInt())).thenReturn(infos);
- Map<Integer, String> actual = mPermissionHelper.getAppsRequestingPermission(0);
+ Set<Pair<Integer, String>> actual = mPermissionHelper.getAppsRequestingPermission(0);
- assertThat(actual).containsExactlyEntriesIn(expected);
+ assertThat(actual).containsExactlyElementsIn(expected);
}
@Test
@@ -199,10 +209,11 @@
eq(new String[] {Manifest.permission.POST_NOTIFICATIONS}), anyInt(), eq(userId)))
.thenReturn(infos);
- Map<Integer, String> expected = ImmutableMap.of(1, "first", 2, "second");
+ Set<Pair<Integer, String>> expected =
+ ImmutableSet.of(new Pair(1, "first"), new Pair(2, "second"));
assertThat(mPermissionHelper.getAppsGrantedPermission(userId))
- .containsExactlyEntriesIn(expected);
+ .containsExactlyElementsIn(expected);
}
@Test
@@ -244,4 +255,70 @@
verify(mPermManager, never()).updatePermissionFlags(
anyString(), anyString(), anyInt(), anyInt(), anyBoolean(), anyInt());
}
+
+ @Test
+ public void testIsPermissionFixed() throws Exception {
+ when(mPermManager.getPermissionFlags(anyString(),
+ eq(Manifest.permission.POST_NOTIFICATIONS),
+ anyInt())).thenReturn(FLAG_PERMISSION_USER_SET);
+
+ assertThat(mPermissionHelper.isPermissionFixed("pkg", 0)).isFalse();
+
+ when(mPermManager.getPermissionFlags(anyString(),
+ eq(Manifest.permission.POST_NOTIFICATIONS),
+ anyInt())).thenReturn(FLAG_PERMISSION_USER_SET|FLAG_PERMISSION_POLICY_FIXED);
+
+ assertThat(mPermissionHelper.isPermissionFixed("pkg", 0)).isTrue();
+
+ when(mPermManager.getPermissionFlags(anyString(),
+ eq(Manifest.permission.POST_NOTIFICATIONS),
+ anyInt())).thenReturn(FLAG_PERMISSION_SYSTEM_FIXED);
+
+ assertThat(mPermissionHelper.isPermissionFixed("pkg", 0)).isTrue();
+ }
+
+ @Test
+ public void testGetNotificationPermissionValues() throws Exception {
+ int userId = 1;
+ PackageInfo first = new PackageInfo();
+ first.packageName = "first";
+ first.requestedPermissions =
+ new String[] {"something else", Manifest.permission.POST_NOTIFICATIONS};
+ ApplicationInfo aiFirst = new ApplicationInfo();
+ aiFirst.uid = 1;
+ first.applicationInfo = aiFirst;
+
+ PackageInfo second = new PackageInfo();
+ second.packageName = "second";
+ second.requestedPermissions = new String[] {Manifest.permission.POST_NOTIFICATIONS};
+ ApplicationInfo aiSecond = new ApplicationInfo();
+ aiSecond.uid = 2;
+ second.applicationInfo = aiSecond;
+
+ PackageInfo third = new PackageInfo();
+ third.packageName = "third";
+ third.requestedPermissions = new String[] {Manifest.permission.POST_NOTIFICATIONS};
+ ApplicationInfo aiThird = new ApplicationInfo();
+ aiThird.uid = 3;
+ third.applicationInfo = aiThird;
+
+ ParceledListSlice<PackageInfo> infos = new ParceledListSlice<>(
+ ImmutableList.of(first, second));
+ when(mPackageManager.getPackagesHoldingPermissions(
+ eq(new String[] {Manifest.permission.POST_NOTIFICATIONS}), anyInt(), eq(userId)))
+ .thenReturn(infos);
+ ParceledListSlice<PackageInfo> requesting = new ParceledListSlice<>(
+ ImmutableList.of(first, second, third));
+ when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS), anyInt()))
+ .thenReturn(requesting);
+
+ Map<Pair<Integer, String>, Boolean> expected = ImmutableMap.of(new Pair(1, "first"), true,
+ new Pair(2, "second"), true,
+ new Pair(3, "third"), false);
+
+ Map<Pair<Integer, String>, Boolean> actual =
+ mPermissionHelper.getNotificationPermissionValues(userId);
+
+ assertThat(actual).containsExactlyEntriesIn(expected);
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index d90f91a..b89a94a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -29,6 +29,8 @@
import static android.app.NotificationManager.IMPORTANCE_MAX;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.media.AudioAttributes.CONTENT_TYPE_SONIFICATION;
+import static android.media.AudioAttributes.USAGE_NOTIFICATION;
import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
@@ -102,6 +104,7 @@
import android.util.ArraySet;
import android.util.IntArray;
import android.util.Pair;
+import android.util.Slog;
import android.util.StatsEvent;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
@@ -111,6 +114,9 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
+import com.android.server.notification.PermissionHelper.PackagePermission;
+
+import com.google.common.collect.ImmutableMap;
import org.json.JSONArray;
import org.json.JSONObject;
@@ -582,6 +588,394 @@
}
@Test
+ public void testReadXml_oldXml_migrates() throws Exception {
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+ mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
+
+ String xml = "<ranking version=\"2\">\n"
+ + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
+ + "<channel id=\"idn\" name=\"name\" importance=\"2\" />\n"
+ + "<channel id=\"miscellaneous\" name=\"Uncategorized\" />\n"
+ + "</package>\n"
+ + "<package name=\"" + PKG_O + "\" importance=\"0\">\n"
+ + "<channel id=\"ido\" name=\"name2\" importance=\"2\" show_badge=\"true\"/>\n"
+ + "</package>\n"
+ + "<package name=\"" + PKG_P + "\" importance=\"2\">\n"
+ + "<channel id=\"idp\" name=\"name3\" importance=\"4\" locked=\"2\" />\n"
+ + "</package>\n"
+ + "</ranking>\n";
+ NotificationChannel idn = new NotificationChannel("idn", "name", IMPORTANCE_LOW);
+ idn.setSound(null, new AudioAttributes.Builder()
+ .setUsage(USAGE_NOTIFICATION)
+ .setContentType(CONTENT_TYPE_SONIFICATION)
+ .setFlags(0)
+ .build());
+ idn.setShowBadge(false);
+ NotificationChannel ido = new NotificationChannel("ido", "name2", IMPORTANCE_LOW);
+ ido.setShowBadge(true);
+ ido.setSound(null, new AudioAttributes.Builder()
+ .setUsage(USAGE_NOTIFICATION)
+ .setContentType(CONTENT_TYPE_SONIFICATION)
+ .setFlags(0)
+ .build());
+ NotificationChannel idp = new NotificationChannel("idp", "name3", IMPORTANCE_HIGH);
+ idp.lockFields(2);
+ idp.setSound(null, new AudioAttributes.Builder()
+ .setUsage(USAGE_NOTIFICATION)
+ .setContentType(CONTENT_TYPE_SONIFICATION)
+ .setFlags(0)
+ .build());
+
+ // Notifications enabled, not user set
+ PackagePermission nMr1Expected = new PackagePermission(PKG_N_MR1, 0, true, false);
+ // Notifications not enabled, so user set
+ PackagePermission oExpected = new PackagePermission(PKG_O, 0, false, true);
+ // Notifications enabled, user set b/c channel modified
+ PackagePermission pExpected = new PackagePermission(PKG_P, 0, true, true);
+
+ loadByteArrayXml(xml.getBytes(), true, UserHandle.USER_SYSTEM);
+
+ assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+
+ assertEquals(idn, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, idn.getId(), false));
+ compareChannels(ido, mHelper.getNotificationChannel(PKG_O, UID_O, ido.getId(), false));
+ compareChannels(idp, mHelper.getNotificationChannel(PKG_P, UID_P, idp.getId(), false));
+
+ verify(mPermissionHelper).setNotificationPermission(nMr1Expected);
+ verify(mPermissionHelper).setNotificationPermission(oExpected);
+ verify(mPermissionHelper).setNotificationPermission(pExpected);
+ }
+
+ @Test
+ public void testReadXml_newXml_noMigration() throws Exception {
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+ mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
+
+ String xml = "<ranking version=\"3\">\n"
+ + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
+ + "<channel id=\"idn\" name=\"name\" importance=\"2\"/>\n"
+ + "<channel id=\"miscellaneous\" name=\"Uncategorized\" />\n"
+ + "</package>\n"
+ + "<package name=\"" + PKG_O + "\" >\n"
+ + "<channel id=\"ido\" name=\"name2\" importance=\"2\" show_badge=\"true\"/>\n"
+ + "</package>\n"
+ + "<package name=\"" + PKG_P + "\" >\n"
+ + "<channel id=\"idp\" name=\"name3\" importance=\"4\" locked=\"2\" />\n"
+ + "</package>\n"
+ + "</ranking>\n";
+ NotificationChannel idn = new NotificationChannel("idn", "name", IMPORTANCE_LOW);
+ idn.setSound(null, new AudioAttributes.Builder()
+ .setUsage(USAGE_NOTIFICATION)
+ .setContentType(CONTENT_TYPE_SONIFICATION)
+ .setFlags(0)
+ .build());
+ idn.setShowBadge(false);
+ NotificationChannel ido = new NotificationChannel("ido", "name2", IMPORTANCE_LOW);
+ ido.setShowBadge(true);
+ ido.setSound(null, new AudioAttributes.Builder()
+ .setUsage(USAGE_NOTIFICATION)
+ .setContentType(CONTENT_TYPE_SONIFICATION)
+ .setFlags(0)
+ .build());
+ NotificationChannel idp = new NotificationChannel("idp", "name3", IMPORTANCE_HIGH);
+ idp.lockFields(2);
+ idp.setSound(null, new AudioAttributes.Builder()
+ .setUsage(USAGE_NOTIFICATION)
+ .setContentType(CONTENT_TYPE_SONIFICATION)
+ .setFlags(0)
+ .build());
+
+ loadByteArrayXml(xml.getBytes(), true, UserHandle.USER_SYSTEM);
+
+ assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+
+ assertEquals(idn, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, idn.getId(), false));
+ compareChannels(ido, mHelper.getNotificationChannel(PKG_O, UID_O, ido.getId(), false));
+ compareChannels(idp, mHelper.getNotificationChannel(PKG_P, UID_P, idp.getId(), false));
+
+ verify(mPermissionHelper, never()).setNotificationPermission(any());
+ }
+
+ @Test
+ public void testChannelXmlForNonBackup_postMigration() throws Exception {
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+ mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
+
+ ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), true);
+ appPermissions.put(new Pair(3, "third"), false);
+ appPermissions.put(new Pair(UID_P, PKG_P), true);
+ appPermissions.put(new Pair(UID_O, PKG_O), false);
+ appPermissions.put(new Pair(UID_N_MR1, PKG_N_MR1), true);
+
+ when(mPermissionHelper.getNotificationPermissionValues(UserHandle.USER_SYSTEM))
+ .thenReturn(appPermissions);
+
+ NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
+ NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
+ NotificationChannel channel1 =
+ new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+ channel1.setSound(null, null);
+ NotificationChannel channel2 =
+ new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ channel2.setDescription("descriptions for all");
+ channel2.setSound(null, null);
+ channel2.enableLights(true);
+ channel2.setBypassDnd(true);
+ channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+ channel2.enableVibration(false);
+ channel2.setGroup(ncg.getId());
+ channel2.setLightColor(Color.BLUE);
+ NotificationChannel channel3 = new NotificationChannel("id3", "NAM3", IMPORTANCE_HIGH);
+ channel3.enableVibration(true);
+
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, false, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, false, false);
+ mHelper.createNotificationChannel(PKG_O, UID_O, getChannel(), true, false);
+
+ mHelper.setShowBadge(PKG_N_MR1, UID_N_MR1, true);
+ mHelper.setInvalidMessageSent(PKG_P, UID_P);
+ mHelper.setValidMessageSent(PKG_P, UID_P);
+ mHelper.setInvalidMsgAppDemoted(PKG_P, UID_P, true);
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(
+ PKG_N_MR1, UID_N_MR1, false, UserHandle.USER_SYSTEM);
+ String expected = "<ranking version=\"3\">\n"
+ + "<package name=\"com.example.o\" show_badge=\"true\" "
+ + "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
+ + "sent_valid_msg=\"false\" user_demote_msg_app=\"false\" uid=\"1111\">\n"
+ + "<channel id=\"id\" name=\"name\" importance=\"2\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" orig_imp=\"2\" />\n"
+ + "</package>\n"
+ + "<package name=\"com.example.p\" show_badge=\"true\" "
+ + "app_user_locked_fields=\"0\" sent_invalid_msg=\"true\" sent_valid_msg=\"true\""
+ + " user_demote_msg_app=\"true\" uid=\"2222\" />\n"
+ + "<package name=\"com.example.n_mr1\" show_badge=\"true\" "
+ + "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
+ + "sent_valid_msg=\"false\" user_demote_msg_app=\"false\" uid=\"0\">\n"
+ + "<channelGroup id=\"1\" name=\"bye\" blocked=\"false\" locked=\"0\" />\n"
+ + "<channelGroup id=\"2\" name=\"hello\" blocked=\"false\" locked=\"0\" />\n"
+ + "<channel id=\"id1\" name=\"name1\" importance=\"4\" show_badge=\"true\" "
+ + "orig_imp=\"4\" />\n"
+ + "<channel id=\"id2\" name=\"name2\" desc=\"descriptions for all\" "
+ + "importance=\"2\" priority=\"2\" visibility=\"-1\" lights=\"true\" "
+ + "light_color=\"-16776961\" show_badge=\"true\" group=\"1\" orig_imp=\"2\" />\n"
+ + "<channel id=\"id3\" name=\"NAM3\" importance=\"4\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" vibration_enabled=\"true\" show_badge=\"true\" "
+ + "orig_imp=\"4\" />\n"
+ + "<channel id=\"miscellaneous\" name=\"Uncategorized\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+ + "</package>\n"
+ + "</ranking>";
+ assertThat(baos.toString()).contains(expected);
+ }
+
+ @Test
+ public void testChannelXmlForBackup_postMigration() throws Exception {
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+ mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
+
+ ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), true);
+ appPermissions.put(new Pair(3, "third"), false);
+ appPermissions.put(new Pair(UID_P, PKG_P), true);
+ appPermissions.put(new Pair(UID_O, PKG_O), false);
+ appPermissions.put(new Pair(UID_N_MR1, PKG_N_MR1), true);
+
+ when(mPermissionHelper.getNotificationPermissionValues(UserHandle.USER_SYSTEM))
+ .thenReturn(appPermissions);
+
+ NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
+ NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
+ NotificationChannel channel1 =
+ new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+ channel1.setSound(null, null);
+ NotificationChannel channel2 =
+ new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ channel2.setDescription("descriptions for all");
+ channel2.setSound(null, null);
+ channel2.enableLights(true);
+ channel2.setBypassDnd(true);
+ channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+ channel2.enableVibration(false);
+ channel2.setGroup(ncg.getId());
+ channel2.setLightColor(Color.BLUE);
+ NotificationChannel channel3 = new NotificationChannel("id3", "NAM3", IMPORTANCE_HIGH);
+ channel3.enableVibration(true);
+
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, false, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, false, false);
+ mHelper.createNotificationChannel(PKG_O, UID_O, getChannel(), true, false);
+
+ mHelper.setShowBadge(PKG_N_MR1, UID_N_MR1, true);
+ mHelper.setInvalidMessageSent(PKG_P, UID_P);
+ mHelper.setValidMessageSent(PKG_P, UID_P);
+ mHelper.setInvalidMsgAppDemoted(PKG_P, UID_P, true);
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(
+ PKG_N_MR1, UID_N_MR1, true, UserHandle.USER_SYSTEM);
+ String expected = "<ranking version=\"3\">\n"
+ // Importance 0 because off in permissionhelper
+ + "<package name=\"com.example.o\" importance=\"0\" show_badge=\"true\" "
+ + "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
+ + "sent_valid_msg=\"false\" user_demote_msg_app=\"false\">\n"
+ + "<channel id=\"id\" name=\"name\" importance=\"2\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" orig_imp=\"2\" />\n"
+ + "</package>\n"
+ // Importance default because on in permission helper
+ + "<package name=\"com.example.p\" importance=\"3\" show_badge=\"true\" "
+ + "app_user_locked_fields=\"0\" sent_invalid_msg=\"true\" sent_valid_msg=\"true\""
+ + " user_demote_msg_app=\"true\" />\n"
+ // Importance default because on in permission helper
+ + "<package name=\"com.example.n_mr1\" importance=\"3\" show_badge=\"true\" "
+ + "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
+ + "sent_valid_msg=\"false\" user_demote_msg_app=\"false\">\n"
+ + "<channelGroup id=\"1\" name=\"bye\" blocked=\"false\" locked=\"0\" />\n"
+ + "<channelGroup id=\"2\" name=\"hello\" blocked=\"false\" locked=\"0\" />\n"
+ + "<channel id=\"id1\" name=\"name1\" importance=\"4\" show_badge=\"true\" "
+ + "orig_imp=\"4\" />\n"
+ + "<channel id=\"id2\" name=\"name2\" desc=\"descriptions for all\" "
+ + "importance=\"2\" priority=\"2\" visibility=\"-1\" lights=\"true\" "
+ + "light_color=\"-16776961\" show_badge=\"true\" group=\"1\" orig_imp=\"2\" />\n"
+ + "<channel id=\"id3\" name=\"NAM3\" importance=\"4\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" vibration_enabled=\"true\" show_badge=\"true\" "
+ + "orig_imp=\"4\" />\n"
+ + "<channel id=\"miscellaneous\" name=\"Uncategorized\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+ + "</package>\n"
+ // Packages that exist solely in permissionhelper
+ + "<package name=\"first\" importance=\"3\" />\n"
+ + "<package name=\"third\" importance=\"0\" />\n"
+ + "</ranking>";
+ assertThat(baos.toString()).contains(expected);
+ }
+
+ @Test
+ public void testChannelXmlForBackup_postMigration_noExternal() throws Exception {
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+ mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
+
+ ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(UID_P, PKG_P), true);
+ appPermissions.put(new Pair(UID_O, PKG_O), false);
+ when(mPermissionHelper.getNotificationPermissionValues(UserHandle.USER_SYSTEM))
+ .thenReturn(appPermissions);
+
+ NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
+ NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
+ NotificationChannel channel1 =
+ new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+ channel1.setSound(null, null);
+ NotificationChannel channel2 =
+ new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ channel2.setDescription("descriptions for all");
+ channel2.setSound(null, null);
+ channel2.enableLights(true);
+ channel2.setBypassDnd(true);
+ channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+ channel2.enableVibration(false);
+ channel2.setGroup(ncg.getId());
+ channel2.setLightColor(Color.BLUE);
+ NotificationChannel channel3 = new NotificationChannel("id3", "NAM3", IMPORTANCE_HIGH);
+ channel3.enableVibration(true);
+
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, false, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, false, false);
+ mHelper.createNotificationChannel(PKG_O, UID_O, getChannel(), true, false);
+
+ mHelper.setShowBadge(PKG_N_MR1, UID_N_MR1, true);
+ mHelper.setInvalidMessageSent(PKG_P, UID_P);
+ mHelper.setValidMessageSent(PKG_P, UID_P);
+ mHelper.setInvalidMsgAppDemoted(PKG_P, UID_P, true);
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(
+ PKG_N_MR1, UID_N_MR1, true, UserHandle.USER_SYSTEM);
+ String expected = "<ranking version=\"3\">\n"
+ // Importance 0 because off in permissionhelper
+ + "<package name=\"com.example.o\" importance=\"0\" show_badge=\"true\" "
+ + "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
+ + "sent_valid_msg=\"false\" user_demote_msg_app=\"false\">\n"
+ + "<channel id=\"id\" name=\"name\" importance=\"2\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" orig_imp=\"2\" />\n"
+ + "</package>\n"
+ // Importance default because on in permission helper
+ + "<package name=\"com.example.p\" importance=\"3\" show_badge=\"true\" "
+ + "app_user_locked_fields=\"0\" sent_invalid_msg=\"true\" sent_valid_msg=\"true\""
+ + " user_demote_msg_app=\"true\" />\n"
+ // Importance missing because missing from permission helper
+ + "<package name=\"com.example.n_mr1\" show_badge=\"true\" "
+ + "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
+ + "sent_valid_msg=\"false\" user_demote_msg_app=\"false\">\n"
+ + "<channelGroup id=\"1\" name=\"bye\" blocked=\"false\" locked=\"0\" />\n"
+ + "<channelGroup id=\"2\" name=\"hello\" blocked=\"false\" locked=\"0\" />\n"
+ + "<channel id=\"id1\" name=\"name1\" importance=\"4\" show_badge=\"true\" "
+ + "orig_imp=\"4\" />\n"
+ + "<channel id=\"id2\" name=\"name2\" desc=\"descriptions for all\" "
+ + "importance=\"2\" priority=\"2\" visibility=\"-1\" lights=\"true\" "
+ + "light_color=\"-16776961\" show_badge=\"true\" group=\"1\" orig_imp=\"2\" />\n"
+ + "<channel id=\"id3\" name=\"NAM3\" importance=\"4\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" vibration_enabled=\"true\" show_badge=\"true\" "
+ + "orig_imp=\"4\" />\n"
+ + "<channel id=\"miscellaneous\" name=\"Uncategorized\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+ + "</package>\n"
+ + "</ranking>";
+ assertThat(baos.toString()).contains(expected);
+ }
+
+ @Test
+ public void testChannelXmlForBackup_postMigration_noLocalSettings() throws Exception {
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+ mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
+
+ ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), true);
+ appPermissions.put(new Pair(3, "third"), false);
+ appPermissions.put(new Pair(UID_P, PKG_P), true);
+ appPermissions.put(new Pair(UID_O, PKG_O), false);
+ appPermissions.put(new Pair(UID_N_MR1, PKG_N_MR1), true);
+
+ when(mPermissionHelper.getNotificationPermissionValues(UserHandle.USER_SYSTEM))
+ .thenReturn(appPermissions);
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(
+ PKG_N_MR1, UID_N_MR1, true, UserHandle.USER_SYSTEM);
+ String expected = "<ranking version=\"3\">\n"
+ // Packages that exist solely in permissionhelper
+ + "<package name=\"" + PKG_P + "\" importance=\"3\" />\n"
+ + "<package name=\"" + PKG_O + "\" importance=\"0\" />\n"
+ + "<package name=\"" + PKG_N_MR1 + "\" importance=\"3\" />\n"
+ + "<package name=\"first\" importance=\"3\" />\n"
+ + "<package name=\"third\" importance=\"0\" />\n"
+ + "</ranking>";
+ assertThat(baos.toString()).contains(expected);
+ }
+
+ @Test
public void testBackupXml_backupCanonicalizedSoundUri() throws Exception {
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
@@ -2835,6 +3229,28 @@
}
@Test
+ public void testLockChannelsForOEM_onlyGivenPkg_appDoesNotExistYet_restoreData_postMigration()
+ throws Exception {
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+ mHelper.lockChannelsForOEM(new String[] {PKG_O});
+
+ final String xml = "<ranking version=\"1\">\n"
+ + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+ + "<channel id=\"a\" name=\"a\" importance=\"3\"/>"
+ + "<channel id=\"b\" name=\"b\" importance=\"3\"/>"
+ + "</package>"
+ + "</ranking>";
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+ null);
+ parser.nextTag();
+ mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+ assertFalse(mHelper.getNotificationChannel(PKG_O, UID_O, "a", false)
+ .isImportanceLockedByOEM());
+ }
+
+ @Test
public void testLockChannelsForOEM_channelSpecific_appDoesNotExistYet_restoreData()
throws Exception {
mHelper.lockChannelsForOEM(new String[] {PKG_O + ":b", PKG_O + ":c"});
@@ -2863,6 +3279,35 @@
}
@Test
+ public void testLockChannelsForOEM_channelSpecific_appDoesNotExistYet_restoreData_postMigration()
+ throws Exception {
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+ mHelper.lockChannelsForOEM(new String[] {PKG_O + ":b", PKG_O + ":c"});
+
+ final String xml = "<ranking version=\"1\">\n"
+ + "<package name=\"" + PKG_O + "\" uid=\"" + 3 + "\" >\n"
+ + "<channel id=\"a\" name=\"a\" importance=\"3\"/>"
+ + "<channel id=\"b\" name=\"b\" importance=\"3\"/>"
+ + "</package>"
+ + "<package name=\"" + PKG_O + "\" uid=\"" + 30 + "\" >\n"
+ + "<channel id=\"c\" name=\"c\" importance=\"3\"/>"
+ + "</package>"
+ + "</ranking>";
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+ null);
+ parser.nextTag();
+ mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+ assertFalse(mHelper.getNotificationChannel(PKG_O, 3, "a", false)
+ .isImportanceLockedByOEM());
+ assertFalse(mHelper.getNotificationChannel(PKG_O, 3, "b", false)
+ .isImportanceLockedByOEM());
+ assertFalse(mHelper.getNotificationChannel(PKG_O, 30, "c", false)
+ .isImportanceLockedByOEM());
+ }
+
+ @Test
public void testLockChannelsForOEM_channelSpecific_clearData() {
NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
mHelper.getImportance(PKG_O, UID_O);
@@ -2913,6 +3358,59 @@
}
@Test
+ public void testLockChannelsForOEM_channelSpecific_clearData_postMigration() {
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+ NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+ mHelper.getImportance(PKG_O, UID_O);
+ mHelper.lockChannelsForOEM(new String[] {PKG_O + ":" + a.getId()});
+ mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false);
+ assertFalse(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false)
+ .isImportanceLockedByOEM());
+
+ mHelper.clearData(PKG_O, UID_O);
+
+ // it's back!
+ mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false);
+ // and never locked
+ assertFalse(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false)
+ .isImportanceLockedByOEM());
+ }
+
+ @Test
+ public void testLockChannelsForOEM_channelDoesNotExistYet_appWide_postMigration() {
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+ NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+ NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
+ mHelper.createNotificationChannel(PKG_O, 3, a, true, false);
+
+ mHelper.lockChannelsForOEM(new String[] {PKG_O});
+
+ assertFalse(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false)
+ .isImportanceLockedByOEM());
+
+ mHelper.createNotificationChannel(PKG_O, 3, b, true, false);
+ assertFalse(mHelper.getNotificationChannel(PKG_O, 3, b.getId(), false)
+ .isImportanceLockedByOEM());
+ }
+
+ @Test
+ public void testLockChannelsForOEM_channelDoesNotExistYet_channelSpecific_postMigration() {
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+ NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+ NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
+ mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false);
+
+ mHelper.lockChannelsForOEM(new String[] {PKG_O + ":a", PKG_O + ":b"});
+
+ assertFalse(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false)
+ .isImportanceLockedByOEM());
+
+ mHelper.createNotificationChannel(PKG_O, UID_O, b, true, false);
+ assertFalse(mHelper.getNotificationChannel(PKG_O, UID_O, b.getId(), false)
+ .isImportanceLockedByOEM());
+ }
+
+ @Test
public void testUpdateNotificationChannel_oemLockedImportance() {
NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false);
@@ -2936,6 +3434,44 @@
}
@Test
+ public void testUpdateNotificationChannel_fixedPermission() {
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+ when(mPermissionHelper.isPermissionFixed(PKG_O, 0)).thenReturn(true);
+
+ NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false);
+
+ NotificationChannel update = new NotificationChannel("a", "a", IMPORTANCE_NONE);
+ update.setAllowBubbles(false);
+
+ mHelper.updateNotificationChannel(PKG_O, UID_O, update, true);
+
+ assertEquals(IMPORTANCE_HIGH,
+ mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).getImportance());
+ assertEquals(false,
+ mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).canBubble());
+ }
+
+ @Test
+ public void testUpdateNotificationChannel_notFixedPermission() {
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+ when(mPermissionHelper.isPermissionFixed(PKG_O, 0)).thenReturn(false);
+
+ NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false);
+
+ NotificationChannel update = new NotificationChannel("a", "a", IMPORTANCE_NONE);
+ update.setAllowBubbles(false);
+
+ mHelper.updateNotificationChannel(PKG_O, UID_O, update, true);
+
+ assertEquals(IMPORTANCE_NONE,
+ mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).getImportance());
+ assertEquals(false,
+ mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).canBubble());
+ }
+
+ @Test
public void testUpdateDefaultApps_add_multiUser() {
NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java
index 7446e9e..7d89d87 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java
@@ -7,6 +7,7 @@
import static org.mockito.Mockito.spy;
import android.app.Application;
+import android.app.PendingIntent;
import android.content.Intent;
import android.net.Uri;
import android.service.notification.Condition;
@@ -18,6 +19,7 @@
import androidx.test.filters.SmallTest;
import com.android.server.UiServiceTestCase;
+import com.android.server.pm.PackageManagerService;
import org.junit.Before;
import org.junit.Test;
@@ -326,6 +328,12 @@
assertEquals(Condition.STATE_FALSE, condition.state);
}
+ @Test
+ public void testGetPendingIntent() {
+ PendingIntent pi = mService.getPendingIntent(1000);
+ assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME, pi.getIntent().getPackage());
+ }
+
private Calendar getNow() {
Calendar now = new GregorianCalendar();
now.set(Calendar.HOUR_OF_DAY, 14);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 0b91802..d4d8b868 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -477,7 +477,6 @@
@Test
public void testConsecutiveLaunch() {
- mTrampolineActivity.setState(ActivityRecord.State.INITIALIZING, "test");
onActivityLaunched(mTrampolineActivity);
mActivityMetricsLogger.notifyActivityLaunching(mTopActivity.intent,
mTrampolineActivity /* caller */, mTrampolineActivity.getUid());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 5327dee..bc0cc1f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -16,9 +16,11 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
@@ -100,7 +102,6 @@
import static org.mockito.Mockito.never;
import android.app.ActivityOptions;
-import android.app.WindowConfiguration;
import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.DestroyActivityItem;
@@ -526,8 +527,7 @@
final Rect insets = new Rect();
final DisplayInfo displayInfo = task.mDisplayContent.getDisplayInfo();
final DisplayPolicy policy = task.mDisplayContent.getDisplayPolicy();
- policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
- displayInfo.logicalHeight, displayInfo.displayCutout, insets);
+ policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.displayCutout, insets);
policy.convertNonDecorInsetsToStableInsets(insets, displayInfo.rotation);
Task.intersectWithInsetsIfFits(stableRect, stableRect, insets);
@@ -560,7 +560,7 @@
final ActivityRecord activity = createActivityWith2LevelTask();
final Task task = activity.getTask();
final Task rootTask = activity.getRootTask();
- rootTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ rootTask.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final Rect stableRect = new Rect();
rootTask.mDisplayContent.getStableRect(stableRect);
@@ -568,8 +568,7 @@
final Rect insets = new Rect();
final DisplayInfo displayInfo = rootTask.mDisplayContent.getDisplayInfo();
final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy();
- policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
- displayInfo.logicalHeight, displayInfo.displayCutout, insets);
+ policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.displayCutout, insets);
policy.convertNonDecorInsetsToStableInsets(insets, displayInfo.rotation);
Task.intersectWithInsetsIfFits(stableRect, stableRect, insets);
@@ -602,19 +601,22 @@
@Test
public void respectRequestedOrientationForNonResizableInSplitWindows() {
- final Task task = new TaskBuilder(mSupervisor)
- .setCreateParentTask(true).setCreateActivity(true).build();
- final Task rootTask = task.getRootTask();
+ final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea();
+ spyOn(tda);
+ doReturn(true).when(tda).supportsNonResizableMultiWindow();
+ final Task rootTask = mDisplayContent.getDefaultTaskDisplayArea().createRootTask(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ rootTask.setBounds(0, 0, 1000, 500);
final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setParentTask(task)
+ .setParentTask(rootTask)
+ .setCreateTask(true)
.setOnTop(true)
.setResizeMode(RESIZE_MODE_UNRESIZEABLE)
.setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
.build();
+ final Task task = activity.getTask();
// Task in landscape.
- rootTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- task.setBounds(0, 0, 1000, 500);
assertEquals(ORIENTATION_LANDSCAPE, task.getConfiguration().orientation);
// Asserts fixed orientation request is respected, and the orientation is not changed.
@@ -623,7 +625,7 @@
// Clear size compat.
activity.clearSizeCompatMode();
activity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
- activity.mDisplayContent.sendNewConfiguration();
+ mDisplayContent.sendNewConfiguration();
// Relaunching the app should still respect the orientation request.
assertEquals(ORIENTATION_PORTRAIT, activity.getConfiguration().orientation);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index b0fb812..abd43a4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -623,7 +623,7 @@
wpcAfterConfigChange1.getConfiguration().getLocales());
assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive());
- mAtm.mInternal.onPackageUninstalled(DEFAULT_PACKAGE_NAME);
+ mAtm.mInternal.onPackageUninstalled(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
WindowProcessController wpcAfterConfigChange2 = createWindowProcessController(
DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
index a34586b..66da2a6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
@@ -19,6 +19,7 @@
import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
@@ -36,6 +37,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
@@ -266,6 +268,30 @@
}
/**
+ * Verifies that process state will be updated with pending top without activity state change.
+ * E.g. switch focus between resumed activities in multi-window mode.
+ */
+ @Test
+ public void testUpdatePendingTopForTopResumed() {
+ final Task task1 = new TaskBuilder(mSupervisor)
+ .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW).build();
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm)
+ .setTask(task1).setUid(ActivityBuilder.DEFAULT_FAKE_UID + 1).build();
+ task1.setResumedActivity(activity1, "test");
+
+ final ActivityRecord activity2 = new TaskBuilder(mSupervisor)
+ .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW)
+ .setCreateActivity(true).build().getTopMostActivity();
+ activity2.getTask().setResumedActivity(activity2, "test");
+
+ mAtm.mAmInternal.deletePendingTopUid(activity1.getUid());
+ clearInvocations(mAtm);
+ activity1.moveFocusableActivityToTop("test");
+ assertTrue(mAtm.mAmInternal.isPendingTopUid(activity1.getUid()));
+ verify(mAtm).updateOomAdj();
+ }
+
+ /**
* We need to launch home again after user unlocked for those displays that do not have
* encryption aware home app.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index fb8bc7b..c0959d3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -419,6 +419,7 @@
task.getBounds(taskBounds);
taskFragment.setBounds(0, 0, taskBounds.right / 2, taskBounds.bottom);
spyOn(taskFragment);
+ mockSurfaceFreezerSnapshot(taskFragment.mSurfaceFreezer);
assertTrue(mDc.mChangingContainers.isEmpty());
assertFalse(mDc.mAppTransition.isTransitionSet());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 0a3c813..046328d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -291,11 +291,11 @@
mAppWindow,
mChildAppWindowAbove,
mDockedDividerWindow,
- voiceInteractionWindow,
mImeWindow,
mImeDialogWindow,
mStatusBarWindow,
mNotificationShadeWindow,
+ voiceInteractionWindow, // It can show above lock screen.
mNavBarWindow));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
index 004e45a..f41fee7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
@@ -167,15 +167,13 @@
private Rect getStableInsetsLw(DisplayInfo di) {
Rect result = new Rect();
- mDisplayPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
- di.displayCutout, result);
+ mDisplayPolicy.getStableInsetsLw(di.rotation, di.displayCutout, result);
return result;
}
private Rect getNonDecorInsetsLw(DisplayInfo di) {
Rect result = new Rect();
- mDisplayPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
- di.displayCutout, result);
+ mDisplayPolicy.getNonDecorInsetsLw(di.rotation, di.displayCutout, result);
return result;
}
@@ -185,8 +183,8 @@
}
private int getNonDecorDisplayHeight(DisplayInfo di) {
- return mDisplayPolicy.getNonDecorDisplayHeight(di.logicalWidth, di.logicalHeight,
- di.rotation, 0 /* ui */, di.displayCutout);
+ return mDisplayPolicy.getNonDecorDisplayHeight(di.logicalHeight, di.rotation,
+ di.displayCutout);
}
private int getConfigDisplayWidth(DisplayInfo di) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 03132c4..af45b80 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -24,7 +24,6 @@
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
-import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
@@ -35,13 +34,8 @@
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
-import static android.view.WindowManagerPolicyConstants.ALT_BAR_BOTTOM;
-import static android.view.WindowManagerPolicyConstants.ALT_BAR_LEFT;
-import static android.view.WindowManagerPolicyConstants.ALT_BAR_RIGHT;
-import static android.view.WindowManagerPolicyConstants.ALT_BAR_TOP;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -59,10 +53,8 @@
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.util.Pair;
-import android.util.SparseArray;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
-import android.view.Gravity;
import android.view.InsetsState;
import android.view.InsetsVisibilities;
import android.view.PrivacyIndicatorBounds;
@@ -221,13 +213,9 @@
InsetsSourceProvider provider =
mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR);
- if (INSETS_LAYOUT_GENERALIZATION) {
- // In the new flexible insets setup, the insets frame should always respect the window
- // layout result.
- assertEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame());
- } else {
- assertNotEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame());
- }
+ // In the new flexible insets setup, the insets frame should always respect the window
+ // layout result.
+ assertEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame());
}
@Test
@@ -244,47 +232,6 @@
}
@Test
- public void addingWindow_variousGravities_alternateBarPosUpdated() {
- mDisplayPolicy.removeWindowLw(mNavBarWindow); // Removes the existing one.
-
- WindowState win1 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel1");
- win1.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
- win1.mAttrs.gravity = Gravity.TOP;
- win1.getFrame().set(0, 0, 200, 500);
- addWindow(win1);
-
- assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_TOP);
- mDisplayPolicy.removeWindowLw(win1);
-
- WindowState win2 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel2");
- win2.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
- win2.mAttrs.gravity = Gravity.BOTTOM;
- win2.getFrame().set(0, 0, 200, 500);
- addWindow(win2);
-
- assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_BOTTOM);
- mDisplayPolicy.removeWindowLw(win2);
-
- WindowState win3 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel3");
- win3.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
- win3.mAttrs.gravity = Gravity.LEFT;
- win3.getFrame().set(0, 0, 200, 500);
- addWindow(win3);
-
- assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_LEFT);
- mDisplayPolicy.removeWindowLw(win3);
-
- WindowState win4 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel4");
- win4.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
- win4.mAttrs.gravity = Gravity.RIGHT;
- win4.getFrame().set(0, 0, 200, 500);
- addWindow(win4);
-
- assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_RIGHT);
- mDisplayPolicy.removeWindowLw(win4);
- }
-
- @Test
public void layoutWindowLw_fitStatusBars() {
mWindow.mAttrs.setFitInsetsTypes(Type.statusBars());
addWindowWithRawInsetsState(mWindow);
@@ -706,8 +653,7 @@
// Force the display bounds because it is not synced with display frames in policy test.
mDisplayContent.getWindowConfiguration().setBounds(mFrames.mUnrestricted);
mDisplayContent.getInsetsStateController().onPostLayout();
- mDisplayPolicy.simulateLayoutDisplay(simulatedDisplayFrames,
- new SparseArray<>() /* barContentFrames */);
+ mDisplayPolicy.simulateLayoutDisplay(simulatedDisplayFrames);
final StringWriter realFramesDump = new StringWriter();
mFrames.dump(prefix, new PrintWriter(realFramesDump));
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index 1d2baab..f5b361c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -45,7 +45,6 @@
import android.util.Pair;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
-import android.view.Gravity;
import android.view.WindowManagerGlobal;
import com.android.internal.R;
@@ -94,10 +93,7 @@
DISPLAY_WIDTH, DISPLAY_HEIGHT, shortSizeDp, longSizeDp);
mDisplayPolicy.onConfigurationChanged();
- mStatusBarWindow.mAttrs.gravity = Gravity.TOP;
addWindow(mStatusBarWindow);
-
- mNavBarWindow.mAttrs.gravity = Gravity.BOTTOM;
addWindow(mNavBarWindow);
// Update source frame and visibility of insets providers.
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index a1e8ca4..32cca47 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -37,6 +37,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
@@ -44,6 +45,7 @@
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Intent;
+import android.content.pm.ShortcutServiceInternal;
import android.graphics.PixelFormat;
import android.os.Binder;
import android.os.IBinder;
@@ -71,6 +73,7 @@
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import java.util.ArrayList;
@@ -89,6 +92,7 @@
public class DragDropControllerTests extends WindowTestsBase {
private static final int TIMEOUT_MS = 3000;
private static final int TEST_UID = 12345;
+ private static final int TEST_PROFILE_UID = 12345 * UserHandle.PER_USER_RANGE;
private static final int TEST_PID = 67890;
private static final String TEST_PACKAGE = "com.test.package";
@@ -387,6 +391,32 @@
}
}
+ @Test
+ public void testValidateProfileAppShortcutArguments_notCallingUid() {
+ doReturn(PERMISSION_GRANTED).when(mWm.mContext)
+ .checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS));
+ final Session session = Mockito.spy(new Session(mWm, new IWindowSessionCallback.Stub() {
+ @Override
+ public void onAnimatorScaleChanged(float scale) {}
+ }));
+ final ShortcutServiceInternal shortcutService = mock(ShortcutServiceInternal.class);
+ final Intent[] shortcutIntents = new Intent[1];
+ shortcutIntents[0] = new Intent();
+ doReturn(shortcutIntents).when(shortcutService).createShortcutIntents(anyInt(), any(),
+ any(), any(), anyInt(), anyInt(), anyInt());
+ LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
+ LocalServices.addService(ShortcutServiceInternal.class, shortcutService);
+
+ ArgumentCaptor<Integer> callingUser = ArgumentCaptor.forClass(Integer.class);
+ session.validateAndResolveDragMimeTypeExtras(
+ createClipDataForShortcut("test_package", "test_shortcut_id",
+ mock(UserHandle.class)),
+ TEST_PROFILE_UID, TEST_PID, TEST_PACKAGE);
+ verify(shortcutService).createShortcutIntents(callingUser.capture(), any(),
+ any(), any(), anyInt(), anyInt(), anyInt());
+ assertTrue(callingUser.getValue() == UserHandle.getUserId(TEST_PROFILE_UID));
+ }
+
private ClipData createClipDataForShortcut(String packageName, String shortcutId,
UserHandle user) {
final Intent data = new Intent();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 22e687a..2847283 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1244,7 +1244,8 @@
}
return new TaskSnapshot(1, new ComponentName("", ""), buffer,
ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
- Surface.ROTATION_0, taskSize, new Rect() /* insets */, false /* isLowResolution */,
+ Surface.ROTATION_0, taskSize, new Rect() /* contentInsets */,
+ new Rect() /* letterboxInsets*/, false /* isLowResolution */,
true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN, 0 /* mSystemUiVisibility */,
false /* isTranslucent */, false /* hasImeSurface */);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 9d2a691..b4c449a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -124,7 +124,7 @@
// Verify that the finish callback to reparent the leash is called
verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_RECENTS), eq(adapter));
// Verify the animation canceled callback to the app was made
- verify(mMockRunner).onAnimationCanceled(null /* taskSnapshot */);
+ verify(mMockRunner).onAnimationCanceled(null /* taskIds */, null /* taskSnapshots */);
verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
}
@@ -207,7 +207,8 @@
wallpaperWindowToken.cancelAnimation();
assertTrue(mController.isAnimatingTask(activity.getTask()));
assertFalse(mController.isAnimatingWallpaper(wallpaperWindowToken));
- verify(mMockRunner, never()).onAnimationCanceled(null /* taskSnapshot */);
+ verify(mMockRunner, never()).onAnimationCanceled(null /* taskIds */,
+ null /* taskSnapshots */);
}
@Test
@@ -254,7 +255,7 @@
mController.setDeferredCancel(true /* deferred */, false /* screenshot */);
mController.cancelAnimationWithScreenshot(false /* screenshot */);
- verify(mMockRunner).onAnimationCanceled(null /* taskSnapshot */);
+ verify(mMockRunner).onAnimationCanceled(null /* taskIds */, null /* taskSnapshots */);
// Simulate the app transition finishing
mController.mAppTransitionListener.onAppTransitionStartingLocked(false, false, 0, 0, 0);
@@ -281,7 +282,8 @@
anyInt(), eq(false) /* restoreFromDisk */, eq(false) /* isLowResolution */);
mController.setDeferredCancel(true /* deferred */, true /* screenshot */);
mController.cancelAnimationWithScreenshot(true /* screenshot */);
- verify(mMockRunner).onAnimationCanceled(mMockTaskSnapshot /* taskSnapshot */);
+ verify(mMockRunner).onAnimationCanceled(any(int[].class) /* taskIds */,
+ any(TaskSnapshot[].class) /* taskSnapshots */);
// Continue the animation (simulating a call to cleanupScreenshot())
mController.continueDeferredCancelAnimation();
@@ -322,7 +324,7 @@
doReturn(mMockTaskSnapshot).when(mWm.mTaskSnapshotController).getSnapshot(anyInt(),
anyInt(), eq(false) /* restoreFromDisk */, eq(false) /* isLowResolution */);
mController.cancelAnimationWithScreenshot(true /* screenshot */);
- verify(mMockRunner).onAnimationCanceled(any());
+ verify(mMockRunner).onAnimationCanceled(any(), any());
// Simulate process crashing and ensure the animation is still canceled
mController.binderDied();
@@ -700,7 +702,7 @@
mController.setWillFinishToHome(true);
mController.cancelAnimationForDisplayChange();
- verify(mMockRunner).onAnimationCanceled(any());
+ verify(mMockRunner).onAnimationCanceled(any(), any());
verify(mAnimationCallbacks).onAnimationFinished(REORDER_MOVE_TO_TOP, false);
}
@@ -715,7 +717,7 @@
mController.setWillFinishToHome(false);
mController.cancelAnimationForDisplayChange();
- verify(mMockRunner).onAnimationCanceled(any());
+ verify(mMockRunner).onAnimationCanceled(any(), any());
verify(mAnimationCallbacks).onAnimationFinished(REORDER_MOVE_TO_ORIGINAL_POSITION, false);
}
@@ -735,7 +737,7 @@
doReturn(mMockTaskSnapshot).when(mWm.mTaskSnapshotController).getSnapshot(anyInt(),
anyInt(), eq(false) /* restoreFromDisk */, eq(false) /* isLowResolution */);
mController.cancelAnimationForHomeStart();
- verify(mMockRunner).onAnimationCanceled(any());
+ verify(mMockRunner).onAnimationCanceled(any(), any());
// Continue the animation (simulating a call to cleanupScreenshot())
mController.continueDeferredCancelAnimation();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 73e571a..d4991fc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -25,6 +25,7 @@
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
+import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
@@ -158,46 +159,6 @@
}
@Test
- public void testKeepBoundsWhenChangingFromFreeformToFullscreen() {
- removeGlobalMinSizeRestriction();
- // Create landscape freeform display and a freeform app.
- DisplayContent display = new TestDisplayContent.Builder(mAtm, 2000, 1000)
- .setCanRotate(false)
- .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM).build();
- setUpApp(display);
-
- // Put app window into portrait freeform and then make it a compat app.
- final Rect bounds = new Rect(100, 100, 400, 600);
- mTask.setBounds(bounds);
- prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
- assertEquals(bounds, mActivity.getBounds());
- // Activity is not yet in size compat mode; it is filling the freeform task window.
- assertActivityMaxBoundsSandboxed();
-
- // The activity should be able to accept negative x position [-150, 100 - 150, 600].
- final int dx = bounds.left + bounds.width() / 2;
- final int dy = bounds.top + bounds.height() / 2;
- mTask.setBounds(bounds.left - dx, bounds.top - dy, bounds.right - dx, bounds.bottom - dy);
- // expected:<Rect(-150, 100 - 150, 600)> but was:<Rect(-150, 0 - 150, 500)>
- assertEquals(mTask.getBounds(), mActivity.getBounds());
-
- final int density = mActivity.getConfiguration().densityDpi;
-
- // Change display configuration to fullscreen.
- Configuration c = new Configuration(display.getRequestedOverrideConfiguration());
- c.windowConfiguration.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
- display.onRequestedOverrideConfigurationChanged(c);
-
- // Check if dimensions on screen stay the same by scaling.
- assertScaled();
- assertEquals(bounds.width(), mActivity.getBounds().width());
- assertEquals(bounds.height(), mActivity.getBounds().height());
- assertEquals(density, mActivity.getConfiguration().densityDpi);
- // Size compat mode is sandboxed at the activity level.
- assertActivityMaxBoundsSandboxed();
- }
-
- @Test
public void testFixedAspectRatioBoundsWithDecorInSquareDisplay() {
final int notchHeight = 100;
setUpApp(new TestDisplayContent.Builder(mAtm, 600, 800).setNotch(notchHeight).build());
@@ -397,8 +358,7 @@
@Test
public void testAspectRatioMatchParentBoundsAndImeAttachable() {
- setUpApp(new TestDisplayContent.Builder(mAtm, 1000, 2000)
- .setSystemDecorations(true).build());
+ setUpApp(new TestDisplayContent.Builder(mAtm, 1000, 2000).build());
prepareUnresizable(mActivity, 2f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
assertFitted();
@@ -687,7 +647,7 @@
.setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE)
.setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
.build();
- assertTrue(activity.shouldCreateCompatDisplayInsets());
+ assertFalse(activity.shouldCreateCompatDisplayInsets());
// The non-resizable activity should not be size compat because it is on a resizable task
// in multi-window mode.
@@ -719,7 +679,7 @@
}
@Test
- public void testShouldCreateCompatDisplayInsetsWhenUnresizeableAndSupportsSizeChangesFalse() {
+ public void testShouldNotCreateCompatDisplayInsetsWhenRootActivityIsResizeable() {
setUpDisplaySizeWithApp(1000, 2500);
// Make the task root resizable.
@@ -728,7 +688,7 @@
// Create an activity on the same task.
final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */false,
RESIZE_MODE_UNRESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
- assertTrue(activity.shouldCreateCompatDisplayInsets());
+ assertFalse(activity.shouldCreateCompatDisplayInsets());
}
@Test
@@ -1275,7 +1235,7 @@
// The activity doesn't fill the display, so the letterbox of the rotated activity is
// overlapped with the rotated content frame of status bar. Hence the status bar shouldn't
// be transparent.
- assertFalse(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
+ assertFalse(displayPolicy.isFullyTransparentAllowed(w, ITYPE_STATUS_BAR));
// Activity is sandboxed.
assertActivityMaxBoundsSandboxed();
@@ -1288,7 +1248,7 @@
// The letterbox should only cover the notch area, so status bar can be transparent.
assertEquals(new Rect(notchHeight, 0, 0, 0), mActivity.getLetterboxInsets());
- assertTrue(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
+ assertTrue(displayPolicy.isFullyTransparentAllowed(w, ITYPE_STATUS_BAR));
assertActivityMaxBoundsSandboxed();
}
@@ -2269,6 +2229,12 @@
activity.info.resizeMode = isUnresizable
? RESIZE_MODE_UNRESIZEABLE
: RESIZE_MODE_RESIZEABLE;
+ final Task task = activity.getTask();
+ if (task != null) {
+ // Update the Task resize value as activity will follow the task.
+ task.mResizeMode = activity.info.resizeMode;
+ task.getRootActivity().info.resizeMode = activity.info.resizeMode;
+ }
activity.mVisibleRequested = true;
if (maxAspect >= 0) {
activity.info.setMaxAspectRatio(maxAspect);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index cb209abf..42f4d58 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -82,6 +82,7 @@
@Test
public void testStartChangeTransition_resetSurface() {
+ mockSurfaceFreezerSnapshot(mTaskFragment.mSurfaceFreezer);
final Rect startBounds = new Rect(0, 0, 1000, 1000);
final Rect endBounds = new Rect(500, 500, 1000, 1000);
mTaskFragment.setBounds(startBounds);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index 97afc16..f573b70 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -144,7 +144,8 @@
final int orientation = Configuration.ORIENTATION_PORTRAIT;
final float scaleFraction = 0.25f;
final Rect contentInsets = new Rect(1, 2, 3, 4);
- final Point taskSize = new Point(5, 6);
+ final Rect letterboxInsets = new Rect(5, 6, 7, 8);
+ final Point taskSize = new Point(9, 10);
try {
TaskSnapshot.Builder builder =
@@ -156,6 +157,7 @@
builder.setColorSpace(sRGB);
builder.setOrientation(orientation);
builder.setContentInsets(contentInsets);
+ builder.setLetterboxInsets(letterboxInsets);
builder.setIsTranslucent(true);
builder.setSnapshot(buffer);
builder.setIsRealSnapshot(true);
@@ -176,6 +178,7 @@
assertFalse(snapshot.isLowResolution());
assertEquals(orientation, snapshot.getOrientation());
assertEquals(contentInsets, snapshot.getContentInsets());
+ assertEquals(letterboxInsets, snapshot.getLetterboxInsets());
assertTrue(snapshot.isTranslucent());
assertSame(buffer, snapshot.getHardwareBuffer());
assertTrue(snapshot.isRealSnapshot());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index d09ba29..437d418 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -59,7 +59,8 @@
*/
class TaskSnapshotPersisterTestBase extends WindowTestsBase {
- private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40);
+ private static final Rect TEST_CONTENT_INSETS = new Rect(10, 20, 30, 40);
+ private static final Rect TEST_LETTERBOX_INSETS = new Rect();
static final File FILES_DIR = getInstrumentation().getTargetContext().getFilesDir();
static final long MOCK_SNAPSHOT_ID = 12345678;
@@ -208,7 +209,7 @@
return new TaskSnapshot(MOCK_SNAPSHOT_ID, mTopActivityComponent,
HardwareBuffer.createFromGraphicBuffer(buffer),
ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
- mRotation, taskSize, TEST_INSETS,
+ mRotation, taskSize, TEST_CONTENT_INSETS, TEST_LETTERBOX_INSETS,
// When building a TaskSnapshot with the Builder class, isLowResolution
// is always false. Low-res snapshots are only created when loading from
// disk.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index 061a4c5f..c0ae8a5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
@@ -160,7 +161,8 @@
if (mSystemDecorations) {
doReturn(true).when(newDisplay).supportsSystemDecorations();
doReturn(true).when(displayPolicy).hasNavigationBar();
- doReturn(20).when(displayPolicy).getNavigationBarHeight(anyInt(), anyInt());
+ doReturn(NAV_BAR_BOTTOM).when(displayPolicy).navigationBarPosition(anyInt());
+ doReturn(20).when(displayPolicy).getNavigationBarHeight(anyInt());
} else {
doReturn(false).when(displayPolicy).hasNavigationBar();
doReturn(false).when(displayPolicy).hasStatusBar();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 4e77fa7..d9a166a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -473,7 +473,7 @@
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
final TransitionController controller = new TransitionController(mAtm, snapshotController);
final ITransitionPlayer player = new ITransitionPlayer.Default();
- controller.registerTransitionPlayer(player);
+ controller.registerTransitionPlayer(player, null /* appThread */);
ITaskOrganizer mockOrg = mock(ITaskOrganizer.class);
final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
@@ -539,7 +539,7 @@
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
final TransitionController controller = new TransitionController(mAtm, snapshotController);
final ITransitionPlayer player = new ITransitionPlayer.Default();
- controller.registerTransitionPlayer(player);
+ controller.registerTransitionPlayer(player, null /* appThread */);
ITaskOrganizer mockOrg = mock(ITaskOrganizer.class);
final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index 3f0c13c..f366f57 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -316,7 +316,8 @@
final IBinder mockBinder = mock(IBinder.class);
final ITransitionPlayer mockPlayer = mock(ITransitionPlayer.class);
doReturn(mockBinder).when(mockPlayer).asBinder();
- mWm.mAtmService.getTransitionController().registerTransitionPlayer(mockPlayer);
+ mWm.mAtmService.getTransitionController().registerTransitionPlayer(mockPlayer,
+ null /* appThread */);
Transition transit =
mWm.mAtmService.getTransitionController().createTransition(TRANSIT_OPEN);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index db7def8..bec53d7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -1119,16 +1119,15 @@
final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
spyOn(container);
spyOn(surfaceAnimator);
- spyOn(surfaceFreezer);
+ mockSurfaceFreezerSnapshot(surfaceFreezer);
doReturn(t).when(container).getPendingTransaction();
doReturn(t).when(container).getSyncTransaction();
// Leash and snapshot created for change transition.
container.initializeChangeTransition(new Rect(0, 0, 1000, 2000));
- // Can't really take a snapshot, manually set one.
- surfaceFreezer.mSnapshot = mock(SurfaceFreezer.Snapshot.class);
assertNotNull(surfaceFreezer.mLeash);
+ assertNotNull(surfaceFreezer.mSnapshot);
assertEquals(surfaceFreezer.mLeash, container.getAnimationLeash());
// Start animation: surfaceAnimator take over the leash and snapshot from surfaceFreezer.
@@ -1145,9 +1144,9 @@
// Prepare another change transition.
container.initializeChangeTransition(new Rect(0, 0, 1000, 2000));
- surfaceFreezer.mSnapshot = mock(SurfaceFreezer.Snapshot.class);
assertNotNull(surfaceFreezer.mLeash);
+ assertNotNull(surfaceFreezer.mSnapshot);
assertEquals(surfaceFreezer.mLeash, container.getAnimationLeash());
assertNotEquals(prevLeash, container.getAnimationLeash());
@@ -1174,6 +1173,64 @@
assertNull(surfaceAnimator.mSnapshot);
}
+ @Test
+ public void testUnfreezeWindow_removeWindowFromChanging() {
+ final WindowContainer container = createTaskFragmentWithParentTask(
+ createTask(mDisplayContent), false);
+ mockSurfaceFreezerSnapshot(container.mSurfaceFreezer);
+ final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+
+ container.initializeChangeTransition(new Rect(0, 0, 1000, 2000));
+
+ assertTrue(mDisplayContent.mChangingContainers.contains(container));
+
+ container.mSurfaceFreezer.unfreeze(t);
+
+ assertFalse(mDisplayContent.mChangingContainers.contains(container));
+ }
+
+ @Test
+ public void testFailToTaskSnapshot_unfreezeWindow() {
+ final WindowContainer container = createTaskFragmentWithParentTask(
+ createTask(mDisplayContent), false);
+ final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+ spyOn(container.mSurfaceFreezer);
+
+ container.initializeChangeTransition(new Rect(0, 0, 1000, 2000));
+
+ verify(container.mSurfaceFreezer).freeze(any(), any(), any(), any());
+ verify(container.mSurfaceFreezer).unfreeze(any());
+ assertTrue(mDisplayContent.mChangingContainers.isEmpty());
+ }
+
+ @Test
+ public void testRemoveUnstartedFreezeSurfaceWhenFreezeAgain() {
+ final WindowContainer container = createTaskFragmentWithParentTask(
+ createTask(mDisplayContent), false);
+ container.mSurfaceControl = mock(SurfaceControl.class);
+ final SurfaceFreezer surfaceFreezer = container.mSurfaceFreezer;
+ mockSurfaceFreezerSnapshot(surfaceFreezer);
+ final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+ spyOn(container);
+ doReturn(t).when(container).getPendingTransaction();
+ doReturn(t).when(container).getSyncTransaction();
+
+ // Leash and snapshot created for change transition.
+ container.initializeChangeTransition(new Rect(0, 0, 1000, 2000));
+
+ assertNotNull(surfaceFreezer.mLeash);
+ assertNotNull(surfaceFreezer.mSnapshot);
+
+ final SurfaceControl prevLeash = surfaceFreezer.mLeash;
+ final SurfaceFreezer.Snapshot prevSnapshot = surfaceFreezer.mSnapshot;
+ spyOn(prevSnapshot);
+
+ container.initializeChangeTransition(new Rect(0, 0, 1500, 2500));
+
+ verify(t).remove(prevLeash);
+ verify(prevSnapshot).destroy(t);
+ }
+
/* Used so we can gain access to some protected members of the {@link WindowContainer} class */
private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
private final int mLayer;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index a482bda..1eed79f1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -543,6 +543,36 @@
}
@Test
+ public void testSetAdjacentLaunchRoot() {
+ DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
+
+ final Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+ dc, WINDOWING_MODE_MULTI_WINDOW, null);
+ final RunningTaskInfo info1 = task1.getTaskInfo();
+ final Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+ dc, WINDOWING_MODE_MULTI_WINDOW, null);
+ final RunningTaskInfo info2 = task2.getTaskInfo();
+
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setAdjacentRoots(info1.token, info2.token);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
+ assertEquals(task1.getAdjacentTaskFragment(), task2);
+ assertEquals(task2.getAdjacentTaskFragment(), task1);
+
+ wct = new WindowContainerTransaction();
+ wct.setLaunchAdjacentFlagRoot(info1.token);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
+ assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, task1);
+
+ task1.setAdjacentTaskFragment(null);
+ task2.setAdjacentTaskFragment(null);
+ wct = new WindowContainerTransaction();
+ wct.clearLaunchAdjacentFlagRoot(info1.token);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
+ assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, null);
+ }
+
+ @Test
public void testTileAddRemoveChild() {
final StubOrganizer listener = new StubOrganizer();
mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 42fe0e9..386ff4b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -30,7 +30,6 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.os.Process.SYSTEM_UID;
import static android.view.View.VISIBLE;
-import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
@@ -76,6 +75,7 @@
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.hardware.HardwareBuffer;
import android.hardware.display.DisplayManager;
import android.os.Build;
import android.os.Bundle;
@@ -279,12 +279,11 @@
}
if (addAll || ArrayUtils.contains(requestedWindows, W_STATUS_BAR)) {
mStatusBarWindow = createCommonWindow(null, TYPE_STATUS_BAR, "mStatusBarWindow");
- if (INSETS_LAYOUT_GENERALIZATION) {
- mStatusBarWindow.mAttrs.height = STATUS_BAR_HEIGHT;
- mStatusBarWindow.mAttrs.gravity = Gravity.TOP;
- mStatusBarWindow.mAttrs.layoutInDisplayCutoutMode =
- LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- }
+ mStatusBarWindow.mAttrs.height = STATUS_BAR_HEIGHT;
+ mStatusBarWindow.mAttrs.gravity = Gravity.TOP;
+ mStatusBarWindow.mAttrs.layoutInDisplayCutoutMode =
+ LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ mStatusBarWindow.mAttrs.setFitInsetsTypes(0);
}
if (addAll || ArrayUtils.contains(requestedWindows, W_NOTIFICATION_SHADE)) {
mNotificationShadeWindow = createCommonWindow(null, TYPE_NOTIFICATION_SHADE,
@@ -292,14 +291,13 @@
}
if (addAll || ArrayUtils.contains(requestedWindows, W_NAVIGATION_BAR)) {
mNavBarWindow = createCommonWindow(null, TYPE_NAVIGATION_BAR, "mNavBarWindow");
- if (INSETS_LAYOUT_GENERALIZATION) {
- mNavBarWindow.mAttrs.height = NAV_BAR_HEIGHT;
- mNavBarWindow.mAttrs.gravity = Gravity.BOTTOM;
- mNavBarWindow.mAttrs.paramsForRotation = new WindowManager.LayoutParams[4];
- for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
- mNavBarWindow.mAttrs.paramsForRotation[rot] =
- getNavBarLayoutParamsForRotation(rot);
- }
+ mNavBarWindow.mAttrs.height = NAV_BAR_HEIGHT;
+ mNavBarWindow.mAttrs.gravity = Gravity.BOTTOM;
+ mNavBarWindow.mAttrs.paramsForRotation = new WindowManager.LayoutParams[4];
+ mNavBarWindow.mAttrs.setFitInsetsTypes(0);
+ for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
+ mNavBarWindow.mAttrs.paramsForRotation[rot] =
+ getNavBarLayoutParamsForRotation(rot);
}
}
if (addAll || ArrayUtils.contains(requestedWindows, W_DOCK_DIVIDER)) {
@@ -332,30 +330,27 @@
int width = WindowManager.LayoutParams.MATCH_PARENT;
int height = WindowManager.LayoutParams.MATCH_PARENT;
int gravity = Gravity.BOTTOM;
- if (INSETS_LAYOUT_GENERALIZATION) {
- switch (rotation) {
- case ROTATION_UNDEFINED:
- case Surface.ROTATION_0:
- case Surface.ROTATION_180:
- height = NAV_BAR_HEIGHT;
- break;
- case Surface.ROTATION_90:
- gravity = Gravity.RIGHT;
- width = NAV_BAR_HEIGHT;
- break;
- case Surface.ROTATION_270:
- gravity = Gravity.LEFT;
- width = NAV_BAR_HEIGHT;
- break;
- }
+ switch (rotation) {
+ case ROTATION_UNDEFINED:
+ case Surface.ROTATION_0:
+ case Surface.ROTATION_180:
+ height = NAV_BAR_HEIGHT;
+ break;
+ case Surface.ROTATION_90:
+ gravity = Gravity.RIGHT;
+ width = NAV_BAR_HEIGHT;
+ break;
+ case Surface.ROTATION_270:
+ gravity = Gravity.LEFT;
+ width = NAV_BAR_HEIGHT;
+ break;
}
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR);
lp.width = width;
lp.height = height;
- if (INSETS_LAYOUT_GENERALIZATION) {
- lp.gravity = gravity;
- }
+ lp.gravity = gravity;
+ lp.setFitInsetsTypes(0);
return lp;
}
@@ -799,7 +794,7 @@
TestTransitionPlayer registerTestTransitionPlayer() {
final TestTransitionPlayer testPlayer = new TestTransitionPlayer(
mAtm.getTransitionController(), mAtm.mWindowOrganizerController);
- testPlayer.mController.registerTransitionPlayer(testPlayer);
+ testPlayer.mController.registerTransitionPlayer(testPlayer, null /* appThread */);
return testPlayer;
}
@@ -875,6 +870,21 @@
mAtm.mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp = 1;
}
+ /** Mocks the behavior of taking a snapshot. */
+ void mockSurfaceFreezerSnapshot(SurfaceFreezer surfaceFreezer) {
+ final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
+ mock(SurfaceControl.ScreenshotHardwareBuffer.class);
+ final HardwareBuffer hardwareBuffer = mock(HardwareBuffer.class);
+ spyOn(surfaceFreezer);
+ doReturn(screenshotBuffer).when(surfaceFreezer)
+ .createSnapshotBufferInner(any(), any());
+ doReturn(null).when(surfaceFreezer)
+ .createFromHardwareBufferInner(any());
+ doReturn(hardwareBuffer).when(screenshotBuffer).getHardwareBuffer();
+ doReturn(100).when(hardwareBuffer).getWidth();
+ doReturn(100).when(hardwareBuffer).getHeight();
+ }
+
/**
* Builder for creating new activities.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index dfbdede..722aee7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -297,6 +297,7 @@
final WindowState appAboveImeTarget = createWindow("appAboveImeTarget");
mDisplayContent.setImeLayeringTarget(imeAppTarget);
+ mDisplayContent.setImeControlTarget(imeAppTarget);
mDisplayContent.assignChildLayers(mTransaction);
// Ime should be above all app windows except for non-fullscreen app window above it and
@@ -343,6 +344,7 @@
@Test
public void testAssignWindowLayers_ForStatusBarImeTarget() {
mDisplayContent.setImeLayeringTarget(mStatusBarWindow);
+ mDisplayContent.setImeControlTarget(mStatusBarWindow);
mDisplayContent.assignChildLayers(mTransaction);
assertWindowHigher(mImeWindow, mChildAppWindowAbove);
@@ -408,6 +410,7 @@
mAppWindow.mActivityRecord, "imeAppTarget");
mDisplayContent.setImeInputTarget(imeAppTarget);
mDisplayContent.setImeLayeringTarget(imeAppTarget);
+ mDisplayContent.setImeControlTarget(imeAppTarget);
mDisplayContent.updateImeParent();
// Simulate the ime layering target task is animating with recents animation.
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index b3b1491..0bd7b20 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -94,7 +94,8 @@
private static final String PROP_DISABLE_QUOTA = "fw.disable_quota";
private static final String PROP_VERIFY_STORAGE = "fw.verify_storage";
- private static final long DELAY_IN_MILLIS = 30 * DateUtils.SECOND_IN_MILLIS;
+ private static final long DELAY_CHECK_STORAGE_DELTA = 30 * DateUtils.SECOND_IN_MILLIS;
+ private static final long DELAY_RECALCULATE_QUOTAS = 10 * DateUtils.HOUR_IN_MILLIS;
private static final long DEFAULT_QUOTA = DataUnit.MEBIBYTES.toBytes(64);
public static class Lifecycle extends SystemService {
@@ -529,6 +530,7 @@
private class H extends Handler {
private static final int MSG_CHECK_STORAGE_DELTA = 100;
private static final int MSG_LOAD_CACHED_QUOTAS_FROM_FILE = 101;
+ private static final int MSG_RECALCULATE_QUOTAS = 102;
/**
* By only triggering a re-calculation after the storage has changed sizes, we can avoid
* recalculating quotas too often. Minimum change delta defines the percentage of change
@@ -568,7 +570,7 @@
recalculateQuotas(getInitializedStrategy());
notifySignificantDelta();
}
- sendEmptyMessageDelayed(MSG_CHECK_STORAGE_DELTA, DELAY_IN_MILLIS);
+ sendEmptyMessageDelayed(MSG_CHECK_STORAGE_DELTA, DELAY_CHECK_STORAGE_DELTA);
break;
}
case MSG_LOAD_CACHED_QUOTAS_FROM_FILE: {
@@ -588,7 +590,13 @@
mPreviousBytes = mStats.getAvailableBytes();
recalculateQuotas(strategy);
}
- sendEmptyMessageDelayed(MSG_CHECK_STORAGE_DELTA, DELAY_IN_MILLIS);
+ sendEmptyMessageDelayed(MSG_CHECK_STORAGE_DELTA, DELAY_CHECK_STORAGE_DELTA);
+ sendEmptyMessageDelayed(MSG_RECALCULATE_QUOTAS, DELAY_RECALCULATE_QUOTAS);
+ break;
+ }
+ case MSG_RECALCULATE_QUOTAS: {
+ recalculateQuotas(getInitializedStrategy());
+ sendEmptyMessageDelayed(MSG_RECALCULATE_QUOTAS, DELAY_RECALCULATE_QUOTAS);
break;
}
default:
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index ad042dd..cc33f88 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -799,8 +799,10 @@
* @param stats The {@link IntervalStats} object selected.
* @param mutable Whether or not the data inside the stats object is mutable.
* @param accumulatedResult The list to which to add extracted data.
+ * @return Whether or not to continue providing new stats to this combiner. If {@code false}
+ * is returned, then combine will no longer be called.
*/
- void combine(IntervalStats stats, boolean mutable, List<T> accumulatedResult);
+ boolean combine(IntervalStats stats, boolean mutable, List<T> accumulatedResult);
}
/**
@@ -863,8 +865,9 @@
try {
readLocked(f, stats);
- if (beginTime < stats.endTime) {
- combiner.combine(stats, false, results);
+ if (beginTime < stats.endTime
+ && !combiner.combine(stats, false, results)) {
+ break;
}
} catch (Exception e) {
Slog.e(TAG, "Failed to read usage stats file", e);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index f0ceff1..6dbf4c5 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -29,8 +29,10 @@
import static android.app.usage.UsageEvents.Event.USER_UNLOCKED;
import static android.app.usage.UsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY;
import static android.app.usage.UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY;
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import android.Manifest;
+import android.annotation.CurrentTimeMillisLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -84,6 +86,7 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
@@ -94,6 +97,7 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
+import com.android.server.utils.AlarmQueue;
import java.io.BufferedReader;
import java.io.BufferedWriter;
@@ -134,8 +138,15 @@
private static final long TEN_SECONDS = 10 * 1000;
private static final long TWENTY_MINUTES = 20 * 60 * 1000;
+ private static final long ONE_DAY = 24 * HOUR_IN_MILLIS;
+ private static final long ONE_WEEK = 7 * ONE_DAY;
private static final long FLUSH_INTERVAL = COMPRESS_TIME ? TEN_SECONDS : TWENTY_MINUTES;
static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds.
+ /**
+ * Used when we can't determine the next app launch time. Assume the app will get launched
+ * this amount of time in the future.
+ */
+ private static final long UNKNOWN_LAUNCH_TIME_DELAY_MS = 365 * ONE_DAY;
private static final boolean ENABLE_KERNEL_UPDATES = true;
private static final File KERNEL_COUNTER_FILE = new File("/proc/uid_procstat/set");
@@ -160,6 +171,9 @@
static final int MSG_UNLOCKED_USER = 5;
static final int MSG_PACKAGE_REMOVED = 6;
static final int MSG_ON_START = 7;
+ static final int MSG_HANDLE_LAUNCH_TIME_ON_USER_UNLOCK = 8;
+ static final int MSG_NOTIFY_ESTIMATED_LAUNCH_TIME_CHANGED = 9;
+ static final int MSG_NOTIFY_ESTIMATED_LAUNCH_TIMES_CHANGED = 10;
private final Object mLock = new Object();
Handler mHandler;
@@ -194,8 +208,12 @@
private final SparseArray<LinkedList<Event>> mReportedEvents = new SparseArray<>();
final SparseArray<ArraySet<String>> mUsageReporters = new SparseArray();
final SparseArray<ActivityData> mVisibleActivities = new SparseArray();
+ @GuardedBy("mLock")
+ private final SparseArray<LaunchTimeAlarmQueue> mLaunchTimeAlarmQueues = new SparseArray<>();
private final ArraySet<UsageStatsManagerInternal.UsageEventListener> mUsageEventListeners =
new ArraySet<>();
+ private final CopyOnWriteArraySet<UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener>
+ mEstimatedLaunchTimeChangedListeners = new CopyOnWriteArraySet<>();
private static class ActivityData {
private final String mTaskRootPackage;
@@ -369,6 +387,11 @@
}
mUserUnlockedStates.remove(userId);
mUserState.put(userId, null); // release the service (mainly for GC)
+ LaunchTimeAlarmQueue alarmQueue = mLaunchTimeAlarmQueues.get(userId);
+ if (alarmQueue != null) {
+ alarmQueue.removeAllAlarms();
+ mLaunchTimeAlarmQueues.remove(userId);
+ }
}
}
@@ -415,6 +438,8 @@
}
reportEvent(unlockEvent, userId);
+ mHandler.obtainMessage(MSG_HANDLE_LAUNCH_TIME_ON_USER_UNLOCK, userId, 0).sendToTarget();
+
// Remove all the stats stored in memory and in system DE.
mReportedEvents.remove(userId);
deleteRecursively(new File(Environment.getDataSystemDeDirectory(userId), "usagestats"));
@@ -437,6 +462,7 @@
* <br/>
* Note: DO NOT call this while holding the usage stats lock ({@code mLock}).
*/
+ @Nullable
private HashMap<String, Long> getInstalledPackages(int userId) {
if (mPackageManager == null) {
return null;
@@ -472,6 +498,33 @@
}
}
+ private class LaunchTimeAlarmQueue extends AlarmQueue<String> {
+ private final int mUserId;
+
+ LaunchTimeAlarmQueue(int userId, @NonNull Context context, @NonNull Looper looper) {
+ super(context, looper, "*usage.launchTime*", "Estimated launch times", true, 30_000L);
+ mUserId = userId;
+ }
+
+ @Override
+ protected boolean isForUser(@NonNull String key, int userId) {
+ return mUserId == userId;
+ }
+
+ @Override
+ protected void processExpiredAlarms(@NonNull ArraySet<String> expired) {
+ if (DEBUG) {
+ Slog.d(TAG, "Processing " + expired.size() + " expired alarms: "
+ + expired.toString());
+ }
+ if (expired.size() > 0) {
+ mHandler.obtainMessage(
+ MSG_NOTIFY_ESTIMATED_LAUNCH_TIMES_CHANGED, mUserId, 0, expired)
+ .sendToTarget();
+ }
+ }
+ }
+
private class UserActionsReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -522,6 +575,8 @@
@Override
public void onStatsReloaded() {
+ // This method ends up being called with the lock held, so we need to be careful how we
+ // call into other things.
mAppStandby.postOneTimeCheckIdleStates();
}
@@ -556,7 +611,7 @@
/**
* Obfuscate both {@link UsageEvents.Event#NOTIFICATION_SEEN} and
* {@link UsageEvents.Event#NOTIFICATION_INTERRUPTION} events if the provided calling uid does
- * not hold the {@link android.Manifest.permission.MANAGE_NOTIFICATIONS} permission.
+ * not hold the {@link android.Manifest.permission#MANAGE_NOTIFICATIONS} permission.
*/
private boolean shouldObfuscateNotificationEvents(int callingPid, int callingUid) {
if (callingUid == Process.SYSTEM_UID) {
@@ -953,6 +1008,23 @@
event.mTaskRootClass, usageSourcePackage);
resumedData.lastEvent = Event.ACTIVITY_RESUMED;
mVisibleActivities.put(event.mInstanceId, resumedData);
+ final long estimatedLaunchTime =
+ mAppStandby.getEstimatedLaunchTime(event.mPackage, userId);
+ final long now = System.currentTimeMillis();
+ if (estimatedLaunchTime < now || estimatedLaunchTime > now + ONE_WEEK) {
+ // If the estimated launch time is in the past or more than a week into
+ // the future, then we re-estimate a future launch time of less than a week
+ // from now, so notify listeners of an estimated launch time change.
+ // Clear the cached value.
+ if (DEBUG) {
+ Slog.d(TAG, event.getPackageName()
+ + " app launch resetting future launch estimate");
+ }
+ mAppStandby.setEstimatedLaunchTime(event.mPackage, userId, 0);
+ mHandler.obtainMessage(
+ MSG_NOTIFY_ESTIMATED_LAUNCH_TIME_CHANGED, userId, 0, event.mPackage)
+ .sendToTarget();
+ }
break;
case Event.ACTIVITY_PAUSED:
ActivityData pausedData = mVisibleActivities.get(event.mInstanceId);
@@ -1110,6 +1182,11 @@
Slog.i(TAG, "Removing user " + userId + " and all data.");
mUserState.remove(userId);
mAppTimeLimit.onUserRemoved(userId);
+ final LaunchTimeAlarmQueue alarmQueue = mLaunchTimeAlarmQueues.get(userId);
+ if (alarmQueue != null) {
+ alarmQueue.removeAllAlarms();
+ mLaunchTimeAlarmQueues.remove(userId);
+ }
}
mAppStandby.onUserRemoved(userId);
// Cancel any scheduled jobs for this user since the user is being removed.
@@ -1129,6 +1206,10 @@
// when the user service is initialized and package manager is queried.
return;
}
+ final LaunchTimeAlarmQueue alarmQueue = mLaunchTimeAlarmQueues.get(userId);
+ if (alarmQueue != null) {
+ alarmQueue.removeAlarmForKey(packageName);
+ }
final UserUsageStatsService userService = mUserState.get(userId);
if (userService == null) {
return;
@@ -1274,6 +1355,7 @@
/**
* Called by the Binder stub.
*/
+ @Nullable
UsageEvents queryEventsForPackage(int userId, long beginTime, long endTime,
String packageName, boolean includeTaskRoot) {
synchronized (mLock) {
@@ -1290,6 +1372,183 @@
}
}
+ @Nullable
+ private UsageEvents queryEarliestAppEvents(int userId, long beginTime, long endTime,
+ int eventType) {
+ synchronized (mLock) {
+ if (!mUserUnlockedStates.contains(userId)) {
+ Slog.w(TAG, "Failed to query earliest events for locked user " + userId);
+ return null;
+ }
+
+ final UserUsageStatsService service = getUserUsageStatsServiceLocked(userId);
+ if (service == null) {
+ return null; // user was stopped or removed
+ }
+ return service.queryEarliestAppEvents(beginTime, endTime, eventType);
+ }
+ }
+
+ @Nullable
+ private UsageEvents queryEarliestEventsForPackage(int userId, long beginTime, long endTime,
+ @NonNull String packageName, int eventType) {
+ synchronized (mLock) {
+ if (!mUserUnlockedStates.contains(userId)) {
+ Slog.w(TAG, "Failed to query earliset package events for locked user " + userId);
+ return null;
+ }
+
+ final UserUsageStatsService service = getUserUsageStatsServiceLocked(userId);
+ if (service == null) {
+ return null; // user was stopped or removed
+ }
+ return service.queryEarliestEventsForPackage(
+ beginTime, endTime, packageName, eventType);
+ }
+ }
+
+ @CurrentTimeMillisLong
+ long getEstimatedPackageLaunchTime(int userId, String packageName) {
+ long estimatedLaunchTime = mAppStandby.getEstimatedLaunchTime(packageName, userId);
+ final long now = System.currentTimeMillis();
+ if (estimatedLaunchTime < now || estimatedLaunchTime == Long.MAX_VALUE) {
+ estimatedLaunchTime = calculateEstimatedPackageLaunchTime(userId, packageName);
+ mAppStandby.setEstimatedLaunchTime(packageName, userId, estimatedLaunchTime);
+
+ synchronized (mLock) {
+ LaunchTimeAlarmQueue alarmQueue = mLaunchTimeAlarmQueues.get(userId);
+ if (alarmQueue == null) {
+ alarmQueue = new LaunchTimeAlarmQueue(
+ userId, getContext(), BackgroundThread.get().getLooper());
+ mLaunchTimeAlarmQueues.put(userId, alarmQueue);
+ }
+ alarmQueue.addAlarm(packageName,
+ SystemClock.elapsedRealtime() + (estimatedLaunchTime - now));
+ }
+ }
+ return estimatedLaunchTime;
+ }
+
+ @CurrentTimeMillisLong
+ private long calculateEstimatedPackageLaunchTime(int userId, String packageName) {
+ synchronized (mLock) {
+ final long endTime = System.currentTimeMillis();
+ final long beginTime = endTime - ONE_WEEK;
+ final long unknownTime = endTime + UNKNOWN_LAUNCH_TIME_DELAY_MS;
+ final UsageEvents events = queryEarliestEventsForPackage(
+ userId, beginTime, endTime, packageName, Event.ACTIVITY_RESUMED);
+ if (events == null) {
+ if (DEBUG) {
+ Slog.d(TAG, "No events for " + userId + ":" + packageName);
+ }
+ return unknownTime;
+ }
+ final UsageEvents.Event event = new UsageEvents.Event();
+ final boolean hasMoreThan24HoursOfHistory;
+ if (events.getNextEvent(event)) {
+ hasMoreThan24HoursOfHistory = endTime - event.getTimeStamp() > ONE_DAY;
+ if (DEBUG) {
+ Slog.d(TAG, userId + ":" + packageName + " history > 24 hours="
+ + hasMoreThan24HoursOfHistory);
+ }
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, userId + ":" + packageName + " has no events");
+ }
+ return unknownTime;
+ }
+ do {
+ if (event.getEventType() == Event.ACTIVITY_RESUMED) {
+ final long timestamp = event.getTimeStamp();
+ final long nextLaunch =
+ calculateNextLaunchTime(hasMoreThan24HoursOfHistory, timestamp);
+ if (nextLaunch > endTime) {
+ return nextLaunch;
+ }
+ }
+ } while (events.getNextEvent(event));
+ return unknownTime;
+ }
+ }
+
+ @CurrentTimeMillisLong
+ private static long calculateNextLaunchTime(
+ boolean hasMoreThan24HoursOfHistory, long eventTimestamp) {
+ // For our estimates, we assume the user opens an app at consistent times
+ // (ie. like clockwork).
+ // If the app has more than 24 hours of history, then we assume the user will
+ // reopen the app at the same time on a specific day.
+ // If the app has less than 24 hours of history (meaning it was likely just
+ // installed), then we assume the user will open it at exactly the same time
+ // on the following day.
+ if (hasMoreThan24HoursOfHistory) {
+ return eventTimestamp + ONE_WEEK;
+ } else {
+ return eventTimestamp + ONE_DAY;
+ }
+ }
+
+ private void handleEstimatedLaunchTimesOnUserUnlock(int userId) {
+ synchronized (mLock) {
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ final long now = System.currentTimeMillis();
+ final long beginTime = now - ONE_WEEK;
+ final UsageEvents events = queryEarliestAppEvents(
+ userId, beginTime, now, Event.ACTIVITY_RESUMED);
+ if (events == null) {
+ return;
+ }
+ final ArrayMap<String, Boolean> hasMoreThan24HoursOfHistory = new ArrayMap<>();
+ final UsageEvents.Event event = new UsageEvents.Event();
+ LaunchTimeAlarmQueue alarmQueue = mLaunchTimeAlarmQueues.get(userId);
+ if (alarmQueue == null) {
+ alarmQueue = new LaunchTimeAlarmQueue(
+ userId, getContext(), BackgroundThread.get().getLooper());
+ mLaunchTimeAlarmQueues.put(userId, alarmQueue);
+ }
+ final ArraySet<String> changedTimes = new ArraySet<>();
+ for (boolean unprocessedEvent = events.getNextEvent(event); unprocessedEvent;
+ unprocessedEvent = events.getNextEvent(event)) {
+ final String packageName = event.getPackageName();
+ if (!hasMoreThan24HoursOfHistory.containsKey(packageName)) {
+ boolean hasHistory = now - event.getTimeStamp() > ONE_DAY;
+ if (DEBUG) {
+ Slog.d(TAG,
+ userId + ":" + packageName + " history > 24 hours=" + hasHistory);
+ }
+ hasMoreThan24HoursOfHistory.put(packageName, hasHistory);
+ }
+ if (event.getEventType() == Event.ACTIVITY_RESUMED) {
+ long estimatedLaunchTime =
+ mAppStandby.getEstimatedLaunchTime(packageName, userId);
+ if (estimatedLaunchTime < now || estimatedLaunchTime == Long.MAX_VALUE) {
+ //noinspection ConstantConditions
+ estimatedLaunchTime = calculateNextLaunchTime(
+ hasMoreThan24HoursOfHistory.get(packageName), event.getTimeStamp());
+ mAppStandby.setEstimatedLaunchTime(
+ packageName, userId, estimatedLaunchTime);
+ }
+ if (estimatedLaunchTime < now + ONE_WEEK) {
+ // Before a user is unlocked, we don't know when the app will be launched,
+ // so we give callers the UNKNOWN time. Now that we have a better estimate,
+ // we should notify them of the change.
+ if (DEBUG) {
+ Slog.d(TAG, "User " + userId + " unlock resulting in"
+ + " estimated launch time change for " + packageName);
+ }
+ changedTimes.add(packageName);
+ }
+ alarmQueue.addAlarm(packageName, nowElapsed + (estimatedLaunchTime - now));
+ }
+ }
+ if (changedTimes.size() > 0) {
+ mHandler.obtainMessage(
+ MSG_NOTIFY_ESTIMATED_LAUNCH_TIMES_CHANGED, userId, 0, changedTimes)
+ .sendToTarget();
+ }
+ }
+ }
+
/**
* Called via the local interface.
*/
@@ -1309,6 +1568,22 @@
}
}
+ /**
+ * Called via the local interface.
+ */
+ private void registerLaunchTimeChangedListener(
+ @NonNull UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener listener) {
+ mEstimatedLaunchTimeChangedListeners.add(listener);
+ }
+
+ /**
+ * Called via the local interface.
+ */
+ private void unregisterLaunchTimeChangedListener(
+ @NonNull UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener listener) {
+ mEstimatedLaunchTimeChangedListeners.remove(listener);
+ }
+
private String buildFullToken(String packageName, String token) {
final StringBuilder sb = new StringBuilder(packageName.length() + token.length() + 1);
sb.append(packageName);
@@ -1564,6 +1839,44 @@
loadGlobalComponentUsageLocked();
}
break;
+ case MSG_HANDLE_LAUNCH_TIME_ON_USER_UNLOCK: {
+ final int userId = msg.arg1;
+ handleEstimatedLaunchTimesOnUserUnlock(userId);
+ }
+ break;
+ case MSG_NOTIFY_ESTIMATED_LAUNCH_TIME_CHANGED: {
+ final int userId = msg.arg1;
+ final String pkgName = (String) msg.obj;
+ final long nextEstimatedLaunchTime =
+ getEstimatedPackageLaunchTime(userId, pkgName);
+ if (DEBUG) {
+ Slog.d(TAG, "Notifying listener for " + userId + ":" + pkgName);
+ }
+ for (UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener listener :
+ mEstimatedLaunchTimeChangedListeners) {
+ listener.onEstimatedLaunchTimeChanged(
+ userId, pkgName, nextEstimatedLaunchTime);
+ }
+ }
+ break;
+ case MSG_NOTIFY_ESTIMATED_LAUNCH_TIMES_CHANGED: {
+ final int userId = msg.arg1;
+ final ArraySet<String> pkgNames = (ArraySet<String>) msg.obj;
+ if (DEBUG) {
+ Slog.d(TAG, "Notifying listeners for " + userId + "-->" + pkgNames);
+ }
+ for (int p = pkgNames.size() - 1; p >= 0; --p) {
+ final String pkgName = pkgNames.valueAt(p);
+ final long nextEstimatedLaunchTime =
+ getEstimatedPackageLaunchTime(userId, pkgName);
+ for (UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener listener :
+ mEstimatedLaunchTimeChangedListeners) {
+ listener.onEstimatedLaunchTimeChanged(
+ userId, pkgName, nextEstimatedLaunchTime);
+ }
+ }
+ }
+ break;
default:
super.handleMessage(msg);
break;
@@ -2463,6 +2776,11 @@
}
@Override
+ public long getEstimatedPackageLaunchTime(String packageName, int userId) {
+ return UsageStatsService.this.getEstimatedPackageLaunchTime(userId, packageName);
+ }
+
+ @Override
public long getTimeSinceLastJobRun(String packageName, int userId) {
return mAppStandby.getTimeSinceLastJobRun(packageName, userId);
}
@@ -2527,6 +2845,18 @@
public void unregisterListener(@NonNull UsageEventListener listener) {
UsageStatsService.this.unregisterListener(listener);
}
+
+ @Override
+ public void registerLaunchTimeChangedListener(
+ @NonNull EstimatedLaunchTimeChangedListener listener) {
+ UsageStatsService.this.registerLaunchTimeChangedListener(listener);
+ }
+
+ @Override
+ public void unregisterLaunchTimeChangedListener(
+ @NonNull EstimatedLaunchTimeChangedListener listener) {
+ UsageStatsService.this.unregisterLaunchTimeChangedListener(listener);
+ }
}
private class MyPackageMonitor extends PackageMonitor {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index c4a8e81..23694fc 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -29,6 +29,8 @@
import static android.app.usage.UsageStatsManager.INTERVAL_WEEKLY;
import static android.app.usage.UsageStatsManager.INTERVAL_YEARLY;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.usage.ConfigurationStats;
import android.app.usage.EventList;
import android.app.usage.EventStats;
@@ -365,43 +367,46 @@
private static final StatCombiner<UsageStats> sUsageStatsCombiner =
new StatCombiner<UsageStats>() {
@Override
- public void combine(IntervalStats stats, boolean mutable,
+ public boolean combine(IntervalStats stats, boolean mutable,
List<UsageStats> accResult) {
if (!mutable) {
accResult.addAll(stats.packageStats.values());
- return;
+ return true;
}
final int statCount = stats.packageStats.size();
for (int i = 0; i < statCount; i++) {
accResult.add(new UsageStats(stats.packageStats.valueAt(i)));
}
+ return true;
}
};
private static final StatCombiner<ConfigurationStats> sConfigStatsCombiner =
new StatCombiner<ConfigurationStats>() {
@Override
- public void combine(IntervalStats stats, boolean mutable,
+ public boolean combine(IntervalStats stats, boolean mutable,
List<ConfigurationStats> accResult) {
if (!mutable) {
accResult.addAll(stats.configurations.values());
- return;
+ return true;
}
final int configCount = stats.configurations.size();
for (int i = 0; i < configCount; i++) {
accResult.add(new ConfigurationStats(stats.configurations.valueAt(i)));
}
+ return true;
}
};
private static final StatCombiner<EventStats> sEventStatsCombiner =
new StatCombiner<EventStats>() {
@Override
- public void combine(IntervalStats stats, boolean mutable,
+ public boolean combine(IntervalStats stats, boolean mutable,
List<EventStats> accResult) {
stats.addEventStatsTo(accResult);
+ return true;
}
};
@@ -414,6 +419,7 @@
* and bucket, then calls the {@link com.android.server.usage.UsageStatsDatabase.StatCombiner}
* provided to select the stats to use from the IntervalStats object.
*/
+ @Nullable
private <T> List<T> queryStats(int intervalType, final long beginTime, final long endTime,
StatCombiner<T> combiner) {
if (intervalType == INTERVAL_BEST) {
@@ -510,16 +516,16 @@
List<Event> results = queryStats(INTERVAL_DAILY,
beginTime, endTime, new StatCombiner<Event>() {
@Override
- public void combine(IntervalStats stats, boolean mutable,
+ public boolean combine(IntervalStats stats, boolean mutable,
List<Event> accumulatedResult) {
final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
final int size = stats.events.size();
for (int i = startIndex; i < size; i++) {
- if (stats.events.get(i).mTimeStamp >= endTime) {
- return;
+ Event event = stats.events.get(i);
+ if (event.mTimeStamp >= endTime) {
+ return false;
}
- Event event = stats.events.get(i);
final int eventType = event.mEventType;
if (eventType == Event.SHORTCUT_INVOCATION
&& (flags & HIDE_SHORTCUT_EVENTS) == HIDE_SHORTCUT_EVENTS) {
@@ -552,6 +558,7 @@
}
accumulatedResult.add(event);
}
+ return true;
}
});
@@ -564,6 +571,60 @@
return new UsageEvents(results, table, true);
}
+ /**
+ * Returns a {@link UsageEvents} object whose events list contains only the earliest event seen
+ * for each app as well as the earliest event of {@code eventType} seen for each app.
+ */
+ @Nullable
+ UsageEvents queryEarliestAppEvents(final long beginTime, final long endTime,
+ final int eventType) {
+ if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
+ return null;
+ }
+ final ArraySet<String> names = new ArraySet<>();
+ final ArraySet<String> eventSuccess = new ArraySet<>();
+ final List<Event> results = queryStats(INTERVAL_DAILY,
+ beginTime, endTime, (stats, mutable, accumulatedResult) -> {
+ final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
+ final int size = stats.events.size();
+ for (int i = startIndex; i < size; i++) {
+ final Event event = stats.events.get(i);
+ if (event.getTimeStamp() >= endTime) {
+ return false;
+ }
+ if (event.getPackageName() == null) {
+ continue;
+ }
+ if (eventSuccess.contains(event.getPackageName())) {
+ continue;
+ }
+
+ final boolean firstEvent = names.add(event.getPackageName());
+
+ if (event.getEventType() == eventType) {
+ accumulatedResult.add(event);
+ eventSuccess.add(event.getPackageName());
+ } else if (firstEvent) {
+ // Save the earliest found event for the app, even if it doesn't match.
+ accumulatedResult.add(event);
+ }
+ }
+ return true;
+ });
+
+ if (results == null || results.isEmpty()) {
+ return null;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Found " + results.size() + " early events for " + names.size() + " apps");
+ }
+
+ String[] table = names.toArray(new String[names.size()]);
+ Arrays.sort(table);
+ return new UsageEvents(results, table, false);
+ }
+
+ @Nullable
UsageEvents queryEventsForPackage(final long beginTime, final long endTime,
final String packageName, boolean includeTaskRoot) {
if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
@@ -576,11 +637,11 @@
final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
final int size = stats.events.size();
for (int i = startIndex; i < size; i++) {
- if (stats.events.get(i).mTimeStamp >= endTime) {
- return;
+ final Event event = stats.events.get(i);
+ if (event.mTimeStamp >= endTime) {
+ return false;
}
- final Event event = stats.events.get(i);
if (!packageName.equals(event.mPackage)) {
continue;
}
@@ -595,6 +656,7 @@
}
accumulatedResult.add(event);
}
+ return true;
});
if (results == null || results.isEmpty()) {
@@ -606,6 +668,48 @@
return new UsageEvents(results, table, includeTaskRoot);
}
+ /**
+ * Returns a {@link UsageEvents} object whose events list contains only the earliest event seen
+ * for the package as well as the earliest event of {@code eventType} seen for the package.
+ */
+ @Nullable
+ UsageEvents queryEarliestEventsForPackage(final long beginTime, final long endTime,
+ @NonNull final String packageName, final int eventType) {
+ if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
+ return null;
+ }
+ final List<Event> results = queryStats(INTERVAL_DAILY,
+ beginTime, endTime, (stats, mutable, accumulatedResult) -> {
+ final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
+ final int size = stats.events.size();
+ for (int i = startIndex; i < size; i++) {
+ final Event event = stats.events.get(i);
+ if (event.getTimeStamp() >= endTime) {
+ return false;
+ }
+
+ if (!packageName.equals(event.getPackageName())) {
+ continue;
+ }
+ if (event.getEventType() == eventType) {
+ accumulatedResult.add(event);
+ // We've found the earliest of eventType. No need to keep going.
+ return false;
+ } else if (accumulatedResult.size() == 0) {
+ // Save the earliest found event, even if it doesn't match.
+ accumulatedResult.add(event);
+ }
+ }
+ return true;
+ });
+
+ if (results == null || results.isEmpty()) {
+ return null;
+ }
+
+ return new UsageEvents(results, new String[]{packageName}, false);
+ }
+
void persistActiveStats() {
if (mStatsChanged) {
Slog.i(TAG, mLogPrefix + "Flushing usage stats to disk");
@@ -876,7 +980,6 @@
return Long.toString(elapsedTime);
}
-
void printEvent(IndentingPrintWriter pw, Event event, boolean prettyDates) {
pw.printPair("time", formatDateTime(event.mTimeStamp, prettyDates));
pw.printPair("type", eventToString(event.mEventType));
@@ -925,13 +1028,13 @@
List<Event> events = queryStats(INTERVAL_DAILY,
beginTime, endTime, new StatCombiner<Event>() {
@Override
- public void combine(IntervalStats stats, boolean mutable,
+ public boolean combine(IntervalStats stats, boolean mutable,
List<Event> accumulatedResult) {
final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
final int size = stats.events.size();
for (int i = startIndex; i < size; i++) {
if (stats.events.get(i).mTimeStamp >= endTime) {
- return;
+ return false;
}
Event event = stats.events.get(i);
@@ -940,6 +1043,7 @@
}
accumulatedResult.add(event);
}
+ return true;
}
});
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 22eb50a..2141e42 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -970,6 +970,21 @@
= "carrier_use_ims_first_for_emergency_bool";
/**
+ * When {@code true}, the determination of whether to place a call as an emergency call will be
+ * based on the known {@link android.telephony.emergency.EmergencyNumber}s for the SIM on which
+ * the call is being placed. In a dual SIM scenario, if Sim A has the emergency numbers
+ * 123, 456 and Sim B has the emergency numbers 789, and the user places a call on SIM A to 789,
+ * it will not be treated as an emergency call in this case.
+ * When {@code false}, the determination is based on the emergency numbers from all device SIMs,
+ * regardless of which SIM the call is being placed on. If Sim A has the emergency numbers
+ * 123, 456 and Sim B has the emergency numbers 789, and the user places a call on SIM A to 789,
+ * the call will be dialed as an emergency number, but with an unspecified routing.
+ * @hide
+ */
+ public static final String KEY_USE_ONLY_DIALED_SIM_ECC_LIST_BOOL =
+ "use_only_dialed_sim_ecc_list_bool";
+
+ /**
* When IMS instant lettering is available for a carrier (see
* {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), determines the list of characters
* which may not be contained in messages. Should be specified as a regular expression suitable
@@ -5321,6 +5336,7 @@
sDefaults.putBoolean(KEY_CARRIER_IMS_GBA_REQUIRED_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL, true);
+ sDefaults.putBoolean(KEY_USE_ONLY_DIALED_SIM_ECC_LIST_BOOL, false);
sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING, "");
sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING, "");
sDefaults.putString(KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING, "");
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index 42d7707..a25abc9 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -119,7 +119,7 @@
throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
}
- return new ImsRcsManager(mContext, subscriptionId, sRcsCache);
+ return new ImsRcsManager(mContext, subscriptionId, sRcsCache, sTelephonyCache);
}
/**
@@ -157,7 +157,7 @@
throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
}
- return new SipDelegateManager(mContext, subscriptionId, sRcsCache);
+ return new SipDelegateManager(mContext, subscriptionId, sRcsCache, sTelephonyCache);
}
private static IImsRcsController getIImsRcsControllerInterface() {
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index d250088..95448c7 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -242,6 +242,11 @@
}
/**
+ * The physical cell ID which differentiates cells using the same radio channel.
+ *
+ * In GERAN, this value is the BSIC. The range is [0-63].
+ * Reference: 3GPP TS 3.03 section 4.2.2.
+ *
* In UTRAN, this value is primary scrambling code. The range is [0, 511].
* Reference: 3GPP TS 25.213 section 5.2.2.
*
diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
index 41e24dd..2ff4ac5 100644
--- a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
+++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
@@ -17,6 +17,8 @@
package android.telephony;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
@@ -66,10 +68,15 @@
private final IBinder mLiveToken;
private SignalStrengthUpdateRequest(
- @NonNull List<SignalThresholdInfo> signalThresholdInfos,
+ @Nullable List<SignalThresholdInfo> signalThresholdInfos,
boolean isReportingRequestedWhileIdle,
boolean isSystemThresholdReportingRequestedWhileIdle) {
- validate(signalThresholdInfos);
+ // System app (like Bluetooth) can specify the request to report system thresholds while
+ // device is idle (with permission protection). In this case, the request doesn't need to
+ // provide a non-empty list of SignalThresholdInfo which is only asked for public apps.
+ if (!isSystemThresholdReportingRequestedWhileIdle) {
+ validate(signalThresholdInfos);
+ }
mSignalThresholdInfos = signalThresholdInfos;
mIsReportingRequestedWhileIdle = isReportingRequestedWhileIdle;
@@ -128,13 +135,15 @@
/**
* Set the builder object if require reporting on the system thresholds when device is idle.
*
- * This can only used by the system caller.
+ * <p>This can only used by the system caller. Requires permission
+ * {@link android.Manifest.permission#LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH}.
*
* @param isSystemThresholdReportingRequestedWhileIdle true if request reporting on the
* system thresholds when device is idle
* @return the builder to facilitate the chaining
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
public @NonNull Builder setSystemThresholdReportingRequestedWhileIdle(
boolean isSystemThresholdReportingRequestedWhileIdle) {
mIsSystemThresholdReportingRequestedWhileIdle =
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 9c52220..0236306 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -12524,26 +12524,6 @@
}
/**
- * Enable or disable signal strength changes from radio will always be reported in any
- * condition (e.g. screen is off). This is only allowed for System caller.
- *
- * @param isEnabled {@code true} for enabling; {@code false} for disabling.
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public void setAlwaysReportSignalStrength(boolean isEnabled) {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null) {
- telephony.setAlwaysReportSignalStrength(getSubId(), isEnabled);
- }
- } catch (RemoteException ex) {
- Log.e(TAG, "setAlwaysReportSignalStrength RemoteException", ex);
- ex.rethrowAsRuntimeException();
- }
- }
-
- /**
* Get the most recently available signal strength information.
*
* Get the most recent SignalStrength information reported by the modem. Due
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 36082dc..7a27ed7 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -45,6 +45,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -1482,6 +1483,65 @@
}
}
+ /**
+ * Register a new callback, which is used to notify the registrant of changes to
+ * the state of the underlying IMS service that is attached to telephony to
+ * implement IMS functionality. If the manager is created for
+ * the {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID},
+ * this throws an {@link ImsException}.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE}
+ * or that the calling app has carrier privileges
+ * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param executor the Executor that will be used to call the {@link ImsStateCallback}.
+ * @param callback The callback instance being registered.
+ * @throws ImsException in the case that the callback can not be registered.
+ * See {@link ImsException#getCode} for more information on when this is called.
+ */
+ @RequiresPermission(anyOf = {Manifest.permission.READ_PRECISE_PHONE_STATE,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE})
+ public void registerImsStateCallback(@NonNull Executor executor,
+ @NonNull ImsStateCallback callback) throws ImsException {
+ Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+ Objects.requireNonNull(executor, "Must include a non-null Executor.");
+
+ callback.init(executor);
+ ITelephony telephony = mBinderCache.listenOnBinder(callback, callback::binderDied);
+ if (telephony == null) {
+ throw new ImsException("Telephony server is down",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ try {
+ telephony.registerImsStateCallback(
+ mSubId, ImsFeature.FEATURE_MMTEL, callback.getCallbackBinder());
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
+ } catch (RemoteException | IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Unregisters a previously registered callback.
+ *
+ * @param callback The callback instance to be unregistered.
+ */
+ public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) {
+ Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+
+ ITelephony telephony = mBinderCache.removeRunnable(callback);
+ try {
+ if (telephony != null) {
+ telephony.unregisterImsStateCallback(callback.getCallbackBinder());
+ }
+ } catch (RemoteException ignore) {
+ // ignore it
+ }
+ }
+
private ITelephony getITelephony() {
return mBinderCache.getBinder();
}
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 8d6fa41..42af025 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -39,9 +39,11 @@
import android.util.Log;
import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.ITelephony;
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -159,6 +161,7 @@
private final int mSubId;
private final Context mContext;
private final BinderCacheManager<IImsRcsController> mBinderCache;
+ private final BinderCacheManager<ITelephony> mTelephonyBinderCache;
private final Map<OnAvailabilityChangedListener, AvailabilityCallbackAdapter>
mAvailabilityChangedCallbacks;
@@ -167,11 +170,13 @@
* @hide
*/
public ImsRcsManager(Context context, int subId,
- BinderCacheManager<IImsRcsController> binderCache) {
+ BinderCacheManager<IImsRcsController> binderCache,
+ BinderCacheManager<ITelephony> telephonyBinderCache) {
mSubId = subId;
mContext = context;
mBinderCache = binderCache;
mAvailabilityChangedCallbacks = new HashMap<>();
+ mTelephonyBinderCache = telephonyBinderCache;
}
/**
@@ -534,6 +539,66 @@
}
/**
+ * Register a new callback, which is used to notify the registrant of changes to
+ * the state of the underlying IMS service that is attached to telephony to
+ * implement IMS functionality. If the manager is created for
+ * the {@link android.telephony.SubscriptionManager#DEFAULT_SUBSCRIPTION_ID},
+ * this throws an {@link ImsException}.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE}
+ * or that the calling app has carrier privileges
+ * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param executor the Executor that will be used to call the {@link ImsStateCallback}.
+ * @param callback The callback instance being registered.
+ * @throws ImsException in the case that the callback can not be registered.
+ * See {@link ImsException#getCode} for more information on when this is called.
+ */
+ @RequiresPermission(anyOf = {Manifest.permission.READ_PRECISE_PHONE_STATE,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE})
+ public void registerImsStateCallback(@NonNull Executor executor,
+ @NonNull ImsStateCallback callback) throws ImsException {
+ Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+ Objects.requireNonNull(executor, "Must include a non-null Executor.");
+
+ callback.init(executor);
+ ITelephony telephony = mTelephonyBinderCache.listenOnBinder(callback, callback::binderDied);
+ if (telephony == null) {
+ throw new ImsException("Telephony server is down",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ try {
+ telephony.registerImsStateCallback(
+ mSubId, ImsFeature.FEATURE_RCS, callback.getCallbackBinder());
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
+ } catch (RemoteException | IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Unregisters a previously registered callback.
+ *
+ * @param callback The callback instance to be unregistered.
+ */
+ public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) {
+ Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+
+ ITelephony telephony = mTelephonyBinderCache.removeRunnable(callback);
+ try {
+ if (telephony != null) {
+ telephony.unregisterImsStateCallback(callback.getCallbackBinder());
+ }
+ } catch (RemoteException ignore) {
+ // ignore it
+ }
+ }
+
+ /**
* Add the {@link OnAvailabilityChangedListener} to collection for tracking.
* @param executor The executor that will be used when the publish state is changed and the
* {@link OnAvailabilityChangedListener} is called.
diff --git a/telephony/java/android/telephony/ims/ImsStateCallback.java b/telephony/java/android/telephony/ims/ImsStateCallback.java
new file mode 100644
index 0000000..b9ba93f
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsStateCallback.java
@@ -0,0 +1,184 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Binder;
+
+import com.android.internal.telephony.IImsStateCallback;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.concurrent.Executor;
+
+/**
+ * A callback class used for monitoring changes in IMS service connection states
+ * for a specific subscription.
+ * <p>
+ * @see ImsMmTelManager#registerImsStateCallback(Executor, ImsStateCallback)
+ * @see ImsRcsManager#registerImsStateCallback(Executor, ImsStateCallback)
+ */
+public abstract class ImsStateCallback {
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "REASON_", value = {
+ REASON_UNKNOWN_TEMPORARY_ERROR,
+ REASON_UNKNOWN_PERMANENT_ERROR,
+ REASON_IMS_SERVICE_DISCONNECTED,
+ REASON_NO_IMS_SERVICE_CONFIGURED,
+ REASON_SUBSCRIPTION_INACTIVE,
+ REASON_IMS_SERVICE_NOT_READY
+ })
+ public @interface DisconnectedReason {}
+
+ /**
+ * The underlying IMS service is temporarily unavailable for the
+ * associated subscription.
+ * {@link #onAvailable} will be called when the IMS service becomes
+ * available again.
+ */
+ public static final int REASON_UNKNOWN_TEMPORARY_ERROR = 1;
+
+ /**
+ * The underlying IMS service is permanently unavailable for the
+ * associated subscription and there will be no Manager available for
+ * this subscription.
+ */
+ public static final int REASON_UNKNOWN_PERMANENT_ERROR = 2;
+
+ /**
+ * The underlying IMS service has died, is reconfiguring, or has never
+ * come up yet and as a result is currently unavailable.
+ * {@link #onAvailable} will be called when the IMS service becomes
+ * available. All callbacks should be unregistered now and registered again
+ * if the IMS service moves back to available.
+ */
+ public static final int REASON_IMS_SERVICE_DISCONNECTED = 3;
+
+ /**
+ * There is no IMS service configured for the subscription ID specified.
+ * This is a permanent error and there will be no Manager available for
+ * this subscription.
+ */
+ public static final int REASON_NO_IMS_SERVICE_CONFIGURED = 4;
+
+ /**
+ * The subscription associated with this Manager has moved to an inactive
+ * state (e.g. SIM removed) and the IMS service has torn down the resources
+ * related to this subscription. This has caused this callback
+ * to be deregistered. The callback must be re-registered when this subscription
+ * becomes active in order to continue listening to the IMS service state.
+ */
+ public static final int REASON_SUBSCRIPTION_INACTIVE = 5;
+
+ /**
+ * The IMS service is connected, but in a NOT_READY state. Once the
+ * service moves to ready, {@link #onAvailable} will be called.
+ */
+ public static final int REASON_IMS_SERVICE_NOT_READY = 6;
+
+ private IImsStateCallbackStub mCallback;
+
+ /**
+ * @hide
+ */
+ public void init(@NonNull @CallbackExecutor Executor executor) {
+ if (executor == null) {
+ throw new IllegalArgumentException("ImsStateCallback Executor must be non-null");
+ }
+ mCallback = new IImsStateCallbackStub(this, executor);
+ }
+
+ /**
+ * Using a static class and weak reference here to avoid memory leak caused by the
+ * IImsStateCallback.Stub callback retaining references to the outside ImsStateCallback.
+ */
+ private static class IImsStateCallbackStub extends IImsStateCallback.Stub {
+ private WeakReference<ImsStateCallback> mImsStateCallbackWeakRef;
+ private Executor mExecutor;
+
+ IImsStateCallbackStub(ImsStateCallback imsStateCallback, Executor executor) {
+ mImsStateCallbackWeakRef = new WeakReference<ImsStateCallback>(imsStateCallback);
+ mExecutor = executor;
+ }
+
+ Executor getExecutor() {
+ return mExecutor;
+ }
+
+ public void onAvailable() {
+ ImsStateCallback callback = mImsStateCallbackWeakRef.get();
+ if (callback == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> callback.onAvailable()));
+ }
+
+ public void onUnavailable(int reason) {
+ ImsStateCallback callback = mImsStateCallbackWeakRef.get();
+ if (callback == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> callback.onUnavailable(reason)));
+ }
+ }
+
+ /**
+ * The IMS service has disconnected or is reporting NOT_READY and is no longer
+ * available to users. The user should clean up all related state and
+ * unregister callbacks. If it is a temporary error, {@link #onAvailable} will
+ * be called when the IMS service becomes available again.
+ *
+ * @param reason the specified reason
+ */
+ public abstract void onUnavailable(@DisconnectedReason int reason);
+
+ /**
+ * The IMS service is connected and is ready for communication over the
+ * provided Manager.
+ */
+ public abstract void onAvailable();
+
+ /**
+ * An unexpected error has occurred and the Telephony process has crashed. This
+ * has caused this callback to be deregistered. The callback must be
+ * re-registered in order to continue listening to the IMS service state.
+ */
+ public abstract void onError();
+
+ /**
+ * The callback to notify the death of telephony process
+ * @hide
+ */
+ public final void binderDied() {
+ if (mCallback != null) {
+ mCallback.getExecutor().execute(() -> onError());
+ }
+ }
+
+ /**
+ * Return the callback binder
+ * @hide
+ */
+ public IImsStateCallbackStub getCallbackBinder() {
+ return mCallback;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index 5a80663..48e3d45 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -28,15 +28,16 @@
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.telephony.BinderCacheManager;
-import android.telephony.CarrierConfigManager;
import android.telephony.ims.aidl.IImsRcsController;
import android.telephony.ims.aidl.SipDelegateConnectionAidlWrapper;
+import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.stub.DelegateConnectionMessageCallback;
import android.telephony.ims.stub.DelegateConnectionStateCallback;
import android.telephony.ims.stub.SipDelegate;
import android.util.ArrayMap;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.ITelephony;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -282,6 +283,7 @@
private final Context mContext;
private final int mSubId;
private final BinderCacheManager<IImsRcsController> mBinderCache;
+ private final BinderCacheManager<ITelephony> mTelephonyBinderCache;
/**
* Only visible for testing. To instantiate an instance of this class, please use
@@ -290,10 +292,12 @@
*/
@VisibleForTesting
public SipDelegateManager(Context context, int subId,
- BinderCacheManager<IImsRcsController> binderCache) {
+ BinderCacheManager<IImsRcsController> binderCache,
+ BinderCacheManager<ITelephony> telephonyBinderCache) {
mContext = context;
mSubId = subId;
mBinderCache = binderCache;
+ mTelephonyBinderCache = telephonyBinderCache;
}
/**
@@ -446,4 +450,64 @@
+ " into this method");
}
}
+
+ /**
+ * Register a new callback, which is used to notify the registrant of changes to
+ * the state of the underlying IMS service that is attached to telephony to
+ * implement IMS functionality. If the manager is created for
+ * the {@link android.telephony.SubscriptionManager#DEFAULT_SUBSCRIPTION_ID},
+ * this throws an {@link ImsException}.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE}
+ * or that the calling app has carrier privileges
+ * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param executor the Executor that will be used to call the {@link ImsStateCallback}.
+ * @param callback The callback instance being registered.
+ * @throws ImsException in the case that the callback can not be registered.
+ * See {@link ImsException#getCode} for more information on when this is called.
+ */
+ @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
+ public void registerImsStateCallback(@NonNull Executor executor,
+ @NonNull ImsStateCallback callback) throws ImsException {
+ Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+ Objects.requireNonNull(executor, "Must include a non-null Executor.");
+
+ callback.init(executor);
+ ITelephony telephony = mTelephonyBinderCache.listenOnBinder(callback, callback::binderDied);
+ if (telephony == null) {
+ throw new ImsException("Telephony server is down",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ try {
+ telephony.registerImsStateCallback(
+ mSubId, ImsFeature.FEATURE_RCS, callback.getCallbackBinder());
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
+ } catch (RemoteException | IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Unregisters a previously registered callback.
+ *
+ * @param callback The callback instance to be unregistered.
+ */
+ public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) {
+ Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+
+ ITelephony telephony = mTelephonyBinderCache.removeRunnable(callback);
+
+ try {
+ if (telephony != null) {
+ telephony.unregisterImsStateCallback(callback.getCallbackBinder());
+ }
+ } catch (RemoteException ignore) {
+ // ignore it
+ }
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/IImsStateCallback.aidl b/telephony/java/com/android/internal/telephony/IImsStateCallback.aidl
new file mode 100644
index 0000000..e04b01d
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/IImsStateCallback.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.internal.telephony;
+
+oneway interface IImsStateCallback {
+ void onUnavailable(int reason);
+ void onAvailable();
+}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index d99fb85..b3c5d042 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -67,6 +67,7 @@
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.IBooleanConsumer;
import com.android.internal.telephony.ICallForwardingInfoCallback;
+import com.android.internal.telephony.IImsStateCallback;
import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.INumberVerificationCallback;
import com.android.internal.telephony.OperatorInfo;
@@ -1002,11 +1003,6 @@
boolean isManualNetworkSelectionAllowed(int subId);
/**
- * Enable or disable always reporting signal strength changes from radio.
- */
- void setAlwaysReportSignalStrength(int subId, boolean isEnable);
-
- /**
* Get P-CSCF address from PCO after data connection is established or modified.
* @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN
* @param callingPackage The package making the call.
@@ -2497,4 +2493,14 @@
* NSSAIs (configured, allowed and rejected).
*/
void getSlicingConfig(in ResultReceiver callback);
+
+ /**
+ * Register an IMS connection state callback
+ */
+ void registerImsStateCallback(int subId, int feature, in IImsStateCallback cb);
+
+ /**
+ * Unregister an IMS connection state callback
+ */
+ void unregisterImsStateCallback(in IImsStateCallback cb);
}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 269c575..22fe424 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -437,7 +437,7 @@
</activity>
<activity android:name=".PenStylusActivity"
- android:label="Pen (BUGGED)/Draw"
+ android:label="Pen/Draw"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
diff --git a/tests/HwAccelerationTest/jni/native-lib.cpp b/tests/HwAccelerationTest/jni/native-lib.cpp
index d6b1c49..407d4bf 100644
--- a/tests/HwAccelerationTest/jni/native-lib.cpp
+++ b/tests/HwAccelerationTest/jni/native-lib.cpp
@@ -41,7 +41,7 @@
};
extern "C" JNIEXPORT jlong JNICALL
-Java_com_android_test_hwui_FrontBufferedLayer_nCreate(JNIEnv* env, jobject jSurface) {
+Java_com_android_test_hwui_FrontBufferedLayer_nCreate(JNIEnv* env, jclass, jobject jSurface) {
ANativeWindow* window = ANativeWindow_fromSurface(env, jSurface);
MyWrapper* wrapper = new MyWrapper(window);
ANativeWindow_release(window);
@@ -49,13 +49,13 @@
}
extern "C" JNIEXPORT void JNICALL
-Java_com_android_test_hwui_FrontBufferedLayer_nDestroy(jlong ptr) {
+Java_com_android_test_hwui_FrontBufferedLayer_nDestroy(JNIEnv*, jclass, jlong ptr) {
MyWrapper* wrapper = reinterpret_cast<MyWrapper*>(ptr);
delete wrapper;
}
extern "C" JNIEXPORT void JNICALL Java_com_android_test_hwui_FrontBufferedLayer_nUpdateBuffer(
- JNIEnv* env, jlong ptr, jobject jbuffer) {
+ JNIEnv* env, jclass, jlong ptr, jobject jbuffer) {
MyWrapper* wrapper = reinterpret_cast<MyWrapper*>(ptr);
AHardwareBuffer* buffer = AHardwareBuffer_fromHardwareBuffer(env, jbuffer);
wrapper->setBuffer(buffer);
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index ae62bec..7906f09 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -31,6 +31,7 @@
],
test_suites: ["general-tests"],
java_resources: [
+ ":apex.apexd_test_classpath",
":com.android.apex.apkrollback.test_v2",
":StagedInstallTestApexV2",
":StagedInstallTestApexV2_WrongSha",
@@ -54,6 +55,7 @@
"cts-install-lib-host",
],
data: [
+ ":apex.apexd_test",
":com.android.apex.apkrollback.test_v1",
":StagedInstallTestApexV2",
":StagedInstallTestApexV2_WrongSha",
diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
index 6e24448..e4e535d 100644
--- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
@@ -74,6 +74,11 @@
"ApexV2", SHIM_APEX_PACKAGE_NAME, 2, /* isApex= */ true,
"com.android.apex.cts.shim.v2.apex");
+ private static final String TEST_APEX_PACKAGE_NAME = "com.android.apex.test_package";
+ private static final TestApp TEST_APEX_CLASSPATH = new TestApp("TestApex",
+ TEST_APEX_PACKAGE_NAME, 1, /*isApex=*/true,
+ "apex.apexd_test_classpath.apex");
+
private File mTestStateFile = new File(
InstrumentationRegistry.getInstrumentation().getContext().getFilesDir(),
"stagedinstall_state");
@@ -439,11 +444,13 @@
StagedApexInfo result = getPackageManagerNative().getStagedApexInfo("not found");
assertThat(result).isNull();
// Stage an apex
- int sessionId = Install.single(APEX_V2).setStaged().commit();
+ int sessionId = Install.single(TEST_APEX_CLASSPATH).setStaged().commit();
waitForSessionReady(sessionId);
// Query proper module name
- result = getPackageManagerNative().getStagedApexInfo(SHIM_APEX_PACKAGE_NAME);
- assertThat(result.moduleName).isEqualTo(SHIM_APEX_PACKAGE_NAME);
+ result = getPackageManagerNative().getStagedApexInfo(TEST_APEX_PACKAGE_NAME);
+ assertThat(result.moduleName).isEqualTo(TEST_APEX_PACKAGE_NAME);
+ assertThat(result.hasBootClassPathJars).isTrue();
+ assertThat(result.hasSystemServerClassPathJars).isTrue();
InstallUtils.openPackageInstallerSession(sessionId).abandon();
}
diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
index 8d696f5..78cf9ac 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -61,6 +61,7 @@
private static final String APEX_WRONG_SHA = "com.android.apex.cts.shim.v2_wrong_sha.apex";
private static final String APK_A = "TestAppAv1.apk";
private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test";
+ private static final String APEXD_TEST_APEX = "apex.apexd_test.apex";
private static final String TEST_VENDOR_APEX_ALLOW_LIST =
"/vendor/etc/sysconfig/test-vendor-apex-allow-list.xml";
@@ -480,16 +481,29 @@
@Test
public void testGetStagedModuleNames() throws Exception {
+ assumeTrue("Device does not support updating APEX",
+ mHostUtils.isApexUpdateSupported());
+
runPhase("testGetStagedModuleNames");
}
@Test
+ @LargeTest
public void testGetStagedApexInfo() throws Exception {
+ assumeTrue("Device does not support updating APEX",
+ mHostUtils.isApexUpdateSupported());
+
+ pushTestApex(APEXD_TEST_APEX);
+ getDevice().reboot();
+
runPhase("testGetStagedApexInfo");
}
@Test
public void testStagedApexObserver() throws Exception {
+ assumeTrue("Device does not support updating APEX",
+ mHostUtils.isApexUpdateSupported());
+
runPhase("testStagedApexObserver");
}
diff --git a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
index 7e8a134..f695cbd 100644
--- a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
+++ b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
@@ -62,12 +62,13 @@
private static final StatCombiner<UsageEvents.Event> sUsageStatsCombiner =
new StatCombiner<UsageEvents.Event>() {
@Override
- public void combine(IntervalStats stats, boolean mutable,
+ public boolean combine(IntervalStats stats, boolean mutable,
List<UsageEvents.Event> accResult) {
final int size = stats.events.size();
for (int i = 0; i < size; i++) {
accResult.add(stats.events.get(i));
}
+ return true;
}
};
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
index f385113..3b201f9 100644
--- a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
@@ -135,11 +135,12 @@
}
@Test
- public void testEncodeRecodeParamsWithIkeOptions() throws Exception {
+ public void testEncodeDecodeParamsWithIkeOptions() throws Exception {
final IkeSessionParams params =
createBuilderMinimum()
.addIkeOption(IkeSessionParams.IKE_OPTION_ACCEPT_ANY_REMOTE_ID)
.addIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE)
+ .addIkeOption(IkeSessionParams.IKE_OPTION_INITIAL_CONTACT)
.build();
verifyPersistableBundleEncodeDecodeIsLossless(params);
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index c9a8947a..937f9dc 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -297,8 +297,6 @@
any(),
any());
verify(mNetworkAgent).register();
- verify(mNetworkAgent)
- .setUnderlyingNetworks(eq(singletonList(TEST_UNDERLYING_NETWORK_RECORD_1.network)));
verify(mNetworkAgent).markConnected();
verify(mIpSecSvc)
@@ -313,6 +311,7 @@
final NetworkCapabilities nc = ncCaptor.getValue();
assertTrue(nc.hasTransport(TRANSPORT_CELLULAR));
assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+ assertEquals(List.of(TEST_UNDERLYING_NETWORK_RECORD_1.network), nc.getUnderlyingNetworks());
for (int cap : mConfig.getAllExposedCapabilities()) {
assertTrue(nc.hasCapability(cap));
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index a700171..5253c3e 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -61,6 +61,7 @@
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -116,8 +117,9 @@
capBuilder.setLinkUpstreamBandwidthKbps(TEST_UPSTREAM_BANDWIDTH);
capBuilder.setLinkDownstreamBandwidthKbps(TEST_DOWNSTREAM_BANDWIDTH);
capBuilder.setAdministratorUids(new int[] {TEST_UID});
+ final Network underlyingNetwork = mock(Network.class, CALLS_REAL_METHODS);
UnderlyingNetworkRecord record = new UnderlyingNetworkRecord(
- mock(Network.class, CALLS_REAL_METHODS),
+ underlyingNetwork,
capBuilder.build(), new LinkProperties(), false);
final NetworkCapabilities vcnCaps =
VcnGatewayConnection.buildNetworkCapabilities(
@@ -128,6 +130,7 @@
assertTrue(vcnCaps.hasTransport(TRANSPORT_CELLULAR));
assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_METERED));
assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+ assertTrue(vcnCaps.getUnderlyingNetworks().equals(List.of(underlyingNetwork)));
for (int cap : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
if (cap == NET_CAPABILITY_INTERNET || cap == NET_CAPABILITY_DUN) {
diff --git a/tools/aapt/SdkConstants.h b/tools/aapt/SdkConstants.h
index 857b25e..f57c4ee 100644
--- a/tools/aapt/SdkConstants.h
+++ b/tools/aapt/SdkConstants.h
@@ -47,6 +47,7 @@
SDK_Q = 29,
SDK_R = 30,
SDK_S = 31,
+ SDK_S_V2 = 32,
SDK_CUR_DEVELOPMENT = 10000,
};
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index bd0a4bc..740b44e 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -170,6 +170,16 @@
}
// ==========================================================
+// Build the host shared library: aapt2_jni
+// ==========================================================
+cc_library_host_shared {
+ name: "libaapt2_jni",
+ srcs: toolSources + ["jni/aapt2_jni.cpp"],
+ static_libs: ["libaapt2"],
+ defaults: ["aapt2_defaults"],
+}
+
+// ==========================================================
// Build the host tests: aapt2_tests
// ==========================================================
cc_test_host {
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index 0bbde62..8ea43abf 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -59,6 +59,7 @@
{0x0606, SDK_Q},
{0x0616, SDK_R},
{0x064b, SDK_S},
+ {0x064c, SDK_S_V2},
};
static bool less_entry_id(const std::pair<uint16_t, ApiVersion>& p, uint16_t entryId) {
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index 384346b..f2aaaf5 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -57,6 +57,7 @@
SDK_Q = 29,
SDK_R = 30,
SDK_S = 31,
+ SDK_S_V2 = 32,
SDK_CUR_DEVELOPMENT = 10000,
};
diff --git a/tools/aapt2/jni/ScopedUtfChars.h b/tools/aapt2/jni/ScopedUtfChars.h
new file mode 100644
index 0000000..a8c4b13
--- /dev/null
+++ b/tools/aapt2/jni/ScopedUtfChars.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef SCOPED_UTF_CHARS_H_included
+#define SCOPED_UTF_CHARS_H_included
+
+#include <string.h>
+#include <jni.h>
+
+#include "android-base/logging.h"
+
+// This file was copied with some minor modifications from libnativehelper.
+// As soon as libnativehelper can be compiled for Windows, this file should be
+// replaced with libnativehelper's implementation.
+class ScopedUtfChars {
+ public:
+ ScopedUtfChars(JNIEnv* env, jstring s) : env_(env), string_(s) {
+ CHECK(s != nullptr);
+ utf_chars_ = env->GetStringUTFChars(s, nullptr);
+ }
+
+ ScopedUtfChars(ScopedUtfChars&& rhs) :
+ env_(rhs.env_), string_(rhs.string_), utf_chars_(rhs.utf_chars_) {
+ rhs.env_ = nullptr;
+ rhs.string_ = nullptr;
+ rhs.utf_chars_ = nullptr;
+ }
+
+ ~ScopedUtfChars() {
+ if (utf_chars_) {
+ env_->ReleaseStringUTFChars(string_, utf_chars_);
+ }
+ }
+
+ ScopedUtfChars& operator=(ScopedUtfChars&& rhs) {
+ if (this != &rhs) {
+ // Delete the currently owned UTF chars.
+ this->~ScopedUtfChars();
+
+ // Move the rhs ScopedUtfChars and zero it out.
+ env_ = rhs.env_;
+ string_ = rhs.string_;
+ utf_chars_ = rhs.utf_chars_;
+ rhs.env_ = nullptr;
+ rhs.string_ = nullptr;
+ rhs.utf_chars_ = nullptr;
+ }
+ return *this;
+ }
+
+ const char* c_str() const {
+ return utf_chars_;
+ }
+
+ size_t size() const {
+ return strlen(utf_chars_);
+ }
+
+ const char& operator[](size_t n) const {
+ return utf_chars_[n];
+ }
+
+ private:
+ JNIEnv* env_;
+ jstring string_;
+ const char* utf_chars_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedUtfChars);
+};
+
+#endif // SCOPED_UTF_CHARS_H_included
diff --git a/tools/aapt2/jni/aapt2_jni.cpp b/tools/aapt2/jni/aapt2_jni.cpp
new file mode 100644
index 0000000..ec3c543
--- /dev/null
+++ b/tools/aapt2/jni/aapt2_jni.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include "com_android_tools_aapt2_Aapt2Jni.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "ScopedUtfChars.h"
+
+#include "Diagnostics.h"
+#include "cmd/Compile.h"
+#include "cmd/Link.h"
+#include "util/Util.h"
+
+using android::StringPiece;
+
+/*
+ * Converts a java List<String> into C++ vector<ScopedUtfChars>.
+ */
+static std::vector<ScopedUtfChars> list_to_utfchars(JNIEnv *env, jobject obj) {
+ std::vector<ScopedUtfChars> converted;
+
+ // Call size() method on the list to know how many elements there are.
+ jclass list_cls = env->GetObjectClass(obj);
+ jmethodID size_method_id = env->GetMethodID(list_cls, "size", "()I");
+ CHECK(size_method_id != 0);
+ jint size = env->CallIntMethod(obj, size_method_id);
+ CHECK(size >= 0);
+
+ // Now, iterate all strings in the list
+ // (note: generic erasure means get() return an Object)
+ jmethodID get_method_id = env->GetMethodID(list_cls, "get", "(I)Ljava/lang/Object;");
+ CHECK(get_method_id != 0);
+ for (jint i = 0; i < size; i++) {
+ // Call get(i) to get the string in the ith position.
+ jobject string_obj_uncast = env->CallObjectMethod(obj, get_method_id, i);
+ CHECK(string_obj_uncast != nullptr);
+ jstring string_obj = static_cast<jstring>(string_obj_uncast);
+ converted.push_back(ScopedUtfChars(env, string_obj));
+ }
+
+ return converted;
+}
+
+/*
+ * Extracts all StringPiece from the ScopedUtfChars instances.
+ *
+ * The returned pieces can only be used while the original ones have not been
+ * destroyed.
+ */
+static std::vector<StringPiece> extract_pieces(const std::vector<ScopedUtfChars> &strings) {
+ std::vector<StringPiece> pieces;
+
+ std::for_each(
+ strings.begin(), strings.end(),
+ [&pieces](const ScopedUtfChars &p) { pieces.push_back(p.c_str()); });
+
+ return pieces;
+}
+
+class JniDiagnostics : public aapt::IDiagnostics {
+ public:
+ JniDiagnostics(JNIEnv* env, jobject diagnostics_obj)
+ : env_(env), diagnostics_obj_(diagnostics_obj) {
+ mid_ = NULL;
+ }
+
+ void Log(Level level, aapt::DiagMessageActual& actual_msg) override {
+ jint level_value;
+ switch (level) {
+ case Level::Error:
+ level_value = 3;
+ break;
+
+ case Level::Warn:
+ level_value = 2;
+ break;
+
+ case Level::Note:
+ level_value = 1;
+ break;
+ }
+ jstring message = env_->NewStringUTF(actual_msg.message.c_str());
+ jstring path = env_->NewStringUTF(actual_msg.source.path.c_str());
+ jlong line = -1;
+ if (actual_msg.source.line) {
+ line = actual_msg.source.line.value();
+ }
+ if (!mid_) {
+ jclass diagnostics_cls = env_->GetObjectClass(diagnostics_obj_);
+ mid_ = env_->GetMethodID(diagnostics_cls, "log", "(ILjava/lang/String;JLjava/lang/String;)V");
+ }
+ env_->CallVoidMethod(diagnostics_obj_, mid_, level_value, path, line, message);
+ }
+
+ private:
+ JNIEnv* env_;
+ jobject diagnostics_obj_;
+ jmethodID mid_;
+ DISALLOW_COPY_AND_ASSIGN(JniDiagnostics);
+};
+
+JNIEXPORT jint JNICALL Java_com_android_tools_aapt2_Aapt2Jni_nativeCompile(
+ JNIEnv* env, jclass aapt_obj, jobject arguments_obj, jobject diagnostics_obj) {
+ std::vector<ScopedUtfChars> compile_args_jni =
+ list_to_utfchars(env, arguments_obj);
+ std::vector<StringPiece> compile_args = extract_pieces(compile_args_jni);
+ JniDiagnostics diagnostics(env, diagnostics_obj);
+ return aapt::CompileCommand(&diagnostics).Execute(compile_args, &std::cerr);
+}
+
+JNIEXPORT jint JNICALL Java_com_android_tools_aapt2_Aapt2Jni_nativeLink(JNIEnv* env,
+ jclass aapt_obj,
+ jobject arguments_obj,
+ jobject diagnostics_obj) {
+ std::vector<ScopedUtfChars> link_args_jni =
+ list_to_utfchars(env, arguments_obj);
+ std::vector<StringPiece> link_args = extract_pieces(link_args_jni);
+ JniDiagnostics diagnostics(env, diagnostics_obj);
+ return aapt::LinkCommand(&diagnostics).Execute(link_args, &std::cerr);
+}
+
+JNIEXPORT void JNICALL Java_com_android_tools_aapt2_Aapt2Jni_ping(
+ JNIEnv *env, jclass aapt_obj) {
+ // This is just a no-op method to see if the library has been loaded.
+}
diff --git a/tools/aapt2/jni/com_android_tools_aapt2_Aapt2Jni.h b/tools/aapt2/jni/com_android_tools_aapt2_Aapt2Jni.h
new file mode 100644
index 0000000..3cd9865
--- /dev/null
+++ b/tools/aapt2/jni/com_android_tools_aapt2_Aapt2Jni.h
@@ -0,0 +1,37 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_android_tools_aapt2_Aapt2Jni */
+
+#ifndef _Included_com_android_tools_aapt2_Aapt2Jni
+#define _Included_com_android_tools_aapt2_Aapt2Jni
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_android_tools_aapt2_Aapt2Jni
+ * Method: ping
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_com_android_tools_aapt2_Aapt2Jni_ping
+ (JNIEnv *, jclass);
+
+/*
+ * Class: com_android_tools_aapt2_Aapt2Jni
+ * Method: nativeCompile
+ * Signature: (Ljava/util/List;Lcom/android/tools/aapt2/Aapt2JniDiagnostics;)I
+ */
+JNIEXPORT jint JNICALL Java_com_android_tools_aapt2_Aapt2Jni_nativeCompile(JNIEnv*, jclass, jobject,
+ jobject);
+
+/*
+ * Class: com_android_tools_aapt2_Aapt2Jni
+ * Method: nativeLink
+ * Signature: (Ljava/util/List;Lcom/android/tools/aapt2/Aapt2JniDiagnostics;)I
+ */
+JNIEXPORT jint JNICALL Java_com_android_tools_aapt2_Aapt2Jni_nativeLink(JNIEnv*, jclass, jobject,
+ jobject);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index 44b4ec1..efbbf8e 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -22,9 +22,9 @@
#include <vector>
#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
#include "androidfw/StringPiece.h"
#include "build/version.h"
-
#include "text/Unicode.h"
#include "text/Utf8Iterator.h"
#include "util/BigBuffer.h"
@@ -231,7 +231,14 @@
static const char* const sMinorVersion = "19";
// The build id of aapt2 binary.
- static const std::string sBuildId = android::build::GetBuildNumber();
+ static std::string sBuildId = android::build::GetBuildNumber();
+
+ if (android::base::StartsWith(sBuildId, "eng.")) {
+ time_t now = time(0);
+ tm* ltm = localtime(&now);
+
+ sBuildId = android::base::StringPrintf("eng.%d%d", 1900 + ltm->tm_year, 1 + ltm->tm_mon);
+ }
return android::base::StringPrintf("%s.%s-%s", sMajorVersion, sMinorVersion, sBuildId.c_str());
}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
index c4967eb..4083bea 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
@@ -76,7 +76,7 @@
private static ScanResult buildScanResult(String ssid, String bssid) {
return new ScanResult(
- WifiSsid.createFromAsciiEncoded(ssid),
+ WifiSsid.fromUtf8Text(ssid),
bssid,
"" /* caps */,
0 /* level */,